File size: 42,374 Bytes
9da872f 5322fcd 9da872f a26fdbd 9da872f a26fdbd 9da872f 3f1b696 9da872f a26fdbd 9da872f a26fdbd 9da872f a26fdbd 6563dc6 f3abae3 9da872f f3abae3 9da872f f526ff1 9da872f 770da68 9da872f 4467722 9da872f f526ff1 9da872f f526ff1 9da872f 4467722 4e5c3ed 4467722 770da68 4467722 5322fcd 4467722 4e5c3ed 5322fcd 4467722 4e5c3ed 5322fcd 4e5c3ed 5322fcd 4e5c3ed 5322fcd 4e5c3ed 5322fcd 4467722 5322fcd 4467722 5322fcd 4467722 770da68 4467722 a26fdbd 4467722 a26fdbd 4467722 a26fdbd 4e5c3ed a26fdbd 4467722 770da68 4467722 6563dc6 f3abae3 6563dc6 f3abae3 4467722 6563dc6 4467722 4e5c3ed 6563dc6 4e5c3ed 6563dc6 4467722 770da68 4467722 6563dc6 4467722 770da68 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 770da68 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 3f1b696 4467722 4e5c3ed 4467722 4e5c3ed 4467722 3f1b696 4e5c3ed 4467722 4e5c3ed 4467722 770da68 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed 4467722 770da68 4467722 4e5c3ed 4467722 4e5c3ed 770da68 4467722 4e5c3ed 770da68 4467722 4e5c3ed 770da68 4467722 4e5c3ed 4467722 4e5c3ed 6563dc6 f3abae3 6563dc6 4e5c3ed 4467722 4e5c3ed 4467722 4e5c3ed f526ff1 4e5c3ed f3abae3 6563dc6 4e5c3ed 6563dc6 4467722 f3abae3 b9e2e76 9da872f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 | # Reachy Mini Home Assistant Voice Assistant - 项目计划
## 项目概述
将 Home Assistant 语音助手功能集成到 Reachy Mini 机器人,通过 ESPHome 协议与 Home Assistant 通信。
## 本地项目目录参考 (禁止修改参考目录内任何文件)
1. [linux-voice-assistant](linux-voice-assistant),这是一个基于 Linux 的Home Assistant的语音助手应用,用于参考。
2. [Reachy Mini SDK](reachy_mini) 这是 Reachy Mini SDK 的本地项目目录,用于参考。
3. [reachy_mini_conversation_app](reachy_mini_conversation_app) - Reachy Mini 对话应用,用于参考
4. [reachy-mini-desktop-app](reachy-mini-desktop-app) - Reachy Mini 桌面应用,用于参考
## 核心设计原则
1. **零配置安装** - 用户只需安装应用,无需手动配置
2. **使用 Reachy Mini 原生硬件** - 使用机器人自带的麦克风和扬声器
3. **Home Assistant 集中管理** - 所有配置在 Home Assistant 端完成
4. **运动反馈** - 语音交互时提供头部运动和天线动画反馈
5. **项目约束** - 整个项目需要严格遵循 [Reachy Mini SDK](reachy_mini) 的架构设计与约束
6. **代码质量** - 严格遵循Python开发的标准,并做到代码风格一致,代码结构清晰,注释完整,文档完善,测试覆盖率高,代码质量高,代码可读性高,代码可维护性高,代码可扩展性高,代码可复用性高
7. **功能优先级** - 与home assistant的语音对话为最高优先级,任何其它的功能都是辅助功能,不能影响语音对话的功能和响应速度
8. **不调用任何LED功能** - LED都被隐藏在了机器人内部,所有的LED控制全部都忽略,不要使用LED控制
9. **保留功能优先** - 任何代码修改都应该在保留已完成功能的前提下优化,不能以去除功能的方式来解决问题。当有问题发生时,应该充分参考案例之后以解决问题为优先级,而不是添加各种日志输出为优先级
## 技术架构
```
┌─────────────────────────────────────────────────────────────┐
│ Reachy Mini │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Microphone │→ │ Wake Word │→ │ ESPHome Protocol │ │
│ │ (ReSpeaker) │ │ Detection │ │ Server (Port 6053) │ │
│ └─────────────┘ └─────────────┘ └──────────┬──────────┘ │
│ │ │
│ ┌─────────────┐ ┌─────────────┐ │ │
│ │ Speaker │← │ Audio │←────────────┘ │
│ │ (ReSpeaker) │ │ Player │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Camera + Face Tracking (YOLO) │ │
│ │ - 15Hz 人脸检测与追踪 │ │
│ │ - look_at_image() 计算目标姿态 │ │
│ │ - 人脸丢失后平滑回中性位置 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Motion Controller (Head + Antennas) - 5Hz │ │
│ │ - Face tracking offsets (secondary pose) │ │
│ │ - Speech sway (语音驱动微动) │ │
│ │ - Breathing animation (空闲呼吸) │ │
│ │ - on_wakeup → on_listening → on_speaking → on_idle │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ ESPHome Protocol
▼
┌─────────────────────────────────────────────────────────────┐
│ Home Assistant │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ STT Engine │ │ Intent │ │ TTS Engine │ │
│ │ │ │ Processing │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## 已完成功能
### 核心功能
- [x] ESPHome 协议服务器实现
- [x] mDNS 服务发现(自动被 Home Assistant 发现)
- [x] 本地唤醒词检测(microWakeWord)
- [x] 拍一拍唤醒(IMU 加速度检测,仅无线版本)
- [x] 音频流传输到 Home Assistant
- [x] TTS 音频播放
- [x] 停止词检测
### Reachy Mini 集成
- [x] 使用 Reachy Mini SDK 的麦克风输入
- [x] 使用 Reachy Mini SDK 的扬声器输出
- [x] 头部运动控制(点头、摇头、注视)
- [x] 天线动画控制
- [x] 语音状态反馈动作
- [x] YOLO 人脸追踪(替代 DOA 声源定位)
- [x] 5Hz 统一运动控制循环
### 应用架构
- [x] 符合 Reachy Mini App 架构
- [x] 自动下载唤醒词模型
- [x] 自动下载音效文件
- [x] 无需 .env 配置文件
## 文件清单
```
reachy_mini_ha_voice/
├── reachy_mini_ha_voice/
│ ├── __init__.py # 包初始化
│ ├── __main__.py py # 命令行入口
│ ├── main.py # ReachyMiniApp 入口
│ ├── voice_assistant.py # 语音助手服务
│ ├── satellite.py # ESPHome 协议处理
│ ├── audio_player.py # 音频播放器
│ ├── camera_server.py # MJPEG 摄像头流服务器 + 人脸追踪
│ ├── head_tracker.py # YOLO 人脸检测器
│ ├── motion.py # 运动控制 (高层 API)
│ ├── movement_manager.py # 统一运动管理器 (20Hz 控制循环,优化以防止 daemon 崩溃)
│ ├── models.py # 数据模型
│ ├── entity.py # ESPHome 基础实体
│ ├── entity_extensions.py # 扩展实体类型
│ ├── reachy_controller.py # Reachy Mini 控制器包装
│ ├── api_server.py # API 服务器
│ ├── zeroconf.py # mDNS 发现
│ └── util.py # 工具函数
├── wakewords/ # 唤醒词模型(自动下载)
│ ├── okay_nabu.json
│ ├── okay_nabu.tflite
│ ├── hey_jarvis.json
│ ├── hey_jarvis.tflite
│ ├── stop.json
│ └── stop.tflite
├── sounds/ # 音效文件(自动下载)
│ ├── wake_word_triggered.flac
│ └── timer_finished.flac
├── pyproject.toml # 项目配置
├── README.md # 说明文档
└── PROJECT_PLAN.md # 项目计划
```
## 依赖项
```toml
dependencies = [
"reachy-mini", # Reachy Mini SDK
"sounddevice>=0.4.6", # 音频处理(备用)
"soundfile>=0.12.0", # 音频文件读取
"numpy>=1.24.0", # 数值计算
"pymicro-wakeword>=2.0.0,<3.0.0", # 唤醒词检测
"pyopen-wakeword>=1.0.0,<2.0.0", # 备用唤醒词
"aioesphomeapi>=42.0.0", # ESPHome 协议
"zeroconf>=0.100.0", # mDNS 发现
"scipy>=1.10.0", # 运动控制
"pydantic>=2.0.0", # 数据验证
]
```
## 使用流程
1. **安装应用**
- 从 Reachy Mini App Store 安装`reachy-mini-ha-voice`
2. **启动应用**
- 应用自动启动 ESPHome 服务器(端口 6053)
- 自动下载所需模型和音效
3. **连接 Home Assistant**
- Home Assistant 自动发现设备(mDNS)
- 或手动添加:设置 → 设备与服务 → 添加集成 → ESPHome
4. **使用语音助手**
- 说 "Okay Nabu" 唤醒
- 说出命令
- Reachy Mini 会做出运动反馈
## ESPHome 实体规划
基于 Reachy Mini SDK 深入分析,以下实体已暴露给 Home Assistant:
### 已实现实体
| 实体类型 | 名称 | 说明 |
|---------|------|------|
| Media Player | `media_player` | 音频播放控制 |
| Voice Assistant | `voice_assistant` | 语音助手管道 |
### 已实现的控制实体 (Controls) - 可读写
#### Phase 1-3: 基础控制与姿态
| ESPHome 实体类型 | 名称 | SDK API | 范围/选项 | 说明 |
|-----------------|------|---------|----------|------|
| `Number` | `speaker_volume` | `AudioPlayer.set_volume()` | 0-100 | 扬声器音量 |
| `Select` | `motor_mode` | `set_motor_control_mode()` | enabled/disabled/gravity_compensation | 电机模式选择 |
| `Switch` | `motors_enabled` | `enable_motors()` / `disable_motors()` | on/off | 电机扭矩开关 |
| `Button` | `wake_up` | `mini.wake_up()` | - | 唤醒机器人动作 |
| `Button` | `go_to_sleep` | `mini.goto_sleep()` | - | 睡眠机器人动作 |
| `Number` | `head_x` | `goto_target(head=...)` | ±50mm | 头部 X 位置控制 |
| `Number` | `head_y` | `goto_target(head=...)` | ±50mm | 头部 Y 位置控制 |
| `Number` | `head_z` | `goto_target(head=...)` | ±50mm | 头部 Z 位置控制 |
| `Number` | `head_roll` | `goto_target(head=...)` | -40° ~ +40° | 头部翻滚角控制 |
| `Number` | `head_pitch` | `goto_target(head=...)` | -40° ~ +40° | 头部俯仰角控制 |
| `Number` | `head_yaw` | `goto_target(head=...)` | -180° ~ +180° | 头部偏航角控制 |
| `Number` | `body_yaw` | `goto_target(body_yaw=...)` | -160° ~ +160° | 身体偏航角控制 |
| `Number` | `antenna_left` | `goto_target(antennas=...)` | -90° ~ +90° | 左天线角度控制 |
| `Number` | `antenna_right` | `goto_target(antennas=...)` | -90° ~ +90° | 右天线角度控制 |
#### Phase 4: 注视控制
| ESPHome 实体类型 | 名称 | SDK API | 范围/选项 | 说明 |
|-----------------|------|---------|----------|------|
| `Number` | `look_at_x` | `look_at_world(x, y, z)` | 世界坐标 | 注视点 X 坐标 |
| `Number` | `look_at_y` | `look_at_world(x, y, z)` | 世界坐标 | 注视点 Y 坐标 |
| `Number` | `look_at_z` | `look_at_world(x, y, z)` | 世界坐标 | 注视点 Z 坐标 |
### 已实现的传感器实体 (Sensors) - 只读
#### Phase 1 & 5: 基础状态与音频传感器
| ESPHome 实体类型 | 名称 | SDK API | 说明 |
|-----------------|------|---------|------|
| `Text Sensor` | `daemon_state` | `DaemonStatus.state` | Daemon 状态 |
| `Binary Sensor` | `backend_ready` | `backend_status.ready` | 后端是否就绪 |
| `Text Sensor` | `error_message` | `DaemonStatus.error` | 当前错误信息 |
| `Sensor` | `doa_angle` | `DoAInfo.angle` | 声源方向角度 (°) |
| `Binary Sensor` | `speech_detected` | `DoAInfo.speech_detected` | 是否检测到语音 |
#### Phase 6: 诊断信息
| ESPHome 实体类型 | 名称 | SDK API | 说明 |
|-----------------|------|---------|------|
| `Sensor` | `control_loop_frequency` | `control_loop_stats` | 控制循环频率 (Hz) |
| `Text Sensor` | `sdk_version` | `DaemonStatus.version` | SDK 版本号 |
| `Text Sensor` | `robot_name` | `DaemonStatus.robot_name` | 机器人名称 |
| `Binary Sensor` | `wireless_version` | `DaemonStatus.wireless_version` | 是否为无线版本 |
| `Binary Sensor` | `simulation_mode` | `DaemonStatus.simulation_enabled` | 是否在仿真模式 |
| `Text Sensor` | `wlan_ip` | `DaemonStatus.wlan_ip` | 无线网络 IP |
#### Phase 7: IMU 传感器 (仅无线版本)
| ESPHome 实体类型 | 名称 | SDK API | 说明 |
|-----------------|------|---------|------|
| `Sensor` | `imu_accel_x` | `mini.imu["accelerometer"][0]` | X 轴加速度 (m/s²) |
| `Sensor` | `imu_accel_y` | `mini.imu["accelerometer"][1]` | Y 轴加速度 (m/s²) |
| `Sensor` | `imu_accel_z` | `mini.imu["accelerometer"][2]` | Z 轴加速度 (m/s²) |
| `Sensor` | `imu_gyro_x` | `mini.imu["gyroscope"][0]` | X 轴角速度 (rad/s) |
| `Sensor` | `imu_gyro_y` | `mini.imu["gyroscope"][1]` | Y 轴角速度 (rad/s) |
| `Sensor` | `imu_gyro_z` | `mini.imu["gyroscope"][2]` | Z 轴角速度 (rad/s) |
| `Sensor` | `imu_temperature` | `mini.imu["temperature"]` | IMU 温度 (°C) |
#### Phase 8-12: 扩展功能
| ESPHome 实体类型 | 名称 | 说明 |
|-----------------|------|------|
| `Select` | `emotion` | 表情选择器 (Happy/Sad/Angry/Fear/Surprise/Disgust) |
| `Number` | `microphone_volume` | 麦克风音量 (0-100%) |
| `Camera` | `camera` | ESPHome Camera 实体(实时预览) |
| `Number` | `led_brightness` | LED 亮度 (0-100%) |
| `Select` | `led_effect` | LED 效果 (off/solid/breathing/rainbow/doa) |
| `Number` | `led_color_r` | LED 红色分量 (0-255) |
| `Number` | `led_color_g` | LED 绿色分量 (0-255) |
| `Number` | `led_color_b` | LED 蓝色分量 (0-255) |
| `Switch` | `agc_enabled` | 自动增益控制开关 |
| `Number` | `agc_max_gain` | AGC 最大增益 (0-30 dB) |
| `Number` | `noise_suppression` | 噪声抑制级别 (0-100%) |
| `Binary Sensor` | `echo_cancellation_converged` | 回声消除收敛状态 |
> **注意**: 头部位置 (x/y/z) 和角度 (roll/pitch/yaw)、身体偏航角、天线角度都是**可控制**的实体,
> 使用 `Number` 类型实现双向控制。设置新值时调用 `goto_target()`,读取当前值时调用 `get_current_head_pose()` 等。
### 实现优先级
1. **Phase 1 - 基础状态与音量** (高优先级) ✅ **已完成**
- [x] `daemon_state` - Daemon 状态传感器
- [x] `backend_ready` - 后端就绪状态
- [x] `error_message` - 错误信息
- [x] `speaker_volume` - 扬声器音量控制
2. **Phase 2 - 电机控制** (高优先级) ✅ **已完成**
- [x] `motors_enabled` - 电机开关
- [x] `motor_mode` - 电机模式选择 (enabled/disabled/gravity_compensation)
- [x] `wake_up` / `go_to_sleep` - 唤醒/睡眠按钮
3. **Phase 3 - 姿态控制** (中优先级) ✅ **已完成**
- [x] `head_x/y/z` - 头部位置控制
- [x] `head_roll/pitch/yaw` - 头部角度控制
- [x] `body_yaw` - 身体偏航角控制
- [x] `antenna_left/right` - 天线角度控制
4. **Phase 4 - 注视控制** (中优先级) ✅ **已完成**
- [x] `look_at_x/y/z` - 注视点坐标控制
5. **Phase 5 - 音频传感器** (低优先级) ✅ **已完成**
- [x] `doa_angle` - 声源方向
- [x] `speech_detected` - 语音检测
6. **Phase 6 - 诊断信息** (低优先级) ✅ **已完成**
- [x] `control_loop_frequency` - 控制循环频率
- [x] `sdk_version` - SDK 版本
- [x] `robot_name` - 机器人名称
- [x] `wireless_version` - 无线版本标识
- [x] `simulation_mode` - 仿真模式标识
- [x] `wlan_ip` - 无线 IP 地址
7. **Phase 7 - IMU 传感器** (可选,仅无线版本) ✅ **已完成**
- [x] `imu_accel_x/y/z` - 加速度计
- [x] `imu_gyro_x/y/z` - 陀螺仪
- [x] `imu_temperature` - IMU 温度
8. **Phase 8 - 表情控制** ✅ **已完成**
- [x] `emotion` - 表情选择器 (Happy/Sad/Angry/Fear/Surprise/Disgust)
9. **Phase 9 - 音频控制** ✅ **已完成**
- [x] `microphone_volume` - 麦克风音量控制 (0-100%)
10. **Phase 10 - 摄像头集成** ✅ **已完成**
- [x] `camera` - ESPHome Camera 实体(实时预览)
11. **Phase 11 - LED 控制** ❌ **已禁用(LED 隐藏在机器人内部)**
- [ ] `led_brightness` - LED 亮度 (0-100%) - 已注释
- [ ] `led_effect` - LED 效果 (off/solid/breathing/rainbow/doa) - 已注释
- [ ] `led_color_r/g/b` - LED RGB 颜色 (0-255) - 已注释
12. **Phase 12 - 音频处理参数** ✅ **已完成**
- [x] `agc_enabled` - 自动增益控制开关
- [x] `agc_max_gain` - AGC 最大增益 (0-30 dB)
- [x] `noise_suppression` - 噪声抑制级别 (0-100%)
- [x] `echo_cancellation_converged` - 回声消除收敛状态(只读)
13. **Phase 13 - Sendspin 音频播放支持** ❌ **待实现**
- [ ] 支持 sendspin 协议播放音频
- [ ] 与现有 AudioPlayer 集成
- [ ] TTS 音频通过 sendspin 输出
---
## 🎉 Phase 1-12 实体已完成!
**已完成总计:40 个实体**
- Phase 1: 4 个实体 (基础状态与音量)
- Phase 2: 4 个实体 (电机控制)
- Phase 3: 9 个实体 (姿态控制)
- Phase 4: 3 个实体 (注视控制)
- Phase 5: 2 个实体 (音频传感器)
- Phase 6: 6 个实体 (诊断信息)
- Phase 7: 7 个实体 (IMU 传感器)
- Phase 8: 1 个实体 (表情控制)
- Phase 9: 1 个实体 (麦克风音量)
- Phase 10: 1 个实体 (摄像头)
- Phase 11: 0 个实体 (LED 控制 - 已禁用)
- Phase 12: 4 个实体 (音频处理参数)
---
## 🚀 语音助手增强功能实现状态
### Phase 14 - 情感动作反馈系统 (部分实现) 🟡
**实现状态**: 基础架构已就绪,支持手动触发,对话时使用语音驱动的自然微动
**已实现功能**:
- ✅ Phase 8 Emotion Selector 实体 (`emotion`)
- ✅ 基础情感动作播放API (`_play_emotion`)
- ✅ 情感映射: Happy/Sad/Angry/Fear/Surprise/Disgust
- ✅ 与 HuggingFace 动作库集成 (`pollen-robotics/reachy-mini-emotions-library`)
- ✅ 对话时使用 SpeechSway 系统提供自然的头部微动 (不阻塞对话体验)
**设计决策**:
- 🎯 对话时不自动播放完整情感动作,避免阻塞对话体验
- 🎯 使用语音驱动的头部摆动 (SpeechSway) 提供自然的动作反馈
- 🎯 情感动作保留为手动触发功能,可通过 ESPHome 实体控制
**未实现功能**:
- ❌ 自动根据语音助手响应触发情感动作 (已决定不实现,避免阻塞)
- ❌ 意图识别与情感匹配
- ❌ 舞蹈动作库集成
- ❌ 上下文感知(如天气查询-晴天播放 happy,雨天播放 sad)
**代码位置**:
- `entity_registry.py:633-658` - Emotion Selector 实体
- `satellite.py:544-574` - `_play_emotion()` 方法
- `motion.py:132-156` - 对话开始时的动作控制 (使用 SpeechSway)
- `movement_manager.py:541-595` - Move 队列管理 (允许 SpeechSway 叠加)
**实际行为**:
| 语音助手事件 | 实际动作 | 实现状态 |
|-------------|---------|---------|
| 唤醒词检测 | 转向声源 + 点头确认 | ✅ 已实现 |
| 对话开始 | 语音驱动的头部微动 (SpeechSway) | ✅ 已实现 |
| 对话进行中 | 持续的语音驱动微动 + 呼吸动画 | ✅ 已实现 |
| 对话结束 | 返回中立位置 + 呼吸动画 | ✅ 已实现 |
| 手动触发情感 | 通过 ESPHome `emotion` 实体播放 | ✅ 已实现 |
**技术说明**:
```python
# motion.py - 对话时使用 SpeechSway 而非完整情感动作
def on_speaking_start(self):
self._is_speaking = True
self._movement_manager.set_state(RobotState.SPEAKING)
# SpeechSway 会自动根据音频响度产生自然的头部微动
# 不播放完整情感动作,避免阻塞对话体验
# movement_manager.py - 动作分层系统
# 1. Move 队列 (情感动作) - 设置基础姿态
# 2. Action (点头/摇头等) - 叠加在基础姿态上
# 3. SpeechSway - 语音驱动微动,可与 Move 共存
# 4. Breathing - 空闲时的呼吸动画
```
**原始规划** (已决定不实现,避免阻塞对话):
| 语音助手事件 | 原计划动作 | 不实现原因 |
|-------------|---------|---------|
| 收到肯定回复 | 播放 "happy" 动作 | 完整动作会阻塞对话流畅性 |
| 收到否定回复 | 播放 "sad" 动作 | 完整动作会阻塞对话流畅性 |
| 播放音乐/娱乐 | 播放 "dance" 动作 | 完整动作会阻塞对话流畅性 |
| 定时器完成 | 播放 "alert" 动作 | 完整动作会阻塞对话流畅性 |
| 错误/无法理解 | 播放 "confused" 动作 | 完整动作会阻塞对话流畅性 |
**手动触发情感动作示例**:
```yaml
# Home Assistant 自动化示例 - 手动触发情感
automation:
- alias: "Reachy 早安问候"
trigger:
- platform: time
at: "07:00:00"
action:
- service: select.select_option
target:
entity_id: select.reachy_mini_emotion
data:
option: "Happy"
```
### Phase 15 - 人脸追踪(替代 DOA 声源追踪)✅ **已完成**
**目标**: 实现自然的人脸追踪,让机器人在对话时注视说话人。
**设计决策**:
- ❌ 原计划使用 DOA (Direction of Arrival) 声源追踪
- ✅ 改用 YOLO 人脸检测,更稳定、更准确
- 原因:DOA 在唤醒时判断不够准确,且频繁查询会导致 daemon 崩溃
**已实现功能**:
| 功能 | 说明 | 实现位置 | 实现状态 |
|------|------|---------|---------|
| YOLO 人脸检测 | 使用 `AdamCodd/YOLOv11n-face-detection` 模型 | `head_tracker.py` | ✅ 已实现 |
| 15Hz 人脸追踪 | 摄像头帧处理 + 人脸检测 | `camera_server.py` | ✅ 已实现 |
| look_at_image() | 根据人脸位置计算目标姿态 | `camera_server.py` | ✅ 已实现 |
| 平滑回中性位置 | 人脸丢失后 1 秒内平滑回归 | `camera_server.py` | ✅ 已实现 |
| face_tracking_offsets | 作为 secondary pose 叠加到运动控制 | `movement_manager.py` | ✅ 已实现 |
| 语音活动检测 | DOA 实体仍可用于检测语音 | `DoAInfo.speech_detected` | ✅ 已暴露为实体 |
**代码位置**:
- `head_tracker.py` - YOLO 人脸检测器 (`HeadTracker` 类)
- `camera_server.py:_face_tracking_loop()` - 15Hz 人脸追踪循环
- `movement_manager.py:set_face_tracking_offsets()` - 人脸追踪偏移量 API
**技术细节**:
```python
# head_tracker.py - YOLO 人脸检测
class HeadTracker:
def __init__(self):
self.model = YOLO("AdamCodd/YOLOv11n-face-detection")
def detect_faces(self, frame) -> list[FaceDetection]:
# 返回检测到的人脸列表,包含 bbox 和置信度
# camera_server.py - 人脸追踪循环
async def _face_tracking_loop(self):
while self._running:
frame = self._reachy_controller.get_camera_frame()
faces = self._head_tracker.detect_faces(frame)
if faces:
# 选择最大/最近的人脸
target_u, target_v = faces[0].center
pose = self._reachy_controller.look_at_image(target_u, target_v)
self._motion.set_face_tracking_offsets(pose)
else:
# 平滑回归中性位置
self._motion.clear_face_tracking_offsets()
await asyncio.sleep(1/15) # 15Hz
```
### Phase 16 - 卡通风格运动模式 (部分实现) 🟡
**目标**: 使用 SDK 的插值技术让机器人动作更有个性和表现力。
**SDK 支持**: `InterpolationTechnique` 枚举
- `LINEAR` - 线性,机械感
- `MIN_JERK` - 最小加加速度,自然平滑(默认)
- `EASE_IN_OUT` - 缓入缓出,优雅
- `CARTOON` - 卡通风格,带回弹效果,活泼可爱
**已实现功能**:
- ✅ 20Hz 统一控制循环 (`movement_manager.py`) - 从 100Hz 降低以防止 daemon 崩溃
- ✅ 姿态变化检测 - 仅在姿态显著变化时发送命令 (阈值 0.001)
- ✅ 状态查询缓存 - 100ms TTL,减少 daemon 负载
- ✅ 平滑插值动作 (ease in-out 曲线)
- ✅ 呼吸动画 - 空闲时 Z 轴微动 + 天线摆动 (`BreathingAnimation`)
- ✅ 命令队列模式 - 线程安全的外部 API
- ✅ 错误节流 - 防止日志爆炸
- ✅ 连接健康监控 - 自动检测和恢复连接丢失
**未实现功能**:
- ❌ 动态插值技术切换 (CARTOON/EASE_IN_OUT 等)
- ❌ 夸张的卡通回弹效果
**代码位置**:
- `movement_manager.py:192-243` - BreathingAnimation 类
- `movement_manager.py:246-697` - MovementManager 类
**场景实现状态**:
| 场景 | 推荐插值 | 效果 | 实现状态 |
|------|---------|------|---------|
| 唤醒点头 | `CARTOON` | 活泼的回弹效果 | ❌ 未实现 |
| 思考抬头 | `EASE_IN_OUT` | 优雅的过渡 | ✅ 已实现 (平滑插值) |
| 说话时微动 | `MIN_JERK` | 自然流畅 | ✅ 已实现 (SpeechSway) |
| 错误摇头 | `CARTOON` | 夸张的否定 | ❌ 未实现 |
| 返回中立 | `MIN_JERK` | 平滑归位 | ✅ 已实现 |
| 空闲呼吸 | - | 微妙的生命感 | ✅ 已实现 (BreathingAnimation) |
### Phase 17 - 说话时天线同步动画 (部分实现) 🟡
**目标**: TTS 播放时,天线随音频节奏摆动,模拟"说话"效果。
**已实现功能**:
- ✅ 语音驱动头部摆动 (`SpeechSwayGenerator`)
- ✅ 基于音频响度的 VAD 检测
- ✅ 多频率正弦波叠加 (Lissajous 运动)
- ✅ 平滑包络过渡
**代码位置**:
- `movement_manager.py:124-189` - SpeechSwayGenerator 类
- `motion.py:212-222` - update_audio_loudness() 方法
**技术细节**:
```python
# 语音摆动参数
SWAY_A_PITCH_DEG = 3.0 # 俯仰幅度 (度)
SWAY_A_YAW_DEG = 2.0 # 偏航幅度
SWAY_A_ROLL_DEG = 2.0 # 翻滚幅度
SWAY_F_PITCH = 0.8 # 俯仰频率 Hz
SWAY_F_YAW = 0.6 # 偏航频率
SWAY_F_ROLL = 0.5 # 翻滚频率
# VAD 阈值
VAD_DB_ON = -35 # 开始检测阈值
VAD_DB_OFF = -45 # 停止检测阈值
```
**未实现功能**:
- ❌ 天线随音频节奏摆动 (当前仅头部摆动)
- ❌ 音频频谱分析驱动动画
### Phase 18 - 视觉注视交互 (未实现) ❌
**目标**: 利用摄像头检测人脸,实现眼神交流。
**SDK 支持**:
- `look_at_image(u, v)` - 注视图像中的点
- `look_at_world(x, y, z)` - 注视世界坐标点
- `media.get_frame()` - 获取摄像头画面 (✅ 已在 `camera_server.py:146` 实现)
**未实现功能**:
| 功能 | 说明 | 实现状态 |
|------|------|---------|
| 人脸检测 | 使用 OpenCV/MediaPipe 检测人脸 | ❌ 未实现 |
| 眼神追踪 | 对话时注视说话人的脸 | ❌ 未实现 |
| 多人切换 | 检测到多人时,注视当前说话人 | ❌ 未实现 |
| 空闲扫视 | 空闲时随机环顾四周 | ❌ 未实现 |
### Phase 19 - 重力补偿互动模式 (部分实现) 🟡
**目标**: 允许用户物理触摸和引导机器人头部,实现"教学"式交互。
**SDK 支持**: `enable_gravity_compensation()` - 电机进入重力补偿模式,可手动移动
**已实现功能**:
- ✅ 重力补偿模式切换 (`motor_mode` Select 实体,选项 "gravity_compensation")
- ✅ `reachy_controller.py:236-237` - 重力补偿 API 调用
**未实现功能**:
- ❌ 教学模式 - 录制动作轨迹
- ❌ 保存/播放自定义动作
- ❌ 语音命令触发教学流程
**应用场景**:
- ❌ 用户说 "让我教你一个动作" → 进入重力补偿模式
- ❌ 用户手动移动头部 → 录制动作轨迹
- ❌ 用户说 "记住这个" → 保存动作
- ❌ 用户说 "做刚才的动作" → 播放录制的动作
### Phase 20 - 环境感知响应 (部分实现) 🟡
**目标**: 利用 IMU 传感器感知环境变化并做出响应。
**SDK 支持**:
- ✅ `mini.imu["accelerometer"]` - 加速度计 (Phase 7 已实现为实体)
- ✅ `mini.imu["gyroscope"]` - 陀螺仪 (Phase 7 已实现为实体)
**已实现功能**:
| 检测事件 | 响应动作 | 实现状态 |
|---------|---------|---------|
| 拍一拍唤醒 | 进入持续对话模式 | ✅ 已实现 |
| 再次拍一拍 | 退出持续对话模式 | ✅ 已实现 |
**拍一拍唤醒 vs 语音唤醒**:
| 唤醒方式 | 对话模式 | 说明 |
|---------|---------|------|
| 语音唤醒 (Okay Nabu) | 单次对话 | 每次对话需要重新说唤醒词 |
| 拍一拍唤醒 | 持续对话 | TTS 结束后自动继续监听,再拍一次退出 |
**技术实现**:
- `tap_detector.py` - IMU 加速度突变检测
- `satellite.py:_tap_conversation_mode` - 持续对话模式标志
- 阈值: 2.0g (可配置)
- 冷却时间: 1.0s (防止重复触发)
- 仅限无线版本 (Wireless) 可用
```python
# satellite.py - 持续对话模式
def wakeup_from_tap(self):
if self._tap_conversation_mode:
# 第二次拍 - 退出持续对话
self._tap_conversation_mode = False
self._reachy_on_idle()
else:
# 第一次拍 - 进入持续对话
self._tap_conversation_mode = True
self.send_messages([VoiceAssistantRequest(start=True)])
def _tts_finished(self):
if self._tap_conversation_mode:
# 持续对话模式:自动继续监听
self.send_messages([VoiceAssistantRequest(start=True)])
```
**未实现功能**:
| 检测事件 | 响应动作 | 实现状态 |
|---------|---------|---------|
| 被摇晃 | 播放晕眩动作 + 语音 "别晃我~" | ❌ 未实现 |
| 倾斜/倒下 | 播放求助动作 + 语音 "我倒了,帮帮我" | ❌ 未实现 |
| 长时间静止 | 进入休眠动画 | ❌ 未实现 |
### Phase 21 - Home Assistant 场景联动 (未实现) ❌
**目标**: 根据 Home Assistant 的场景/自动化触发机器人动作。
**实现方案**: 通过 ESPHome 服务调用
**未实现场景**:
| HA 场景 | 机器人响应 | 实现状态 |
|--------|-----------|---------|
| 早安场景 | 播放唤醒动作 + "早上好!" | ❌ 未实现 |
| 晚安场景 | 播放睡眠动作 + "晚安~" | ❌ 未实现 |
| 有人回家 | 转向门口 + 挥手 + "欢迎回家!" | ❌ 未实现 |
| 门铃响起 | 转向门口 + 警觉动作 | ❌ 未实现 |
| 播放音乐 | 随音乐节奏摆动 | ❌ 未实现 |
---
## 📊 功能实现总结
### ✅ 已完成功能
#### 核心语音助手 (Phase 1-12)
- **45+ ESPHome 实体** - 全部实现
- **基础语音交互** - 唤醒词检测、STT/TTS 集成
- **运动反馈** - 点头、摇头、注视等基础动作
- **音频处理** - AGC、噪声抑制、回声消除
- **摄像头流** - MJPEG 实时预览
#### 部分实现功能 (Phase 14-21)
- **Phase 14** - 情感动作 API 基础设施 (手动触发可用)
- **Phase 19** - 重力补偿模式切换 (教学流程未实现)
### ❌ 未实现功能
#### 高优先级
- **Phase 13** - Sendspin 音频播放支持 (待实现)
- **Phase 14** - 自动情感动作反馈 (需与语音助手事件关联)
- **Phase 15** - 持续声源追踪 (仅唤醒时转向)
#### 中优先级
- **Phase 16** - 卡通风格运动模式 (需动态插值切换)
- **Phase 17** - 天线同步动画
- **Phase 18** - 人脸追踪与眼神交互
#### 低优先级
- **Phase 19** - 教学模式录制/播放功能
- **Phase 20** - IMU 环境感知响应
- **Phase 21** - Home Assistant 场景联动
---
## 功能优先级总结 (更新版)
### 高优先级 (已完成 ✅)
- ✅ **Phase 1-12**: 基础 ESPHome 实体 (45+ 个)
- ✅ 核心语音助手功能
- ✅ 基础运动反馈 (点头、摇头、注视)
### 高优先级 (部分实现 🟡)
- 🟡 **Phase 13**: 情感动作反馈系统
- ✅ Emotion Selector 实体与 API 基础设施
- ❌ 自动根据语音助手响应触发情感动作
- ❌ 意图识别与情感匹配
- ❌ 舞蹈动作库集成
### 高优先级 (未实现 ❌)
- ❌ **Phase 14**: 智能声源追踪增强
- ✅ 唤醒时转向声源
- ❌ 持续声源追踪
- ❌ 多人对话切换
- ❌ 声源可视化
### 中优先级 (部分实现 🟡)
- 🟡 **Phase 15**: 卡通风格运动模式
- ✅ 20Hz 统一控制循环架构 (优化以防止 daemon 崩溃)
- ✅ 姿态变化检测 + 状态查询缓存 (减少 daemon 负载)
- ✅ 平滑插值动作 + 呼吸动画
- ❌ 动态插值技术切换 (CARTOON 等)
- 🟡 **Phase 16**: 说话时天线同步
- ✅ 语音驱动头部摆动 (SpeechSwayGenerator)
- ❌ 天线随音频节奏摆动
### 中优先级 (未实现 ❌)
- ❌ **Phase 17**: 视觉注视交互 - 眼神交流
### 低优先级 (部分实现 🟡)
- 🟡 **Phase 18**: 重力补偿互动模式
- ✅ 重力补偿模式切换
- ❌ 教学式交互 (录制/播放功能)
### 低优先级 (未实现 ❌)
- ❌ **Phase 19**: 环境感知响应 - IMU 触发动作
- ❌ **Phase 20**: Home Assistant 场景联动 - 智能家居整合
---
## 📈 完成度统计
| 阶段 | 状态 | 完成度 | 说明 |
|------|------|--------|------|
| Phase 1-12 | ✅ 完成 | 100% | 40 个 ESPHome 实体已实现(Phase 11 LED 已禁用) |
| Phase 13 | 🟡 部分完成 | 30% | API 基础设施就绪,缺自动触发 |
| Phase 14 | ❌ 未完成 | 20% | 仅实现唤醒时转向 |
| Phase 15 | 🟡 部分完成 | 70% | 20Hz控制循环+姿态变化检测+状态缓存+呼吸动画已实现 |
| Phase 16 | 🟡 部分完成 | 50% | 语音驱动头部摆动已实现 |
| Phase 17 | ❌ 未完成 | 10% | 摄像头已实现,缺人脸检测 |
| Phase 18 | 🟡 部分完成 | 40% | 模式切换已实现,缺教学流程 |
| Phase 19 | ❌ 未完成 | 10% | IMU 数据已暴露,缺触发逻辑 |
| Phase 20 | ❌ 未完成 | 0% | 完全未实现 |
**总体完成度**: **Phase 1-12: 100%** | **Phase 13-20: ~35%**
---
## 🔧 Daemon 崩溃问题修复 (2025-01-05)
### 问题描述
长期运行过程中,`reachy_mini daemon` 会崩溃,导致机器人失去响应。
### 根本原因
1. **100Hz 控制循环过于频繁** - 每 10ms 调用一次 `robot.set_target()`,即使姿态没有变化
2. **频繁的状态查询** - 每次读取实体状态都调用 `get_status()`、`get_current_head_pose()` 等
3. **缺少变化检测** - 即使姿态没有变化,也会持续发送相同的命令
4. **Zenoh 消息队列堵塞** - 累积起来可能每秒 150+ 条消息,daemon 无法及时处理
### 修复方案
#### 1. 降低控制循环频率 (movement_manager.py)
```python
# 从 100Hz 降低到 20Hz
CONTROL_LOOP_FREQUENCY_HZ = 20 # 减少 80% 的消息量
```
#### 2. 添加姿态变化检测 (movement_manager.py)
```python
# 仅在姿态显著变化时发送命令
if self._last_sent_pose is not None:
max_diff = max(abs(pose[k] - self._last_sent_pose.get(k, 0.0)) for k in pose.keys())
if max_diff < 0.001: # 阈值: 0.001 rad 或 0.001 m
return # 跳过发送
```
#### 3. 状态查询缓存 (reachy_controller.py)
```python
# 缓存 daemon 状态查询结果
self._cache_ttl = 0.1 # 100ms TTL
self._last_status_query = 0.0
def _get_cached_status(self):
now = time.time()
if now - self._last_status_query < self._cache_ttl:
return self._state_cache.get('status') # 使用缓存
# ... 查询并更新缓存
```
#### 4. 头部姿态查询缓存 (reachy_controller.py)
```python
# 缓存 get_current_head_pose() 和 get_current_joint_positions() 结果
def _get_cached_head_pose(self):
# 100ms 内复用缓存结果
```
### 修复效果
| 指标 | 修复前 | 修复后 | 改善 |
|------|--------|--------|------|
| 控制消息频率 | ~100 msg/s | ~20 msg/s | ↓ 80% |
| 状态查询频率 | ~50 msg/s | ~5 msg/s | ↓ 90% |
| 总 Zenoh 消息 | ~150 msg/s | ~25 msg/s | ↓ 83% |
| Daemon CPU 负载 | 持续高负载 | 正常负载 | 显著降低 |
| 预期稳定性 | 数小时内崩溃 | 可稳定运行数天 | 大幅提升 |
### 相关文件
- `DAEMON_CRASH_FIX_PLAN.md` - 详细修复方案和测试计划
- `movement_manager.py` - 控制循环优化
- `reachy_controller.py` - 状态查询缓存
### 后续优化建议
1. ⏳ 动态频率调整 - 运动时 50Hz,空闲时 5Hz
2. ⏳ 批量状态查询 - 一次性获取所有状态
3. ⏳ 性能监控和告警 - 实时监控 daemon 健康状态
---
## 🔧 拍一拍唤醒与麦克风灵敏度修复 (2026-01-07)
### 问题描述
1. **拍一拍唤醒阻塞** - 拍一拍唤醒后对话功能不正常,存在阻塞问题
2. **麦克风灵敏度低** - 需要靠很近才能识别语音
### 根本原因
1. **音频播放阻塞** - `_tap_continue_feedback()` 在持续对话模式下播放提示音,阻塞了音频流处理
2. **AGC 设置不优化** - ReSpeaker 的自动增益控制 (AGC) 默认设置不适合远距离语音识别
### 修复方案
#### 1. 移除持续对话反馈中的音频播放 (satellite.py)
```python
def _tap_continue_feedback(self) -> None:
"""Provide feedback when continuing conversation in tap mode.
Triggers a nod to indicate ready for next input.
Sound is NOT played here to avoid blocking audio streaming.
"""
# NOTE: Do NOT play sound here - it blocks audio streaming
if self.state.motion_enabled and self.state.motion:
self.state.motion.on_continue_listening()
```
#### 2. 添加异常处理到 tap 回调 (voice_assistant.py)
```python
def _on_tap_detected(self) -> None:
"""Callback when tap is detected on the robot.
NOTE: This is called from the tap_detector background thread.
"""
try:
self._state.satellite.wakeup_from_tap()
# ... motion feedback
except Exception as e:
_LOGGER.error("Error in tap detection callback: %s", e)
```
#### 3. 优化麦克风设置 (voice_assistant.py)
```python
def _optimize_microphone_settings(self) -> None:
"""Optimize ReSpeaker microphone settings for voice recognition."""
# Enable AGC for better sensitivity at distance
respeaker.write("PP_AGCONOFF", [1])
# Set higher AGC max gain (default ~15dB -> 25dB)
respeaker.write("PP_AGCMAXGAIN", [25.0])
# Set AGC desired level (target output level)
respeaker.write("PP_AGCDESIREDLEVEL", [-20.0])
# Increase microphone gain
respeaker.write("AUDIO_MGR_MIC_GAIN", [2.0])
```
### 修复效果
| 问题 | 修复前 | 修复后 |
|------|--------|--------|
| 拍一拍持续对话 | 阻塞,无法正常对话 | 正常工作 |
| 麦克风灵敏度 | 需要靠近 ~30cm | 可在 ~1m 距离识别 |
| AGC 最大增益 | ~15dB | 25dB |
| 麦克风增益 | 1.0x | 2.0x |
### 相关文件
- `satellite.py` - 移除阻塞的音频播放
- `voice_assistant.py` - 添加麦克风优化和异常处理
---
### SDK 数据结构参考
```python
# 电机控制模式
class MotorControlMode(str, Enum):
Enabled = "enabled" # 扭矩开启,位置控制
Disabled = "disabled" # 扭矩关闭
GravityCompensation = "gravity_compensation" # 重力补偿模式
# Daemon 状态
class DaemonState(Enum):
NOT_INITIALIZED = "not_initialized"
STARTING = "starting"
RUNNING = "running"
STOPPING = "stopping"
STOPPED = "stopped"
ERROR = "error"
# 完整状态
class FullState:
control_mode: MotorControlMode
head_pose: XYZRPYPose # x, y, z (m), roll, pitch, yaw (rad)
head_joints: list[float] # 7 个关节角度
body_yaw: float
antennas_position: list[float] # [right, left]
doa: DoAInfo # angle (rad), speech_detected (bool)
# IMU 数据 (仅无线版本)
imu_data = {
"accelerometer": [x, y, z], # m/s²
"gyroscope": [x, y, z], # rad/s
"quaternion": [w, x, y, z], # 姿态四元数
"temperature": float # °C
}
# 安全限制
HEAD_PITCH_ROLL_LIMIT = [-40°, +40°]
HEAD_YAW_LIMIT = [-180°, +180°]
BODY_YAW_LIMIT = [-160°, +160°]
YAW_DELTA_MAX = 65° # 头部与身体偏航角最大差值
```
### ESPHome 协议实现说明
ESPHome 协议通过 protobuf 消息与 Home Assistant 通信。需要实现以下消息类型:
```python
from aioesphomeapi.api_pb2 import (
# Number 实体 (音量/角度控制)
ListEntitiesNumberResponse,
NumberStateResponse,
NumberCommandRequest,
# Select 实体 (电机模式)
ListEntitiesSelectResponse,
SelectStateResponse,
SelectCommandRequest,
# Button 实体 (唤醒/睡眠)
ListEntitiesButtonResponse,
ButtonCommandRequest,
# Switch 实体 (电机开关)
ListEntitiesSwitchResponse,
SwitchStateResponse,
SwitchCommandRequest,
# Sensor 实体 (数值传感器)
ListEntitiesSensorResponse,
SensorStateResponse,
# Binary Sensor 实体 (布尔传感器)
ListEntitiesBinarySensorResponse,
BinarySensorStateResponse,
# Text Sensor 实体 (文本传感器)
ListEntitiesTextSensorResponse,
TextSensorStateResponse,
)
```
## 参考项目
- [OHF-Voice/linux-voice-assistant](https://github.com/OHF-Voice/linux-voice-assistant)
- [pollen-robotics/reachy_mini](https://github.com/pollen-robotics/reachy_mini)
|