第4章 · Gazebo仿真器从入门到上手
Gazebo 这东西,说白了就是 SLAM 工程师的「虚拟试车场」。你想想看,真机跑一次 SLAM 成本多高?撞坏了雷达、电机烧了,都是真金白银。我刚开始做 SLAM 那会儿,有一次在实验室用差速小车跑图,结果轮子卡在地缝里,里程计直接飞了…… 后来我学乖了,先在 Gazebo 里把算法调稳了再上真车。这一章,我就带你彻底搞懂 Gazebo 世界文件、模型文件,还有传感器插件怎么配。
4.1 世界文件 .world —— 仿真舞台的剧本
每个 Gazebo 仿真场景都从一个 .world 文件开始。它定义了「世界」长什么样:重力、光照、地面纹理、物理参数,还有你要加载哪些模型。我个人习惯把 .world 看作舞台的灯光师和场务——它不演戏,但没它不行。
一个典型 world 文件的结构骨架:
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="my_slam_world">
<physics type="ode">
<real_time_update_rate>1000</real_time_update_rate>
<max_step_size>0.001</max_step_size>
</physics>
<scene>
<ambient>0.4 0.4 0.4 1</ambient>
<sky>0.7 0.8 0.9</sky>
</scene>
<include>
<uri>model://ground_plane</uri>
</include>
<include>
<uri>model://my_robot</uri>
<pose>0 0 0.2 0 0 0</pose>
</include>
</world>
</sdf>
这里我特别想提一下 <physics> 标签。做 SLAM 仿真时,max_step_size 我一般设 0.001~0.002,太大容易导致碰撞穿透,太小又跑不动。嗯,这个值你可以在后期调,但一开始别超过 0.005。
4.2 模型文件解析 —— .sdf 与 .urdf 到底用哪个?
很多新手会纠结:到底用 SDF 还是 URDF?我直接说结论:URDF 是 ROS 原生格式,适合描述机器人运动学;SDF 是 Gazebo 的亲儿子,支持更多物理特性。 我在项目中通常用 URDF 描述机器人本体,然后通过 gazebo_ros_control 插件转成 SDF 加载。但如果你只做纯 Gazebo 仿真,直接用 SDF 更省事。
我的个人经验: 如果你要加传感器插件(比如 IMU、激光雷达),SDF 的标签更直观。URDF 里也能加 <gazebo> 扩展标签,但写起来啰嗦。下面给一个 SDF 模型的核心片段:
<model name="diff_drive_robot">
<link name="base_link">
<inertial>
<mass>2.0</mass>
<inertia>
<ixx>0.02</ixx><iyy>0.02</iyy><izz>0.03</izz>
</inertia>
</inertial>
<collision name="base_collision">
<geometry><box><size>0.4 0.3 0.2</size></box></geometry>
</collision>
<visual name="base_visual">
<geometry><box><size>0.4 0.3 0.2</size></box></geometry>
<material><ambient>0.8 0.2 0.2 1</ambient></material>
</visual>
</link>
<joint name="left_wheel_joint" type="revolute">
<parent>base_link</parent>
<child>left_wheel</child>
<axis><xyz>0 1 0</xyz></axis>
</joint>
</model>
注意 <inertia> 的值——我曾经偷懒随便填,结果仿真里机器人原地打转,查了半天才发现惯性矩给小了。你想想看,物理引擎是根据惯性算运动的,填错了整个动力学都崩。
4.3 传感器插件配置 —— 给机器人装上「感官」
Gazebo 的传感器通过插件实现。说白了,插件就是一段 C++ 代码,编译成 .so 库,然后在模型文件里声明一下就能用。我最常用的三个传感器:IMU、激光雷达、相机。下面逐个说。
4.3.1 IMU 插件
IMU 提供角速度和线加速度。Gazebo 自带的 libgazebo_ros_imu_sensor.so 很好用。配置时注意设置噪声参数,否则仿真数据太干净,算法在真机上会炸。
<plugin name="imu_plugin" filename="libgazebo_ros_imu_sensor.so">
<ros>
<namespace>/robot</namespace>
<argument>imu/data</argument>
</ros>
<update_rate>200</update_rate>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.001</stddev>
</noise>
</plugin>
⚠️ 我曾经踩过的坑: 忘记给 IMU 加 <frame_name> 标签,结果发布的坐标系是空的,导致 EKF 融合直接崩溃。记得加上 <frame_name>imu_link</frame_name>。
4.3.2 激光雷达 (Lidar) 插件
激光雷达是 SLAM 的主力传感器。Gazebo 的 libgazebo_ros_ray_sensor.so 可以模拟单线或多线雷达。我一般设置 360° 扫描,720 个采样点,更新率 10Hz。
<plugin name="lidar_plugin" filename="libgazebo_ros_ray_sensor.so">
<ros>
<namespace>/robot</namespace>
<argument>scan</argument>
</ros>
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>720</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
</scan>
<range>
<min>0.1</min>
<max>30.0</max>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
</plugin>
这里 <resolution> 我习惯设 1,表示每个采样点只发一条射线。如果你想要更真实的模拟,可以设成 2 或 3,但计算量会翻倍。
4.3.3 相机插件
视觉 SLAM 离不开相机。Gazebo 的相机插件基于 libgazebo_ros_camera.so。配置分辨率、帧率、畸变参数即可。
<plugin name="camera_plugin" filename="libgazebo_ros_camera.so">
<ros>
<namespace>/robot</namespace>
<argument>camera/image_raw</argument>
</ros>
<update_rate>30</update_rate>
<camera>
<horizontal_fov>1.047</horizontal_fov>
<image>
<width>640</width>
<height>480</height>
<format>R8G8B8</format>
</image>
<clip>
<near>0.1</near>
<far>100</far>
</clip>
</camera>
</plugin>
对了,如果你做 VIO 或 ORB-SLAM,记得把 <distortion> 加上,否则仿真图像没有畸变,算法迁移到真机时精度会掉一截。
4.4 传感器插件对比速查
| 传感器 | 插件库 | 关键参数 | 常见坑点 |
|---|---|---|---|
| IMU | libgazebo_ros_imu_sensor.so | update_rate, noise | 缺少 frame_name |
| 激光雷达 | libgazebo_ros_ray_sensor.so | samples, range, noise | 分辨率过高导致卡顿 |
| 相机 | libgazebo_ros_camera.so | width, height, fov | 未设置畸变参数 |
小结一下: 世界文件搭舞台,模型文件定角色,传感器插件给角色装上感官。这三样东西配齐了,你的仿真 SLAM 系统就能跑起来了。