宇树Go2 Gazebo仿真环境搭建:从URDF到ros2_control的完整实践
1. 为什么要在Gazebo里折腾一只宇树Go2
如果你手上有一台宇树Go2,或者正在评估要不要入手,大概率会遇到一个很现实的问题:真机太贵、太吵、太容易摔坏,而且每次调试步态参数都得把狗搬到空地上,效率极低。我自己的Go2在实验室里跑了一个月,膝盖关节的塑料护罩就换了两套,后来痛定思痛,决定把大部分算法验证工作搬到仿真里做。Gazebo虽然不是最轻量的选择,但它在ROS生态里的成熟度、传感器插件的丰富程度,以及和ros2_control的配合度,目前仍然是四足机器人开发绕不开的一环。
这篇内容就是把我从零搭建Go2 Gazebo仿真环境的完整过程拆开讲清楚。核心关键词包括宇树机器狗、Go2、Gazebo、仿真环境搭建,但我不想写成一份冷冰冰的安装手册,而是把每一步背后的取舍、踩过的坑、以及那些文档里不会写的细节都摊开来说。适合谁看?如果你已经装好了Ubuntu和ROS2,能看懂基本的launch文件,但还没把Go2的模型真正跑进Gazebo里,那这篇就是为你准备的。如果你连ROS2都没碰过,建议先去补一下基础,否则后面会有点吃力。
先说结论:Go2的Gazebo仿真环境搭建,难点不在Gazebo本身,而在于模型文件的来源、URDF和SDF之间的转换、以及控制器配置的匹配。很多人卡在“模型加载出来是白的”或者“关节不动”这两个问题上,本质上都是这几个环节没打通。下面我按实际操作的顺序,从环境准备一路讲到问题排查,中间会穿插参数计算和配置细节,尽量让你能直接抄作业。
2. 环境准备与版本选型的关键决策
2.1 Ubuntu和ROS2版本怎么选
宇树官方对Go2的ROS2支持,目前主要围绕Foxy和Humble两个版本。Foxy是ROS2的第一个LTS,很多早期的Go2示例包都是基于它写的;Humble则是更新、维护更活跃的版本,Gazebo的集成也更顺滑。我一开始用的是Foxy,因为手头的教程大多是Foxy的,但后来发现Gazebo在Foxy下的版本偏老,一些新的传感器插件支持不够好,最后还是切到了Humble。
如果你是从零开始,我建议直接上Ubuntu 22.04加ROS2 Humble。原因很简单:Humble的Gazebo版本是Fortress(也就是Ignition Gazebo的新名字),虽然和经典Gazebo有差异,但社区迁移已经基本完成,宇树的新版unitree_ros2包也在往Humble上靠。Ubuntu 20.04加Foxy的组合不是不能用,但你会花更多时间在解决依赖冲突上。
安装ROS2 Humble的步骤这里不展开,官方文档写得很清楚。重点提醒一句:安装完ROS2后,一定要确认
gz sim --version
能正常输出,Humble对应的是Gazebo Fortress,版本号大概是6.x或7.x。如果你看到的是
gazebo --version
输出9.x或11.x,那说明你装的是经典Gazebo,和ROS2 Humble的默认集成会有出入。
2.2 宇树官方包和社区包的取舍
宇树在GitHub上提供了unitree_ros2和unitree_ros两个仓库,前者是ROS2的,后者是ROS1的。Go2的模型文件主要在unitree_ros里,但ROS2的包需要自己做一些转换。社区里也有人把Go2的URDF直接移植到了ROS2,比如go2_ros2这个包,但质量参差不齐,有的缺少惯性矩阵,有的关节限位写错了。
我的做法是:以宇树官方的URDF为基础,自己手动补全Gazebo需要的标签。官方URDF的好处是尺寸和关节定义准确,坏处是它主要面向真机控制,缺少Gazebo仿真必需的
<gazebo>
标签、
<transmission>
标签和惯性参数。你需要做的就是在每个link里加上惯性矩阵,在每个joint里加上transmission和Gazebo的关节控制插件。
这里有个细节:Go2的URDF里,大腿和小腿的惯性矩阵不能随便填。我试过用默认的球体惯性,结果狗在Gazebo里站都站不稳,一松手就瘫。后来用SolidWorks算了一下,大腿的转动惯量大概是0.0025 kg·m²左右,小腿是0.0018 kg·m²左右,具体数值和你的模型精度有关。如果你懒得算,可以用一个近似公式:对于长方体,I = m*(l²+w²)/12,其中l是长度,w是宽度。这个近似在仿真里够用了。
2.3 Gazebo版本差异带来的坑
Gazebo从经典版(Gazebo Classic)迁移到新版(Ignition/Gazebo Sim)后,很多API变了。比如经典版里用
libgazebo_ros_control.so
,新版里要用
gz_ros2_control
。宇树的官方包大多是针对经典Gazebo写的,如果你在Humble下直接用,会发现插件加载失败。
我的建议是:如果你用的是Humble,就统一用新版Gazebo的插件体系。具体来说,关节控制用
gz_ros2_control
,传感器用
gz-sim-imu-system
和
gz-sim-contact-system
。这些插件在Humble的默认安装里都有,不需要额外编译。如果你非要用经典Gazebo,那就得把ROS2版本降到Foxy,或者自己编译gazebo_ros_pkgs,后者非常折腾,不推荐。
还有一个容易忽略的点:Gazebo Sim的模型加载路径和经典版不一样。经典版用
GAZEBO_MODEL_PATH
,新版用
GZ_SIM_RESOURCE_PATH
。如果你把Go2的模型放在自定义目录里,记得把这个目录加到
GZ_SIM_RESOURCE_PATH
里,否则Gazebo找不到mesh文件,模型会显示成白色方块。
3. 模型文件处理与URDF转SDF的实操细节
3.1 从官方URDF到Gazebo可用模型的完整流程
拿到宇树官方的Go2 URDF后,不能直接扔进Gazebo。你需要做三件事:补惯性、加transmission、加Gazebo插件。我按顺序说。
补惯性是最基础的一步。URDF里每个link都需要
<inertial>
标签,包含质量和惯性矩阵。官方URDF里有些link的惯性是空的,或者只写了质量没写矩阵。你可以用
check_urdf
命令检查,如果报错说缺少惯性,那就得补。补的时候注意,惯性矩阵的参考系是link的质心坐标系,不是关节坐标系。如果你搞混了,狗在仿真里会像喝醉一样乱晃。
加transmission是为了让ros2_control能控制关节。每个需要主动控制的关节,都要加一个
<transmission>
标签,指定关节名和执行器名。Go2有12个主动关节(每条腿3个),所以你要加12个transmission。执行器名可以随便起,但要和后面的控制器配置对应上。
加Gazebo插件是最后一步,也是最容易出错的一步。你需要加两类插件:一类是关节控制插件,一类是传感器插件。关节控制插件用
gz_ros2_control
,配置里指定关节名、PID参数和命令接口。传感器插件看你的需求,IMU用
gz-sim-imu-system
,足端力传感器用
gz-sim-contact-system
。
3.2 URDF和SDF的转换陷阱
Gazebo Sim虽然支持直接加载URDF,但内部会把它转成SDF。这个转换过程有几个坑。第一个坑是mesh路径。URDF里的mesh路径是相对路径,Gazebo转换时会按
GZ_SIM_RESOURCE_PATH
去找。如果你把模型放在
~/go2_ws/src/go2_description/
下,就要把
~/go2_ws/src
加到
GZ_SIM_RESOURCE_PATH
里,否则mesh加载失败。
第二个坑是关节类型。URDF里的
continuous
关节在SDF里会变成
revolute
,但限位会丢失。Go2的髋关节是
continuous
的,如果你不手动加限位,仿真里关节会无限旋转,狗会像陀螺一样转圈。我的做法是在URDF里就把
continuous
改成
revolute
,然后加上合理的限位,比如髋关节的限位设成±0.8 rad,大腿设成±1.2 rad,小腿设成±2.0 rad。这些数值参考了Go2的真机参数,实测在仿真里比较合理。
第三个坑是碰撞体。URDF里的碰撞体默认用视觉mesh,但视觉mesh往往太精细,会导致碰撞检测很慢。我建议把碰撞体换成简单的几何形状,比如圆柱或长方体。Go2的大腿和小腿可以用圆柱近似,机身用长方体。这样仿真速度能提升不少,而且碰撞检测更稳定。
3.3 用Blender导出Gazebo模型的注意事项
有些时候你需要自己建一些场景物体,比如台阶、斜坡、障碍物。用Blender建完模后,导出成DAE或STL,再在URDF里引用。这里有几个细节:Blender的坐标系是Z轴向上,Gazebo也是Z轴向上,但Blender的Y轴和Gazebo的Y轴方向相反。导出时记得在Blender里把模型旋转一下,或者导出后在URDF里加一个
<origin>
标签调整。
另外,Blender导出的DAE文件里,材质和纹理路径可能是绝对路径。Gazebo加载时会找不到纹理,模型显示成灰色。解决办法是在Blender导出时选择“复制纹理”或者手动把纹理路径改成相对路径。我一般用STL格式,虽然不带纹理,但胜在简单可靠,不会出路径问题。
4. 控制器配置与ros2_control集成
4.1 ros2_control的配置结构
ros2_control的配置分两部分:一部分在URDF里,定义关节和接口;另一部分在YAML文件里,定义控制器参数。URDF里的
<ros2_control>
标签要指定关节名、命令接口和状态接口。Go2的12个关节,命令接口都是
position
,状态接口是
position
和
velocity
。
YAML文件里要配置两个控制器:一个
joint_state_broadcaster
,用来发布关节状态;一个
joint_trajectory_controller
,用来接收轨迹命令。如果你要做步态控制,可能还需要一个自定义的控制器,这个后面再说。
配置的时候注意,
joint_trajectory_controller
的
joints
列表要和URDF里的关节名完全一致,大小写都不能错。我见过有人把
FL_hip_joint
写成
fl_hip_joint
,结果控制器加载失败,查了半天才发现是大小写问题。
4.2 控制器参数的计算与调试
joint_trajectory_controller
的PID参数需要调。Go2的关节电机是准直驱的,响应很快,所以P可以给大一点,I和D给小一点。我的经验值是P=100,I=0.1,D=1.0。这个值在仿真里能让关节快速跟踪轨迹,又不会震荡。如果你发现关节有高频抖动,先把D调大,再考虑降P。
还有一个参数是
action_monitor_rate
,默认是20Hz。如果你做高频步态控制,可以调到100Hz,但要注意Gazebo的实时性,调太高会导致仿真变慢。我一般用50Hz,兼顾了控制精度和仿真速度。
4.3 步态控制器的接入方式
如果你只是想让狗在Gazebo里走起来,可以用一个简单的正弦步态控制器。思路是:给每个关节一个正弦轨迹,相位差按腿的顺序设置。Go2的步态通常是trot,对角腿同相,所以FL和RR同相,FR和RL同相,相位差π。
实现的时候,你可以写一个ROS2节点,发布
JointTrajectory
消息,频率设成50Hz。轨迹的幅值和频率需要调,我一开始用幅值0.3 rad、频率1.5Hz,狗走得很别扭,后来改成幅值0.5 rad、频率2.0Hz,就顺多了。这个和真机上的参数不完全一样,因为仿真里没有摩擦和阻尼的精确模型,需要重新调。
5. 传感器仿真与数据验证
5.1 IMU和足端力传感器的配置
Go2真机上有IMU和足端力传感器,仿真里也要加上,否则你的状态估计和力控算法没法验证。IMU用
gz-sim-imu-system
插件,配置里指定link名和更新频率。我一般把IMU放在机身link上,更新频率设成200Hz,和真机一致。
足端力传感器用
gz-sim-contact-system
,配置里指定足端link和碰撞体。注意,接触传感器的输出是接触力,不是关节力矩。如果你需要关节力矩,得用
gz-sim-joint-force-system
,或者从关节电流反推。我一般两个都加,方便对比。
5.2 传感器数据的验证方法
加完传感器后,怎么确认数据是对的?我的做法是:先让狗静止站立,看IMU的加速度输出是不是接近(0, 0, 9.8)。如果Z轴是-9.8,说明坐标系反了,需要在URDF里调整IMU的
<origin>
。然后让狗走几步,看足端力传感器的输出是不是有周期性的峰值,峰值大小和步态周期对不对得上。
还有一个验证方法是把仿真数据录成bag,然后用PlotJuggler画出来。我一般会看三个量:关节位置、关节速度、IMU的角速度。如果关节位置跟踪误差在0.05 rad以内,角速度没有明显跳变,那基本就稳了。
6. 常见问题与排查技巧实录
6.1 模型加载失败与白模问题
模型加载失败最常见的原因是mesh路径不对。Gazebo Sim的报错信息有时候很模糊,只说“找不到文件”,不说是哪个文件。我的排查方法是:先用
gz sim -v 4
启动,打开详细日志,看具体是哪个mesh加载失败。然后检查
GZ_SIM_RESOURCE_PATH
是否包含模型目录,以及URDF里的mesh路径是不是相对路径。
白模问题通常是材质或纹理丢失。如果你用的是DAE文件,检查纹理路径;如果用STL,白模是正常的,因为STL不带材质。你可以在URDF里给link加
<material>
标签,指定颜色,这样至少不是全白。
6.2 关节不动或抖动
关节不动的原因有几个:一是transmission没加,ros2_control找不到关节;二是控制器没加载,检查
ros2 control list_controllers
看控制器状态;三是关节限位设错了,比如把限位设成0,关节就动不了。
关节抖动通常是PID参数问题。先把D调大,如果还抖,降P。还有一个可能是仿真步长太大,Gazebo的默认步长是1ms,如果你调到10ms,关节控制会不稳定。我一般用1ms,虽然慢一点,但稳定。
6.3 仿真速度太慢的优化
Gazebo Sim的实时性取决于你的CPU和场景复杂度。如果仿真速度只有0.5倍实时,可以试试这几个优化:一是把碰撞体换成简单几何形状;二是降低传感器更新频率,比如IMU从200Hz降到100Hz;三是关闭不必要的光照和阴影,在
gz sim
的GUI里把渲染质量调到最低。
还有一个技巧是用
gz sim -s
只启动服务器,不启动GUI,然后用
gz topic
订阅数据。这样能省不少CPU,适合做批量仿真。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 模型显示为白色方块 | mesh路径错误或纹理丢失 |
用
gz sim -v 4
看日志
|
检查
GZ_SIM_RESOURCE_PATH
,改用STL或补材质
|
| 关节不动 | transmission缺失或控制器未加载 |
ros2 control list_controllers
| 补transmission,加载控制器 |
| 关节高频抖动 | PID参数不合适或步长太大 | 观察关节位置曲线 | 调大D,降P,步长设1ms |
| 狗站立时瘫倒 | 惯性矩阵错误或碰撞体太大 | 检查URDF惯性参数 | 重新计算惯性矩阵,简化碰撞体 |
| 仿真速度慢 | 场景太复杂或传感器频率太高 | 看CPU占用 | 简化碰撞体,降传感器频率,关GUI |
| IMU数据方向反了 | IMU的origin坐标系错误 | 静止时看加速度输出 | 调整URDF里IMU的origin |
| 足端力传感器无输出 | 接触插件未配置或碰撞体未定义 |
检查
gz-sim-contact-system
配置
| 补接触插件,确认足端有碰撞体 |
7. 从仿真到真机的迁移经验
仿真跑通了,不代表真机就能直接用。我踩过的最大的坑是:仿真里的关节摩擦和阻尼和真机差很多。仿真里狗能走,真机上可能因为摩擦不够而打滑,或者因为阻尼太大而发热。我的做法是:在仿真里把关节阻尼设成真机的一半,摩擦设成真机的80%,这样迁移的时候只需要微调。
还有一个是控制频率。仿真里可以用50Hz,真机上最好用500Hz以上,因为真机的传感器噪声更大,需要更高的控制频率来抑制。我一般会在仿真里验证算法逻辑,然后在真机上用更高的频率跑,参数再微调。
最后说一个细节:Go2的真机IMU有偏置,仿真里没有。如果你在仿真里调好了状态估计,真机上可能会漂。解决办法是在真机上先做一次静态校准,把偏置记下来,在算法里补偿。这个在仿真里没法验证,只能真机上调。
8. 我个人在实际操作中的体会
这套环境我前前后后搭了三遍,第一遍用Foxy加经典Gazebo,卡在插件加载上;第二遍用Humble加Gazebo Sim,卡在URDF转换上;第三遍才把整个流程跑通。最大的体会是:不要试图一次把所有东西都配好,先让模型能在Gazebo里显示出来,再加关节控制,再加传感器,一步一步来。每加一个东西就验证一下,出问题了容易定位。
另外,宇树的官方文档更新不算快,很多细节得自己试。社区里的教程质量参差不齐,有的直接抄官方URDF但没补惯性,跑起来狗就瘫。我的建议是:以官方URDF为基准,自己补全Gazebo需要的部分,不要直接用别人的模型文件,除非你确认它跑通过。
如果你也在搭Go2的仿真环境,遇到问题可以先检查三个地方:mesh路径、transmission、控制器配置。这三个地方对了,基本就能跑起来。剩下的就是调参数,那个需要耐心,没有捷径。
更多推荐


所有评论(0)