| tags | robotics, emotion, animation, best-practice, embodied-ai |
|---|
综合自:reachy-mini-conversation-app(Pollen Robotics)
如何为社交机器人设计情绪和动作系统,使其:
- 在交互时表现自然
- 在空闲时保持鲜活感
- 支持多种情绪/动作的无缝切换
- 与语音同步产生有机的运动
原则:机器人永远不应该完全静止。
实现:
- 空闲 0.3 秒后自动进入呼吸动画
- 呼吸参数:5mm Z 轴浮动 + 天线交替摆动
- 频率接近人类呼吸(6 次/分钟)
理由:静止的机器人显得机械和"死亡",持续的微运动创造亲和感。
架构:
┌─────────────────────────────────────┐
│ 最终姿态输出(100Hz) │
├─────────────────────────────────────┤
│ 主要动作(互斥) + 次要偏移(叠加) │
│ ├─ 情绪 ├─ 语音摆动 │
│ ├─ 舞蹈 └─ 人脸追踪 │
│ ├─ Goto 定位 │
│ └─ 呼吸 │
└─────────────────────────────────────┘
理由:
- 主要动作互斥避免冲突
- 次要偏移叠加增加自然感
- 单一控制点确保一致性
原则:说话时的运动应该由音频实时驱动。
关键参数:
| 参数 | 值 | 作用 |
|---|---|---|
| 延迟补偿 | 200ms | 对齐音频与机械延迟 |
| VAD 开启阈值 | -35 dB | 检测语音开始 |
| VAD 关闭阈值 | -45 dB | 滞后避免抖动 |
| 振荡轴数 | 6 轴 | pitch/yaw/roll/x/y/z |
理由:
- 多轴独立振荡避免机械感
- 响度驱动幅度保持动态
- 随机初始相位增加有机感
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| LLM 工具调用 | 上下文相关、智能选择 | 延迟较高 | 交互式情绪表达 |
| 随机触发 | 简单、不可预测 | 缺乏意图 | 空闲时的自发行为 |
| 时间触发 | 可预测、可控 | 机械感 | 定期演示 |
| 音频驱动 | 自然同步 | 需要音频输入 | 说话时的辅助运动 |
推荐:组合使用 - LLM 决定"做什么",音频驱动"怎么做",随机增加"变化"。
| 参数 | 保守值 | 活跃值 | 推荐 |
|---|---|---|---|
| Z 轴浮动 | 3mm | 10mm | 5mm |
| 呼吸频率 | 0.08 Hz | 0.15 Hz | 0.1 Hz |
| 天线摆动 | 10° | 20° | 15° |
| 天线频率 | 0.3 Hz | 0.7 Hz | 0.5 Hz |
推荐:使用保守值作为基础,在特定人格或情绪状态下调整。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 用户说话时 | 语音摆动 + 天线冻结 | 避免干扰监听 |
| 机器人说话时 | 语音摆动 + 情绪动作 | 增强表达力 |
| 短暂空闲(<15s) | 呼吸动画 | 保持鲜活感 |
| 长时间空闲(>15s) | LLM 触发创意动作 | 主动吸引注意 |
| 情绪切换 | 插值过渡(1s) | 避免突变 |
-
分层设计:将情绪、舞蹈、呼吸作为独立的 Move 对象,通过队列顺序执行
-
单一控制点:所有运动通过统一的
set_target输出,避免并发冲突 -
线程安全:控制循环独占状态,外部通过消息队列通信
-
时间对齐:使用单调时钟,避免系统时间跳跃影响动画
-
呼吸优先:任何空闲状态都应该回到呼吸,而不是完全静止
-
天线异步:两天线使用相反方向摆动,打破对称感
-
相位随机:每次会话使用不同的初始相位,避免重复感
-
响度映射:运动幅度应该与音量正相关,使用 gamma 曲线调整
-
时长协议:所有 Move 必须实现
duration属性,控制循环依赖它判断完成 -
复合时长:多个子动作组成的复合动作,总时长 = Σ 子动作时长
-
即时响应:收到完整工具参数后立即执行,异步返回结果给 LLM
-
空闲区分:空闲触发的工具调用不生成语音回复,避免打扰
| 动作类型 | 时长来源 | 特点 |
|---|---|---|
| 情绪动作 | 预录制数据 | 固定,由录制决定 |
| 舞蹈动作 | 程序生成 | 固定,由动作定义 |
| Goto 定位 | 参数指定 | 可配置(默认 1s) |
| 呼吸动作 | float("inf") |
无限循环,被中断 |
当前时间 - 开始时间 >= 动作时长 → 动作完成,开始下一个
OpenAI Server → response.function_call_arguments.done
↓
dispatch_tool_call()
↓
Tool.__call__()
↓
movement_manager.queue_move()
↓
function_call_output → OpenAI Server
关键事件:response.function_call_arguments.done 表示参数完全接收,触发执行。
| 反模式 | 问题 | 替代方案 |
|---|---|---|
| 直接操作姿态 | 绕过队列导致冲突 | 通过队列提交 Move |
| 完全静止 | 机械感、"死亡"感 | 持续呼吸动画 |
| 对称运动 | 机械、不自然 | 天线/相位异步 |
| 固定相位 | 重复、可预测 | 随机化初始相位 |
| 控制循环 I/O | 阻塞导致掉帧 | 异步/预取数据 |
| 突然切换 | 廉价感 | 插值过渡 |
| 时长不准 | 动作截断或拖沓 | 精确计算 duration |
| 流式参数处理 | 参数不完整 | 等待 .done 事件 |
- 呼吸动画在空闲 0.3 秒后自动启动
- 所有主要动作通过统一队列管理
- 所有 Move 实现
duration属性 - 语音摆动与音频输出同步(200ms 延迟)
- 天线在监听时冻结,恢复时平滑混合
- 空闲 15 秒后触发 LLM 创意动作
- 每次会话随机化振荡相位
- 控制循环保持 100Hz 稳定
- 工具调用等待完整参数再执行
创建时间:2026-03-02 更新时间:2026-03-02