从 Windows 调试到 Orin 真机:宇树 G1 导览机器人语音交互开发全记录
从 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 核心思路
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→ 推送 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. 环境
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 部署细节。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)