# -*- coding: utf-8 -*- import pyvista as pv import numpy as np import threading import time import math class EmbodiedAvatar: def __init__(self): self.gender = "male" self.plt = pv.Plotter(window_size=(420, 720), title="家庭具身智能|真人生命体") self.plt.set_background("#121212") self.breath_count = 0 self.is_breathing = True self.current_action = "idle" self.eyes = {"left": None, "right": None} self.build_male() self.setup_callbacks() self.start_breath_loop() def build_male(self): self.plt.clear() # 头部 self.head = pv.Sphere(radius=0.18, center=(0, 0, 1.5)) # 身体 self.body = pv.Cylinder(radius=0.22, height=0.8, center=(0, 0, 0.8), direction=(0,0,1)) # 手臂 self.arm_l = pv.Cylinder(radius=0.08, height=0.5, direction=(0,0,1)).translate((-0.32,0,1.2)) self.arm_r = pv.Cylinder(radius=0.08, height=0.5, direction=(0,0,1)).translate((0.32,0,1.2)) # 腿部 self.leg_l = pv.Cylinder(radius=0.09, height=0.55, direction=(0,0,1)).translate((-0.14,0,0.25)) self.leg_r = pv.Cylinder(radius=0.09, height=0.55, direction=(0,0,1)).translate((0.14,0,0.25)) # 眼睛 self.eyes["left"] = pv.Sphere(radius=0.03, center=(-0.08, 0.16, 1.58)) self.eyes["right"] = pv.Sphere(radius=0.03, center=(0.08, 0.16, 1.58)) # 瞳孔 self.pupil_l = pv.Sphere(radius=0.015, center=(-0.08, 0.18, 1.6)) self.pupil_r = pv.Sphere(radius=0.015, center=(0.08, 0.18, 1.6)) skin = (1.0, 0.82, 0.73) self.plt.add_mesh(self.head, color=skin, specular=0.3, name="head") self.plt.add_mesh(self.body, color="#2D6CD0", specular=0.2, name="body") self.plt.add_mesh(self.arm_l, color=skin, name="arm_l") self.plt.add_mesh(self.arm_r, color=skin, name="arm_r") self.plt.add_mesh(self.leg_l, color="#1a1a2e", name="leg_l") self.plt.add_mesh(self.leg_r, color="#1a1a2e", name="leg_r") self.plt.add_mesh(self.eyes["left"], color="white", name="eye_l") self.plt.add_mesh(self.eyes["right"], color="white", name="eye_r") self.plt.add_mesh(self.pupil_l, color="#1a1a1a", name="pupil_l") self.plt.add_mesh(self.pupil_r, color="#1a1a1a", name="pupil_r") # 添加光环 self.aura = pv.Sphere(radius=0.35, center=(0, 0, 1.2)) self.plt.add_mesh(self.aura, color="#4a90d9", opacity=0.15, name="aura") self.plt.add_text("👨 男性生命体|自主感知", color="white", font_size=12, name="title") self.plt.add_text("按 H 显示帮助", color="#888888", font_size=10, position="lower_left", name="help_hint") self.gender = "male" def build_female(self): self.plt.clear() self.head = pv.Sphere(radius=0.17, center=(0, 0, 1.5)) self.body = pv.Cylinder(radius=0.18, height=0.82, center=(0, 0, 0.8), direction=(0,0,1)) self.arm_l = pv.Cylinder(radius=0.07, height=0.52, direction=(0,0,1)).translate((-0.28,0,1.2)) self.arm_r = pv.Cylinder(radius=0.07, height=0.52, direction=(0,0,1)).translate((0.28,0,1.2)) self.leg_l = pv.Cylinder(radius=0.08, height=0.58, direction=(0,0,1)).translate((-0.12,0,0.22)) self.leg_r = pv.Cylinder(radius=0.08, height=0.58, direction=(0,0,1)).translate((0.12,0,0.22)) self.eyes["left"] = pv.Sphere(radius=0.028, center=(-0.07, 0.15, 1.57)) self.eyes["right"] = pv.Sphere(radius=0.028, center=(0.07, 0.15, 1.57)) self.pupil_l = pv.Sphere(radius=0.014, center=(-0.07, 0.17, 1.59)) self.pupil_r = pv.Sphere(radius=0.014, center=(0.07, 0.17, 1.59)) skin = (1.0, 0.85, 0.78) self.plt.add_mesh(self.head, color=skin, specular=0.3, name="head") self.plt.add_mesh(self.body, color="#E87EA1", specular=0.2, name="body") self.plt.add_mesh(self.arm_l, color=skin, name="arm_l") self.plt.add_mesh(self.arm_r, color=skin, name="arm_r") self.plt.add_mesh(self.leg_l, color="#2d2d44", name="leg_l") self.plt.add_mesh(self.leg_r, color="#2d2d44", name="leg_r") self.plt.add_mesh(self.eyes["left"], color="white", name="eye_l") self.plt.add_mesh(self.eyes["right"], color="white", name="eye_r") self.plt.add_mesh(self.pupil_l, color="#1a1a1a", name="pupil_l") self.plt.add_mesh(self.pupil_r, color="#1a1a1a", name="pupil_r") self.aura = pv.Sphere(radius=0.32, center=(0, 0, 1.2)) self.plt.add_mesh(self.aura, color="#e87ea1", opacity=0.12, name="aura") self.plt.add_text("👩 女性生命体|自主感知", color="white", font_size=12, name="title") self.plt.add_text("按 H 显示帮助", color="#888888", font_size=10, position="lower_left", name="help_hint") self.gender = "female" def setup_callbacks(self): """设置键盘回调""" self.plt.add_key_event("g", lambda: self.switch_gender()) self.plt.add_key_event("w", lambda: self.welcome_action()) self.plt.add_key_event("s", lambda: self.serious_action()) self.plt.add_key_event("c", lambda: self.comfort_action()) self.plt.add_key_event("l", lambda: self.listen_action()) self.plt.add_key_event("r", lambda: self.reset_pose()) self.plt.add_key_event("b", lambda: self.toggle_breath()) self.plt.add_key_event("h", lambda: self.show_help()) self.plt.add_key_event("q", lambda: self.plt.close()) self.plt.add_key_event("Left", lambda: self.move_eyes("left")) self.plt.add_key_event("Right", lambda: self.move_eyes("right")) self.plt.add_key_event("Up", lambda: self.move_eyes("up")) self.plt.add_key_event("Down", lambda: self.move_eyes("down")) def start_breath_loop(self): """启动呼吸循环线程""" def breath_loop(): time.sleep(0.5) # 等待窗口初始化 while True: if self.is_breathing: try: self.idle_breath() except: pass time.sleep(0.15) self.breath_thread = threading.Thread(target=breath_loop, daemon=True) self.breath_thread.start() def idle_breath(self): """永久呼吸待机""" self.breath_count += 1 breath_factor = 1.0 + 0.015 * math.sin(self.breath_count * 0.3) aura_factor = 1.0 + 0.04 * math.sin(self.breath_count * 0.3) try: if hasattr(self, 'body') and "body" in self.plt.actors: self.plt.actors["body"].SetScale(1.0, 1.0, breath_factor) if hasattr(self, 'aura') and "aura" in self.plt.actors: self.plt.actors["aura"].SetScale(aura_factor, aura_factor, aura_factor) alpha = 0.12 + 0.05 * math.sin(self.breath_count * 0.3) self.plt.actors["aura"].GetProperty().SetOpacity(alpha) except: pass def move_eyes(self, direction): """眼球运动""" offsets = { "left": (-0.015, 0, 0), "right": (0.015, 0, 0), "up": (0, 0, 0.012), "down": (0, 0, -0.012) } if direction in offsets and hasattr(self, 'pupil_l'): dx, dy, dz = offsets[direction] self.pupil_l.translate((dx, dy, dz)) self.pupil_r.translate((dx, dy, dz)) print(f"👀 视线移动: {direction}") def welcome_action(self): if hasattr(self, 'arm_r'): self.arm_r.rotate_z(-25) self.head.rotate_x(5) print("👋 迎宾中 | 开心体态") def serious_action(self): if hasattr(self, 'arm_l'): self.arm_l.rotate_z(15) self.arm_r.rotate_z(-15) self.head.rotate_x(-5) print("📚 严肃中 | 教育体态") def comfort_action(self): if hasattr(self, 'arm_l'): self.arm_l.rotate_z(20) self.arm_r.rotate_z(-20) print("🤝 安抚中 | 劝和体态") def listen_action(self): if hasattr(self, 'head'): self.head.rotate_z(5) self.body.rotate_x(3) print("👂 倾听中 | 专注体态") def reset_pose(self): if self.gender == "male": self.build_male() else: self.build_female() print("✨ 姿态已重置 | 待机状态") def toggle_breath(self): self.is_breathing = not self.is_breathing status = "开启" if self.is_breathing else "关闭" print(f"🌬️ 呼吸感知: {status}") def show_help(self): help_text = """ ═══════════════════════════════════════ 🎮 具身智能控制面板 ═══════════════════════════════════════ G - 切换性别 (男/女) W - 迎宾开心体态 S - 严肃教育体态 C - 安抚劝和体态 L - 倾听体态 R - 重置姿态 B - 开关呼吸动画 ←↑→↓ - 控制眼球方向 H - 显示此帮助 Q - 退出程序 ═══════════════════════════════════════ """ print(help_text) def switch_gender(self): if self.gender == "male": self.build_female() print("切换为女性具身生命体") else: self.build_male() print("切换为男性具身生命体") def run(self): print("\n" + "="*50) print("🤖 家庭具身智能系统 v2.0") print(" 真人生命体已激活") print("="*50) self.show_help() self.plt.show(interactive=True, auto_close=False) if __name__ == "__main__": ava = EmbodiedAvatar() ava.run()