第3章:Gazebo 仿真环境搭建
好,咱们进入正题。Gazebo 这个工具,说白了就是机器人界的「物理沙盒」。你写好的算法,不敢直接往真机上跑吧?那就先在 Gazebo 里摔打摔打。我当年第一次用 Gazebo 时,差点被它的依赖库搞崩溃——嗯,今天我就把这些坑给你填平。
3.1 安装与配置:别让环境卡住你
我个人习惯用 Gazebo 11(搭配 ROS Noetic)。为什么?稳定。Gazebo 9 太老,Gazebo 13 又太新,社区支持还没跟上。
核心安装命令(Ubuntu 20.04 + ROS Noetic):
# 1. 安装 Gazebo 11
curl -sSL http://get.gazebosim.org | sh
# 2. 安装 ROS 集成包
sudo apt install ros-noetic-gazebo-ros-pkgs ros-noetic-gazebo-ros-control
# 3. 验证安装
gazebo --version # 应该显示 Gazebo 11.x.x
我曾经踩过的坑: 直接 sudo apt install gazebo11 会装到一半卡住。因为 Gazebo 的官方源在国内访问很慢。建议用 curl 脚本安装,它会自动选择最快的镜像。
装完后,跑一下 gazebo 命令。如果看到一只小海龟在空荡荡的世界里,恭喜你,环境通了。如果报错 libGL error,别慌——这是显卡驱动问题。加个环境变量就好:
export LIBGL_ALWAYS_SOFTWARE=1
gazebo
你想想看,一个仿真器连图形界面都打不开,后面还怎么玩?所以这一步必须稳。
3.2 理解模型文件:SDF vs URDF
这是很多新手懵圈的地方。Gazebo 里到底用 SDF 还是 URDF?我直接给你结论:
| 特性 | URDF | SDF |
|---|---|---|
| 起源 | ROS 原生格式 | Gazebo 原生格式 |
| 支持多机器人 | ❌ 只能描述单个机器人 | ✅ 支持世界、多机器人 |
| 物理属性 | 需要额外扩展 | 内置完整物理参数 |
| 闭链机构 | ❌ 不支持 | ✅ 支持 |
我的建议: 如果你只是做单个机器人的运动学仿真,用 URDF 就够了。但如果你要搞多机器人协同、复杂环境交互——老老实实用 SDF。我在项目中遇到过用 URDF 描述四足机器人,结果因为闭链问题折腾了两天,换成 SDF 半小时搞定。
3.2.1 URDF 快速示例
一个最简单的两轮机器人 URDF:
<robot name="simple_robot">
<link name="base_link">
<visual>
<geometry><box size="0.4 0.3 0.1"/></geometry>
</visual>
<collision>
<geometry><box size="0.4 0.3 0.1"/></geometry>
</collision>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 -0.2 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</robot>
注意看,URDF 里 <visual> 和 <collision> 是分开写的。说白了,视觉模型可以很花哨,但碰撞模型一定要简单——这能大幅提升仿真速度。
3.2.2 SDF 的优势
SDF 文件可以包含整个世界的信息。比如地面、光照、甚至重力参数:
<sdf version="1.6">
<world name="my_world">
<include>
<uri>model://ground_plane</uri>
</include>
<include>
<uri>model://sun</uri>
</include>
<gravity>0 0 -9.81</gravity>
</world>
</sdf>
小技巧: 用 gz sdf -p your_model.sdf 可以打印出 SDF 文件的完整结构。调试时特别好用。
3.3 创建简单世界:从空白到有料
咱们来搭一个测试用的世界。别搞太复杂,就一个地面、几堵墙、几个障碍物。
我个人习惯用 Building Editor 来画墙,但命令行方式更可控。这里给一个纯代码的世界文件:
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="test_world">
<include>
<uri>model://sun</uri>
</include>
<include>
<uri>model://ground_plane</uri>
</include>
<!-- 一堵墙 -->
<model name="wall">
<static>true</static>
<link name="body">
<pose>2 0 0.5 0 0 0</pose>
<collision name="collision">
<geometry><box size="0.2 2 1"/></geometry>
</collision>
<visual name="visual">
<geometry><box size="0.2 2 1"/></geometry>
<material><ambient>0.8 0.2 0.2 1</ambient></material>
</visual>
</link>
</model>
<!-- 一个障碍物 -->
<model name="obstacle">
<static>true</static>
<link name="body">
<pose>-1 -1 0.25 0 0 0</pose>
<collision name="collision">
<geometry><cylinder radius="0.3" length="0.5"/></geometry>
</collision>
<visual name="visual">
<geometry><cylinder radius="0.3" length="0.5"/></geometry>
<material><ambient>0.2 0.8 0.2 1</ambient></material>
</visual>
</link>
</model>
</world>
</sdf>
保存为 test.world,然后运行:
gazebo test.world
你会看到一堵红墙和一个绿柱子。嗯,这就是你的第一个仿真世界。
3.4 创建机器人模型:让轮子转起来
光有世界不够,得有个能动的机器人。咱们做一个差速驱动小车,两个主动轮、一个万向轮。
这里我直接给一个完整的 URDF 文件,包含传动和控制器配置:
<robot name="diff_drive_robot">
<link name="base_footprint">
<visual><geometry><box size="0.001 0.001 0.001"/></geometry></visual>
</link>
<joint name="base_joint" type="fixed">
<parent link="base_footprint"/>
<child link="base_link"/>
<origin xyz="0 0 0.05"/>
</joint>
<link name="base_link">
<visual>
<geometry><box size="0.4 0.3 0.1"/></geometry>
<material><color rgba="0.2 0.2 0.8 1"/></material>
</visual>
<collision>
<geometry><box size="0.4 0.3 0.1"/></geometry>
</collision>
<inertial>
<mass value="2.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.02"/>
</inertial>
</link>
<!-- 左轮 -->
<link name="left_wheel">
<visual>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
<material><color rgba="0.8 0.2 0.2 1"/></material>
</visual>
<collision>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0002"/>
</inertial>
</link>
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 -0.2 0" rpy="-1.57 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<!-- 右轮(对称) -->
<link name="right_wheel">
<visual>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
<material><color rgba="0.2 0.8 0.2 1"/></material>
</visual>
<collision>
<geometry><cylinder radius="0.05" length="0.04"/></geometry>
</collision>
<inertial>
<mass value="0.2"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0002"/>
</inertial>
</link>
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="0 0.2 0" rpy="-1.57 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<!-- 万向轮 -->
<link name="caster_wheel">
<visual>
<geometry><sphere radius="0.03"/></geometry>
<material><color rgba="0.2 0.2 0.2 1"/></material>
</visual>
</link>
<joint name="caster_joint" type="fixed">
<parent link="base_link"/>
<child link="caster_wheel"/>
<origin xyz="-0.15 0 -0.05"/>
</joint>
</robot>
注意: 惯性参数 <inertia> 不能随便填。我曾经偷懒全填 0.001,结果机器人一启动就原地爆炸——物理引擎算出来惯性矩阵奇异,直接崩了。至少保证 ixx + iyy > izz 这个条件。
3.5 知识体系总览
下面这张图,是我梳理的本章知识脉络。你照着这个顺序学,不会乱:
3.6 避坑指南与个人经验
最后,分享几个我这些年攒下来的经验:
- 模型坐标系: URDF 里
base_link的坐标系一定要和 Gazebo 世界坐标系对齐。我见过有人把机器人放在地下半米处,结果一启动就穿模。 - 物理引擎: Gazebo 默认用 ODE。如果你发现机器人抖动严重,试试换成 Bullet:
<physics type="bullet">。我在做四足机器人时,Bullet 的稳定性明显更好。 - 性能优化: 别把视觉模型做得太精细。一个 10 万面的 STL 文件,Gazebo 能卡成幻灯片。用
mesh时记得简化。 - 调试技巧: 启动 Gazebo 时加
--verbose参数,能看到详细的日志输出。很多奇怪的问题,看日志就能找到线索。
一个小彩蛋: Gazebo 里按住 Ctrl + Shift 再拖动模型,可以精确调整位置。这个快捷键我用了三年才发现,别走我的弯路。
好了,这一章的内容就到这里。环境搭好了,模型建好了,下一章咱们就可以让机器人在世界里跑起来了。