3、Gazebo仿真基础:世界文件、模型文件、SDF格式、传感器仿真

各位同学,欢迎来到Gazebo仿真的核心环节。说实话,我刚开始接触Gazebo时,也被它那一堆文件格式搞得有点懵。但别担心,今天我们就把它拆开揉碎了讲清楚。

3.1 世界文件:仿真的“舞台”

世界文件,说白了就是告诉Gazebo:“嘿,咱们在哪儿仿真?环境长什么样?”它定义了光照、重力、地面、物理参数,还有你要加载的模型。

我个人习惯把世界文件命名为 my_world.world。它的本质是一个SDF文件,但专门用来描述整个仿真环境。

来看一个最简单的例子:

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="default">
    <include>
      <uri>model://sun</uri>
    </include>
    <include>
      <uri>model://ground_plane</uri>
    </include>
    <physics name="default_physics" type="ode">
      <real_time_update_rate>1000</real_time_update_rate>
      <max_step_size>0.001</max_step_size>
    </physics>
  </world>
</sdf>

这里我加载了太阳和地面。注意那个 physics 标签,它控制着仿真的物理精度。我曾经在项目里把 max_step_size 设成0.01,结果机器人跑起来像在月球上跳舞——太飘了。

小技巧: 如果你想让仿真跑得更快,可以适当调大 max_step_size,但别超过0.005,否则物理碰撞会变得不准确。

3.2 模型文件:你的“演员”

模型文件定义了机器人或物体的外观、碰撞体积、惯性参数等。Gazebo支持两种格式:老牌的URDF和更现代的SDF。

我个人更推荐SDF,因为它能表达URDF做不到的东西,比如关节摩擦、流体阻力。嗯,这里要注意:如果你从ROS1转过来,可能习惯了URDF,但ROS2时代,SDF才是亲儿子。

一个简单的盒子模型长这样:

<?xml version="1.0" ?>
<sdf version="1.6">
  <model name="my_box">
    <pose>0 0 0.5 0 0 0</pose>
    <link name="base_link">
      <collision name="collision">
        <geometry>
          <box>
            <size>1 1 1</size>
          </box>
        </geometry>
      </collision>
      <visual name="visual">
        <geometry>
          <box>
            <size>1 1 1</size>
          </box>
        </geometry>
        <material>
          <ambient>0.8 0.2 0.2 1</ambient>
        </material>
      </visual>
      <inertial>
        <mass>1.0</mass>
        <inertia>
          <ixx>0.166</ixx>
          <iyy>0.166</iyy>
          <izz>0.166</izz>
        </inertia>
      </inertial>
    </link>
  </model>
</sdf>

你想想看,这里分了三个部分:collision 负责物理碰撞,visual 负责你看到的模样,inertial 负责质量分布。三者缺一不可。

避坑指南: 我曾经忘记给模型加 inertial 标签,结果Gazebo直接报错崩溃。记住,每个link都必须有惯性参数,哪怕质量设成0.001都行。

3.3 SDF格式:Gazebo的“通用语言”

SDF(Simulation Description Format)是Gazebo的原生格式。它比URDF强大在哪?我列个表你就明白了:

特性 URDF SDF
支持多个机器人 ❌ 只能描述一个 ✅ 可以描述多个
传感器仿真 ❌ 不支持 ✅ 原生支持
物理参数 有限 丰富(摩擦、阻尼、流体)
世界环境 ❌ 不能定义 ✅ 可以定义

说白了,如果你要做复杂的仿真,SDF是唯一的选择。我刚开始用URDF做传感器仿真时,折腾了半天发现根本不行——因为URDF压根就没设计这个功能。

3.4 传感器仿真:让机器人“感知”世界

传感器仿真,是Gazebo最吸引人的地方。你可以给机器人装上激光雷达、摄像头、IMU、接触传感器等,然后看它如何与环境互动。

举个例子,给机器人加一个激光雷达:

<sensor name="laser" type="ray">
  <pose>0 0 0.1 0 0 0</pose>
  <ray>
    <scan>
      <horizontal>
        <samples>360</samples>
        <resolution>1</resolution>
        <min_angle>-1.57</min_angle>
        <max_angle>1.57</max_angle>
      </horizontal>
    </scan>
    <range>
      <min>0.1</min>
      <max>10.0</max>
      <resolution>0.01</resolution>
    </range>
  </ray>
  <plugin name="laser_plugin" filename="libgazebo_ros_ray_sensor.so"/>
</sensor>

这里 samples 是扫描点数,min_angle 和 max_angle 决定了扫描范围。我做过一个项目,把 samples 设成720,结果仿真卡成PPT——因为每个点都要做射线碰撞检测,计算量太大了。

核心经验: 传感器仿真不是越精细越好。要根据你的实际需求平衡精度和性能。比如做导航避障,360个点就足够了;做精细建图,可以提高到720。

摄像头传感器也很常用:

<sensor name="camera" type="camera">
  <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>
</sensor>

注意那个 horizontal_fov,单位是弧度。1.047弧度约等于60度,这是常见的摄像头视角。如果你想要广角效果,可以调到1.57(90度)甚至更大。

为什么会这样设置?因为Gazebo的摄像头模拟的是真实相机的针孔模型,视野越宽,边缘畸变越明显。我在做视觉SLAM仿真时,就吃过这个亏——仿真里跑得好好的,换到真实相机上就崩了,因为真实相机的畸变参数没配好。

3.5 知识体系总览

下面这张图,是我自己总结的Gazebo仿真知识结构,你一看就明白各个模块的关系了:

Gazebo仿真系统 世界文件 模型文件 SDF格式 传感器仿真 光照/重力/地面 碰撞/视觉/惯性 多机器人/物理 激光/摄像头/IMU

从这张图你能看到,世界文件、模型文件、SDF格式、传感器仿真这四块是相互关联的。世界文件引用模型,模型用SDF描述,传感器是模型的一部分。搞懂了这四者的关系,Gazebo仿真你就入门了。

我的建议: 初学者可以先从修改现成的世界文件和模型文件开始,不要一上来就自己写。Gazebo官方提供了很多示例,在 /usr/share/gazebo-11/worlds/ 目录下,你可以直接拿来改。

好了,这一章的内容就到这里。记住,仿真不是目的,验证算法才是。下一章我们会把这些知识串起来,做一个完整的机器人仿真案例。

可可精品资料库