宇树G1机器人-第 01 章|系统全景与四条铁律
第 01 章|【系统全景与四条铁律】
所属:卷一 · ROS1 全栈 | 状态:已发布(2026-11-06) | 配套代码:
src
拿到宇树 G1 的第一天,我什么都没写。不是没想法,是知道只要动错一个地方,这套几十万的设备就可能再也起不来——而宇树出厂的系统并没有你想的那么皮实。
这篇文章是整个系列的开篇。不教你写代码,先把系统边界画清楚:哪些是宇树的、哪些是我们的、哪颗雷达只能归一个人用、哪几个服务绝对不能 systemctl stop。把这些搞明白,后面所有章节才有意义。
这一章要解决什么
- 设备到手、文档零散、官方 demo 能跑但不知道从何下手
- 想加自研定位/导航,但不敢动宇树自带的服务,怕把生产环境搞崩
- 需要一张“地图”:硬件怎么连、软件栈怎么分、雷达归谁、灰度怎么做
读完之后,你应该能说清楚这台 G1 上两套系统的边界在哪、四个共享资源(雷达、网口、时钟、运控)各自归谁,以及为什么后面 32 章的技术选型长成这个样子。
环境与前置
- 硬件:宇树 G1(29 DoF,机载主机 aarch64 / Ubuntu 20.04)+ Livox MID360(倒装,roll=180°)
- 出厂软件:宇树原生
unitree_sdk2/unitree_slam/advanced_slam,由 systemd 托管 - 我们的目标:在不破坏出厂系统的前提下,叠加一套自研的定位 + 导航 + Web 栈
- 前置知识:会用
systemctl、知道 ROS 话题是什么即可,其余每一章都会补
系统全景
先把整机拆成四层。后面 32 章,每一章都在这四层里的某一层干活。
┌─────────────────────────────────────────────────────────────┐
│ Web 层 HTTP 8080(页面/API) + WebSocket 9002(位姿/点云) │
├─────────────────────────────────────────────────────────────┤
│ 导航层 move_base(TEB) ──ROS1──▶ Nav2(Smac2D/TEB) ──ROS2──▶ │
│ g1_nav_bridge → loco sport 服务 │
├─────────────────────────────────────────────────────────────┤
│ 定位层 FAST-LIO2 → hdl(BBS) → g1_global_init → open3d_loc │
│ 输出 /localization_3d @10Hz + map→odom TF │
├─────────────────────────────────────────────────────────────┤
│ 驱动层 Livox 官方 livox_ros_driver2(倒装走配置 roll=180) │
│ MID360 点云 10Hz / IMU 200Hz │
├─────────────────────────────────────────────────────────────┤
│ 宇树原生 unitree_slam / advanced_slam(出厂 SLAM,独占雷达) │
│ loco sport 服务(步态/运动控制,我们一直复用) │
└─────────────────────────────────────────────────────────────┘
硬件拓扑:雷达走网口,这是后面一切麻烦的源头
MID360 不是 USB 设备,它走以太网。机载主机有一块网口专门接雷达,配的是静态 IP(driver.host_ip,出厂网段 192.168.123.x)。这一个事实,在第 14、21、24 章会反复咬人:
- 开机时 NetworkManager 配网卡可能晚于我们的服务启动 → 雷达驱动 bind 失败 → 收不到数据(第 24 章的
wait_lidar_net就是为它写的) - 雷达是独占资源:同一时间只能有一个进程持有它的数据流
时间也是共享资源:比雷达更隐蔽的第二条命门
雷达之外还有一个被低估的共享资源——时钟。机载主机的 RTC 不准,开机瞬间系统时间是错的,要靠校时服务在头几分钟里把它拽回来。而现场一度同时存在三套校时(ntp-sync.service、rc.local 里的 ntpdate、chronyd),它们互相拉扯,曾在开机约 2 分钟时把时钟跳变 +128 秒。
后果比“网卡晚了”更狠:MID360 报文时间来自雷达内部时基,时钟一跳,FAST-LIO2 的 lidar/IMU 时间对齐被撕裂 → 里程计发散 → open3d_loc 崩溃重启,进入循环。所以 g1-ros2.service 里显式写了 After=chronyd.service,启动脚本另有一道 wait_clock_stable() 兜底。这条线在第 06 章(1840 ms → 2.9 ms)和番外A(200 Hz 被录成 12 Hz)会正面展开。
软件栈边界:宇树的归宇树,我们的归我们
| 模块 | 归属 | 我们动它吗 |
|---|---|---|
unitree_slam / advanced_slam | 宇树出厂 SLAM | 不动代码,只做有界切换 |
| loco sport 服务(运动控制) | 宇树 | 一直复用,从不替换 |
| livox_ros_driver2 | Livox 官方 | 用官方包,倒装只改 MID360_config.json 的 "roll": 180.0 |
| FAST-LIO2 / hdl / open3d_loc | 开源 | 深度改造后自研部署 |
| g1_global_init / g1_nav_bridge | 自研 | 全书主角 |
| web_server / web_bridge | 自研 | 全书主角 |
注意 loco sport 这一行:整个系列(第 1-32 章),我们从来没有自己写过步态控制器。运动控制是另一条线(见姊妹篇《G1 运动控制开发心得》),导航线只负责“告诉它往哪走、走多快”,然后调用宇树的运动接口。
运控侧:第三个共享资源是控制权
导航层最后一步是把 cmd_vel 交给宇树运动接口,而那条链路上不止我们一个写者:运控产品侧会下发自己的指令,手动接管、急停也会插进来。两个进程各发一点,叠加起来就会失控——真机上出过一次 max|dq| 从 0.04 rad/s 冲到 9.89 rad/s 的事故,关节保护直接动作。
所以我们不直连宇树接口,而是把运控收进一个网关(TCP 9500,Domain42 网络隔离),由网关统一裁决“现在归谁控制”,并用 FSM 状态码(501 / 802 / 1000)把控制权切换显式化。这条约束在第 30、31 章展开,本章只需要记住一句:运控是单 writer 的,谁写不取决于代码跑在哪个进程里。
四条铁律
这是整个项目用几次惊险换回来的规矩,写在最前面。
铁律一:不动宇树服务
unitree_slam.service 由 systemd 托管,Restart=on-failure + KillMode=control-group。这意味着:
- 你
kill它,它会被 systemd 拉起来,而KillMode=control-group会连带清掉同组的雷达驱动 - 你改了它的配置,下次升级/重启可能直接起不来,而且你很难还原
我们唯一允许的操作是 systemctl start/stop,并且必须记住恢复命令。真误停了,恢复路径是:
sudo systemctl stop g1-ros2.service # 先停我们的栈,释放雷达
sudo systemctl start unitree_slam.service
铁律二:旧栈全程可用
自研栈(v1 ROS1 / v2 ROS2)和宇树出厂 SLAM 互斥但不能互相破坏。任何时刻,停掉我们的栈,出厂系统必须能立刻接管。这决定了:
- 所有自研组件装在用户目录(
~/xxj/fast-lio、~/micromamba),不污染系统 - 不 root 安装,不修改系统级 ROS 环境
- systemd 服务里用
Conflicts=unitree_slam.service advanced_slam.service声明互斥,由启动脚本负责切换,而不是手动乱停
铁律三:每阶段先验收再推进
这个项目跨越 32 章、两次大迁移(ROS1→ROS2、RPP→TEB)。能不走回头路,靠的就是每一步都有量化验收:
- 录一条基线 bag,把“好不好”变成数字(第 04 章)
- 每章都有“验证与量化”一节,没有数字不算完
- 切换窗口(灰度)是有界的:同一个验收窗口内,允许两栈对照,但写雷达的永远只有一个
铁律四:雷达独占
这是最容易被低估的一条。MID360 同一时间只能供一个消费方。所以:
- 不能让宇树 SLAM 和我们的 FAST-LIO2 同时跑——会抢雷达
- 不能简单地“并行灰度”——没有第二个雷达
- 灰度 = 有界切换窗口:停 A、起 B、验收、不行就切回 A。窗口内只允许一个写雷达
这条约束直接塑造了第 17 章(TF 树设计)、第 21 章(systemd 互斥)、第 24 章(一键切换与回退)的所有设计。
三条技术链与全系列路线图
| 链 | 内容 | 章节 |
|---|---|---|
| 定位 | 驱动 → FAST-LIO2 → 全局初始化 → ICP 跟踪 | 第 03-11 章 |
| 导航 | move_base(TEB) → Nav2(Smac+TEB) → 楼梯通行 | 第 12-13、20-22、26-32 章 |
| Web 同步 | WebSocket 9002 / web_server / stair-walk | 第 18-19、32 章 |
三条链对应卷一到卷六的阶段推进:先用 ROS1 把定位链路跑通 → 迁到 ROS2 → 换成 Nav2/TEB → 接上楼梯与运控 → 部署交付。初始化不算独立一条链,它是定位链的前半段:没有 g1_global_init 把机器人放到地图里,后面 ICP 跟踪无从谈起。读的时候不必按章号顺序,但每一卷都假定上一卷已经可运行。
关键数据(真机实测)
| 指标 | 数值 |
|---|---|
| 点云 | 10 Hz |
| IMU | 200 Hz |
定位输出 /localization_3d | 10 Hz |
| 全局定位耗时 | 7.5 s |
| Web 端口 | HTTP 8080 / WebSocket 9002 |
| 运控网关 | TCP 9500(Domain42) |
验证与量化
开篇没有算法可测,但“边界是否真的画住了”可以验。拿到机器后,按这四条自检,任何一条不成立,后面都不用往下走:
| 检查项 | 命令 / 手段 | 通过标准 |
|---|---|---|
| 互斥声明生效 | systemctl cat g1-ros2.service | grep Conflicts | 同时列出 unitree_slam.service 与 advanced_slam.service |
| 雷达只有一个消费者 | 起自研栈后 ros2 topic hz /livox/lidar;同时看 unitree_slam 状态 | 点云稳定 10 Hz,且 unitree_slam.service 为 inactive |
| 停栈能还权 | systemctl stop g1-ros2.service 后 systemctl start unitree_slam.service | 出厂 SLAM 在 1 分钟内恢复,不再需要手动清进程 |
| 时钟已稳定 | timedatectl 观察开机后 3 分钟;chronyc tracking | 无秒级跳变,System time 偏移持续收敛 |
这四条就是本章的“数字”。后面每一章的验收口径,都是在这个边界上继续加刻度。
踩坑清单
| 现象 | 根因 | 解法 |
|---|---|---|
| 误停 unitree_slam 后无法恢复 | systemd 托管 + KillMode=control-group 连带杀雷达驱动 | 只用 systemctl start/stop,记住恢复命令 |
| 自研栈起来后雷达没数据 | 与宇树 SLAM 同时持有雷达 | Conflicts= 互斥 + 启动脚本切换 |
| 开机后整链起不来 | 网卡/雷达就绪晚于服务启动 | NetworkManager-wait-online + wait_lidar_net(第 24 章) |
| 开机约 2 分钟里程计发散、open3d_loc 崩溃循环 | 三套校时互相拉扯,时钟跳变 +128 s 撕裂 lidar/IMU 对齐 | 只留 chronyd,服务 After=chronyd.service + wait_clock_stable() |
| 关节突然超速、保护动作 | 运控侧多 writer 叠加(0.04 → 9.89 rad/s) | 收进网关,单 writer + FSM 显式交接(第 30、31 章) |
| 改坏了出厂环境 | 在系统目录/root 下装自研依赖 | 全部装用户目录,不 root |
配套代码
- 真机工程
fast-lio-v2/deploy_humanoid/systemd/g1-ros2.service:互斥、启动依赖(网卡/时钟)与杀进程范围声明 - 真机工程
fast-lio-v2/deploy_humanoid/start_ros2.sh:一键启停、wait_lidar_net与wait_clock_stable兜底 - 真机工程
fast-lio-v2/deploy_humanoid/config.json:端口(8080 / 9002 / 9500)与雷达网段配置的单一来源
架构图源文件与端口/话题速查表待补(
src/ep01-系统全景)。本系列的src/目录沿用历史epXX编号,与章号的对应关系见src/README.md。
小结
这一章没有一行算法,但它是后面所有章节的约束来源:
- 不动宇树服务 → 自研栈必须可共存、可切换、可回退
- 旧栈全程可用 → 一切装用户目录、不 root、systemd 互斥
- 先验收再推进 → 每章都要有数字
- 雷达独占 → 灰度只能是有界切换窗口
四个共享资源各归一人:雷达归当前写者、网口归 NetworkManager、时钟归 chronyd、运控归网关。把这四个“唯一”钉死,两套系统才能在同一台机器上活下去。
下一章开始动手:在不 root、不碰宇树环境的前提下,把 ROS1 Noetic 和依赖装齐。
下章预告
第 02 章:【环境搭建:不 root 装 ROS1 Noetic 与依赖】
GitHub地址
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)