3. Gazebo仿真环境搭建:从零开始构建你的测试场
好,咱们进入实战环节。Gazebo仿真环境,说白了就是给机器人算法搭一个「虚拟实验室」。你总不能让机器人直接在真实厂房里乱撞吧?我刚开始做导航时,就吃过这个亏——代码没调好就上真机,结果机器人直接怼墙上了,激光雷达支架都撞歪了。从那以后,我养成了习惯:先在Gazebo里把逻辑跑通,再考虑真机部署。
这一章,咱们就手把手把Gazebo Fortress环境搭起来,包括安装验证、自定义世界、传感器配置。嗯,内容不少,但每一步都很关键。
3.1 Gazebo Fortress安装与验证
Gazebo Fortress是Ignition系列的最新版本,和ROS2 Humble配合得最好。我个人建议直接用二进制安装,省去编译的麻烦。
# 安装Gazebo Fortress
sudo apt-get update
sudo apt-get install ignition-fortress
# 验证安装
ign gazebo --version
如果看到版本号输出,说明安装成功了。你可以试着启动一个空世界看看:
ign gazebo empty.sdf
这时候应该会弹出一个3D窗口,里面啥也没有——别慌,这就是「空世界」。我遇到过有人以为装坏了,其实Gazebo默认就是空的,需要你自己往里加东西。
3.2 创建自定义仿真世界(world文件)
空世界没法用,咱们得自己建一个。Gazebo的世界文件是SDFormat格式,后缀是.sdf或.world。说白了就是一个XML文件,描述场景里有什么东西、长什么样、物理属性如何。
下面是我常用的一个测试世界——一个带围墙的矩形场地,适合做导航避障测试:
<?xml version="1.0" ?>
<sdf version="1.9">
<world name="nav_test_world">
<!-- 地面 -->
<include>
<uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground Plane</uri>
</include>
<!-- 太阳光 -->
<include>
<uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun</uri>
</include>
<!-- 物理引擎配置 -->
<physics name="1ms" type="ignored">
<max_step_size>0.001</max_step_size>
<real_time_factor>1.0</real_time_factor>
</physics>
</world>
</sdf>
保存为my_test_world.sdf,然后用这个命令启动:
ign gazebo my_test_world.sdf
你会看到一个带地面和光照的空白场地。嗯,这才像个样子。
3.3 添加静态障碍物与地面
光有地面不够,咱们得加几个障碍物,模拟真实场景中的柱子、墙壁、箱子。Gazebo里加模型有两种方式:
- Fuel模型库:在线下载现成的模型,比如箱子、圆柱、椅子
- 自定义模型:自己写SDF描述,定义形状、大小、颜色
我个人更喜欢自定义,因为可控性强。下面是一个带障碍物的世界文件示例:
<?xml version="1.0" ?>
<sdf version="1.9">
<world name="obstacle_world">
<include>
<uri>https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground Plane</uri>
</include>
<!-- 障碍物1:红色立方体 -->
<model name="box_obstacle">
<pose>2.0 1.0 0.5 0 0 0</pose>
<link name="body">
<visual name="visual">
<geometry>
<box>
<size>0.8 0.8 1.0</size>
</box>
</geometry>
<material>
<ambient>0.8 0.2 0.2 1.0</ambient>
</material>
</visual>
<collision name="collision">
<geometry>
<box>
<size>0.8 0.8 1.0</size>
</box>
</geometry>
</collision>
</link>
</model>
<!-- 障碍物2:灰色圆柱 -->
<model name="cylinder_obstacle">
<pose>-1.5 -1.0 0.5 0 0 0</pose>
<link name="body">
<visual name="visual">
<geometry>
<cylinder>
<radius>0.3</radius>
<length>1.0</length>
</cylinder>
</geometry>
<material>
<ambient>0.5 0.5 0.5 1.0</ambient>
</material>
</visual>
<collision name="collision">
<geometry>
<cylinder>
<radius>0.3</radius>
<length>1.0</length>
</cylinder>
</geometry>
</collision>
</link>
</model>
</world>
</sdf>
这里要注意:<pose>标签定义了模型的位置和朝向,格式是x y z roll pitch yaw。我习惯把障碍物放在地面以上0.5米处,这样看起来自然。
3.4 传感器插件配置(IMU、激光雷达、里程计)
环境搭好了,机器人也得有「眼睛」和「耳朵」。Gazebo里传感器是通过插件实现的,说白了就是一段C++代码,模拟真实传感器的行为。
下面是一个完整的机器人模型SDF,包含IMU、激光雷达和里程计插件:
<?xml version="1.0" ?>
<sdf version="1.9">
<model name="my_robot">
<link name="base_link">
<!-- 机器人本体 -->
<visual name="visual">
<geometry>
<box>
<size>0.4 0.3 0.2</size>
</box>
</geometry>
</visual>
</link>
<!-- IMU传感器 -->
<link name="imu_link">
<pose>0 0 0.1 0 0 0</pose>
<sensor name="imu_sensor" type="imu">
<always_on>1</always_on>
<update_rate>100</update_rate>
<visualize>false</visualize>
<topic>/imu/data</topic>
</sensor>
</link>
<!-- 激光雷达 -->
<link name="lidar_link">
<pose>0.2 0 0.15 0 0 0</pose>
<sensor name="lidar_sensor" type="lidar">
<always_on>1</always_on>
<update_rate>10</update_rate>
<visualize>true</visualize>
<topic>/scan</topic>
<lidar>
<scan_count>1</scan_count>
<scan_angle>
<min>-2.35619</min>
<max>2.35619</max>
</scan_angle>
<range>
<min>0.1</min>
<max>30.0</max>
</range>
</lidar>
</sensor>
</link>
<!-- 里程计插件 -->
<plugin name="odometry" filename="libignition-gazebo-odometry-system.so">
<body_name>base_link</body_name>
<topic>/odom</topic>
<update_rate>50</update_rate>
</plugin>
</model>
</sdf>
这里有几个关键参数,我解释一下:
| 传感器 | 关键参数 | 说明 |
|---|---|---|
| IMU | update_rate: 100Hz | 提供加速度和角速度,用于状态估计 |
| 激光雷达 | scan_angle: ±135° | 270度扫描范围,30米探测距离 |
| 里程计 | update_rate: 50Hz | 基于轮子编码器推算位置 |
3.5 本章知识体系
下面这张图,帮你理清Gazebo仿真环境的整体结构:
从这张图可以看得很清楚:世界文件是总纲,它把环境、机器人和传感器组合在一起。环境提供物理约束,机器人提供运动载体,传感器提供数据输出——这三者缺一不可。
嗯,到这里Gazebo仿真环境就搭好了。你可以在自定义世界里放几个障碍物,挂上传感器,然后启动看看效果。如果一切正常,你应该能在终端看到/scan、/imu/data、/odom这些话题在发布数据。
ign topic -l列出所有话题,或者用ign topic -e -t /scan查看激光雷达数据。如果能看到数据在刷新,说明传感器插件配置成功了。