从 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: simulator、navigator_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 核心思路classUnitreeASR(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.yamlg1:runtime:robot_backend:unitreenavigator_backend:single_floorvoice:tts_backend:unitree_cliasr_backend:unitreestream_reply:falserobot:network_interface:eth0真机启动:
exportG1_DEPLOY_PROFILE=g1exportG1_NETWORK_INTERFACE=eth0cd~/voice-guide-g1bash~/fix_robot_network.sh# 校时 + DNS,否则 API/HTTPS 全挂python app.py--modevoice# 或 onboard:Web + 语音同机python app.py--modeonboard--host0.0.0.0--port8000阶段 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 静音:
@contextmanagerdef_tts_playback_session(asr,tts):asr.begin_tts_mute()# 播报期间不送识别tts.begin_playback_session()try:yieldfinally: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→ 推送 Giteevoice-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 语音(开发好用,真机延迟和离线差)
五、踩坑清单(给后来人)
- ASR 识别到 TTS 内容→ 播报期 mute,别重启 bridge
- 唤醒词太死板→ fuzzy regex + 单/双唤醒模式
- Orin 时间不对→ 所有 API 失败,先
date再调试 - eth0 / wlan0 混用→ 宇树 SDK 走 eth0,SSH 走 WiFi,别搞错网卡
- Windows 无 git→ 用脚本 + dulwich 推 Gitee;真机以 SSH 为准
- 长 TTS 阻塞对话→ worker 常驻 + 分句 +
stream_tts_sentences_only - LLM 直接控机器人→ 禁止;必须经
GuideTaskManager白名单
六、当前能力与指标(真机)
- 唤醒词:「小达小达」「小达」
- 支持:聊天、知识问答、导览、到点讲解、停止/取消
- 语音文件:
g1_guide_robot/voice/共 9 个模块,与 Windows 开发机字节级一致 - 部署路径:
~/voice-guide-g1(develop分支) - 启动:
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. 环境exportG1_DEPLOY_PROFILE=g1exportG1_NETWORK_INTERFACE=eth0exportDASHSCOPE_API_KEY=你的密钥# 3. 启动cd~/voice-guide-g1 python app.py--modevoice控制台应出现:
● 监听中 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 部署细节。