从 Windows 调试到 Orin 真机:宇树 G1 导览机器人语音交互开发全记录


摘要

本文记录一套面向展厅导览场景的 G1 语音交互系统从 0 到真机落地的过程。系统不是简单「聊天机器人」,而是 ASR → 意图理解 → 导览任务 → TTS 的完整闭环,且遵循「大模型只理解、不直控底层」的安全原则。

真机部署在 Jetson Orin NX 上,ASR/TTS 走 宇树内置离线链路,对话与复杂意图走 通义千问 Qwen Omni API。历经 Windows 模拟调试、云端语音方案验证、真机联调、唤醒词优化、回声抑制、网络/DNS 踩坑等阶段,最终形成可长期维护的 g1_guide_robot/voice/ 模块。


一、项目背景与目标

1.1 要做什么

  • 中文语音对话(科技、展品、公司、场馆问答)
  • 语音发起导览:「带我去实验室讲讲」
  • 到点自动讲解(TTS 播报 locations.yaml 里的讲解词)
  • 安全指令最高优先级:停止 / 取消 / 急停

1.2 不是什么

  • 不是让大模型直接调 SDK 走路
  • 不是纯云端 ASR/TTS demo(真机最终走宇树本机语音)
  • 不是一次性脚本,而是可配置、可切换后端的生产架构

二、整体架构(语音链路)

用户说话
    ↓
[ASR 语音识别]
    ├─ 开发期:ConsoleASR / DashScopeASR / 麦克风
    └─ 真机:UnitreeASR(DDS rt/audio_msg + g1_asr_bridge)
    ↓
[HybridIntentParser 混合意图]
    ├─ 规则优先:停止、取消、楼层确认、导览点位匹配
    └─ Qwen Omni 兜底:复杂说法、开放问答
    ↓
[GuideTaskManager 导览任务]
    chat / guide_to / explain / tour / navigate ...
    ↓
[TTS 语音播报]
    ├─ 开发期:ConsoleTTS / DashScope CosyVoice
    └─ 真机:UnitreeCliTTS(g1_tts_cli / g1_tts_worker)

核心代码目录:

g1_guide_robot/voice/
  loop.py           # 语音主循环、唤醒词、TTS 静音窗口
  asr.py            # 多后端 ASR(Console / DashScope / Unitree)
  tts.py            # 多后端 TTS(Console / DashScope / Unitree CLI)
  recorder.py       # 麦克风录音、宇树组播录音
  streaming_tts.py  # 流式播报(云端方案)
  audio_devices.py  # 音频设备枚举
  audio_player.py   # 本地播放
  status.py         # 终端状态行

三、开发历程(按阶段)

阶段 0:方案设计与模拟闭环(Week 1)

目标: 不接硬件,先把业务跑通。

  • 默认 robot_backend: simulatornavigator_backend: simulator
  • ASR/TTS 用 ConsoleASR / ConsoleTTS(键盘输入、打印输出)
  • 完成:loop.py 主循环、hybrid_intent.py 规则+LLM、task_manager.py 任务状态机

收获: 先验证「意图 → 任务 → 回复」逻辑,再碰硬件,避免联调时分不清是语音问题还是导航问题。


阶段 1:Windows 本地 + 云端语音验证(Week 2)

目标: 在开发机上体验真实听感和对话质量。

模块 方案 作用
ASR DashScope 实时识别 验证识别率、延迟
TTS CosyVoice / TTSMaker 验证音色、长句稳定性
LLM Qwen Omni Plus 聊天 + 复杂意图

关键设计:HybridIntentParser

  • 安全类意图stop / cancel_task / floor_confirmed)永远本地规则优先,不交给 LLM
  • 已能规则解析的导览意图guide_to_location 等)跳过额外 LLM 调用,降低延迟和成本
  • 只有规则搞不定的句子才走 Qwen

这一步证明:语音体验可以云端,但导览控制不能交给模型直出。


阶段 2:对接宇树 G1 真机语音(Week 3–4)

转折点: G1 教育版自带离线 ASR 和机载 TTS,云端语音在真机上不是最优解。

2.1 ASR:UnitreeASR
  • 订阅 DDS 话题 rt/audio_msg
  • 通过 g1_asr_bridge 二进制桥接接收识别文本
  • 网卡绑定 eth0(192.168.123.x 宇树内网)
# asr.py 核心思路
class UnitreeASR(SpeechToText):
    """G1 onboard offline ASR from DDS topic rt/audio_msg."""
2.2 TTS:UnitreeCliTTS
  • 调用 unitree_sdk2-main/build/bin/g1_tts_cli
  • 长句用常驻 g1_tts_worker,避免每句冷启动
  • 需配置 LD_LIBRARY_PATH 指向 aarch64 库
2.3 部署 Profile:G1_DEPLOY_PROFILE=g1
# deploy/profiles.yaml
g1:
  runtime:
    robot_backend: unitree
    navigator_backend: single_floor
  voice:
    tts_backend: unitree_cli
    asr_backend: unitree
    stream_reply: false
  robot:
    network_interface: eth0

真机启动:

export G1_DEPLOY_PROFILE=g1
export G1_NETWORK_INTERFACE=eth0
cd ~/voice-guide-g1
bash ~/fix_robot_network.sh   # 校时 + DNS,否则 API/HTTPS 全挂
python app.py --mode voice
# 或 onboard:Web + 语音同机
python app.py --mode onboard --host 0.0.0.0 --port 8000

阶段 3:唤醒词与误识别优化(持续迭代)

G1 内置 ASR 对方言/噪声敏感,「小达小达」常被识别成「小德小德」「小丹小丹」等。

解法:loop.py 做模糊唤醒匹配,而不是死板字符串相等:

_WAKE_CHAR = r"[达德丹答晓查康兰打大塔他她它]"
_FUZZY_WAKE_PATTERNS = (
    re.compile("小" + _WAKE_SEPARATOR + _WAKE_CHAR + ...),
    ...
)

同时支持:

  • 双唤醒词:小达小达
  • 单唤醒词:小达,带我去大厅
  • 唤醒后 N 秒内免重复唤醒(wake_followup_seconds

经验: 真机语音产品 30% 工作量在唤醒与误触发,不在 LLM。


阶段 4:回声抑制与「自己听自己」(关键坑)

现象: TTS 播报时,麦克风/ASR 把机器人自己说的话又识别一遍,造成死循环或误触发。

解法: _tts_playback_session — 播报期间 ASR 静音:

@contextmanager
def _tts_playback_session(asr, tts):
    asr.begin_tts_mute()   # 播报期间不送识别
    tts.begin_playback_session()
    try:
        yield
    finally:
        tts.end_playback_session()
        asr.end_tts_mute()

并加 post_tts_guard_seconds(播报结束后几秒内降低误触发)。

注意: 不要频繁重启 ASR bridge(会触发 SetMode 提示音),静音比重启优雅。


阶段 5:Orin 真机环境与工程化(Week 5+)

5.1 网络坑(必踩)
问题 现象 解决
系统时间 1970 HTTPS/DNS 全失败 启动脚本校时
网关不是 DNS gitee.com 解析失败 固定 223.5.5.5 / 114.114.114.114
WiFi SSH 慢 git push 像卡死 大 buffer + 后台任务 + 日志
5.2 代码托管
  • 机器人 ~/g1_voice_chat → 推送 Gitee voice-guide-g1
  • Windows 开发代码合并进 master
  • 新建 develop 研发分支
  • 约定:语音模块以真机为准,Windows 与 Orin 哈希对比 19 个 voice/agent 文件一致
5.3 运行模式
模式 用途
console 纯文本调试意图
voice 真机语音导览
web 控制台 + 地图
onboard Orin 同机:Web + 语音(推荐)

四、技术选型总结

层级 真机最终方案 原因
ASR 宇树内置离线 低延迟、不依赖外网、与机载麦匹配
TTS g1_tts_cli / worker 原生喇叭、稳定
LLM Qwen Omni API 开放问答、复杂意图;本地 Orin 不必跑大模型
意图 规则 + LLM 混合 安全、省 token、导览指令可预期
录音 宇树组播 / 本机麦 真机走 DDS,开发机走 PortAudio

曾评估但未采用为主路径:

  • 外置 Jetson Nano 专职语音 + Orin 专职导航(可行,但 ASR/TTS 绑定 G1 网段,全放 Orin 更简单)
  • 全云端 DashScope 语音(开发好用,真机延迟和离线差)

五、踩坑清单(给后来人)

  1. ASR 识别到 TTS 内容 → 播报期 mute,别重启 bridge
  2. 唤醒词太死板 → fuzzy regex + 单/双唤醒模式
  3. Orin 时间不对 → 所有 API 失败,先 date 再调试
  4. eth0 / wlan0 混用 → 宇树 SDK 走 eth0,SSH 走 WiFi,别搞错网卡
  5. Windows 无 git → 用脚本 + dulwich 推 Gitee;真机以 SSH 为准
  6. 长 TTS 阻塞对话 → worker 常驻 + 分句 + stream_tts_sentences_only
  7. LLM 直接控机器人 → 禁止;必须经 GuideTaskManager 白名单

六、当前能力与指标(真机)

  • 唤醒词:「小达小达」「小达」
  • 支持:聊天、知识问答、导览、到点讲解、停止/取消
  • 语音文件:g1_guide_robot/voice/ 共 9 个模块,与 Windows 开发机 字节级一致
  • 部署路径:~/voice-guide-g1develop 分支)
  • 启动:G1_DEPLOY_PROFILE=g1 + app.py --mode voice|onboard

七、后续规划(语音侧)

  • Nano 拆分方案(若 Nav2 吃满 Orin 算力):Nano 跑 voice loop,Orin 提供 HTTP 任务 API
  • 流式 TTS + 流式 LLM 更低首字延迟(真机 profile 目前 stream_reply: false
  • 多语言 / 多方言唤醒词库
  • 语音状态 Web 可视化(与 /map 控制台联动)

导航侧另文,不在本篇展开。


八、附录:最小真机语音启动命令

# 1. 网络
bash ~/fix_robot_network.sh

# 2. 环境
export G1_DEPLOY_PROFILE=g1
export G1_NETWORK_INTERFACE=eth0
export DASHSCOPE_API_KEY=你的密钥

# 3. 启动
cd ~/voice-guide-g1
python app.py --mode voice

控制台应出现:

● 监听中  G1 内置离线 ASR

说「小达小达,你好」或「小达,带我去实验室」进行验证。


九、结语

G1 导览机器人的语音模块,本质上是 「可靠听 + 可靠懂 + 可靠说 + 安全控」 四件事。大模型很强,但展厅机器人真正难的是:唤醒、回声、真机网络、与宇树 SDK 磨合。

我们走过的路径是:先模拟、再云端验证体验、最后真机切宇树原生 ASR/TTS,并用规则层守住安全底线。 如果你也在做 G1 或类似人形导览,希望这篇历程能少踩几个我们踩过的坑。


仓库(Gitee): li-kui-who-loves-learning/voice-guide-g1
硬件: 宇树 G1-EDU + Jetson Orin NX Developer Kit
联系 / 交流: 欢迎在评论区讨论唤醒词、ASR _bridge 或 Orin 部署细节。


Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐