3、Gazebo仿真环境配置:创建自己的仿真世界

说实话,很多初学者一上来就急着让机械臂动起来,结果发现仿真环境乱七八糟——要么地面是透明的,要么光照暗得看不清,要么模型卡在半空中。我当年也踩过这个坑,后来才明白:一个靠谱的仿真世界,是后面所有抓取实验的基础。

这一节,我们就来亲手搭建一个属于自己的仿真世界。你会学到:

  • world文件的完整结构
  • 如何添加地面、光照、障碍物
  • 用SDFormat描述自定义模型
核心思想:Gazebo里的世界,本质上就是一个SDF文件。你写的每一行代码,都在告诉仿真引擎「这里有什么、长什么样、怎么动」。

3.1 world文件长什么样?

先看一个最简单的world文件。我习惯把它放在 worlds/ 目录下,命名规则是 my_robot.world。

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="my_world">
    <include>
      <uri>model://sun</uri>
    </include>
    <include>
      <uri>model://ground_plane</uri>
    </include>
  </world>
</sdf>

嗯,就这么几行。但别小看它——<world> 标签里可以塞进物理参数、重力、磁力、甚至风。我个人建议,刚开始不要贪多,先把基础结构跑通。

3.2 添加地面和光照

地面是必须的,不然你的机械臂会掉进虚空里。Gazebo自带的 ground_plane 模型够用,但如果你想自定义地面纹理,可以这样写:

<model name="ground">
  <static>true</static>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>20 20 0.1</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>20 20 0.1</size>
        </box>
      </geometry>
      <material>
        <ambient>0.8 0.8 0.8 1</ambient>
        <diffuse>0.8 0.8 0.8 1</diffuse>
      </material>
    </visual>
  </link>
</model>

光照呢?Gazebo默认有一个太阳光,但你可以调整它的角度和颜色。我在项目中遇到过一个问题:光照太强导致相机仿真图像过曝。后来我把 <intensity> 从1.0降到了0.6,问题就解决了。

<light name="sun" type="directional">
  <pose>0 0 10 0 0 0</pose>
  <diffuse>0.8 0.8 0.8 1</diffuse>
  <specular>0.2 0.2 0.2 1</specular>
  <direction>-0.5 0.1 -1</direction>
  <attenuation>
    <range>100</range>
    <constant>0.9</constant>
    <linear>0.01</linear>
    <quadratic>0.001</quadratic>
  </attenuation>
  <cast_shadows>true</cast_shadows>
</light>
小技巧:如果你想要室内效果,可以加一个点光源(type="point")放在机械臂上方。我经常在抓取工位上方加一个点光源,这样视觉识别更稳定。

3.3 添加障碍物

障碍物是让仿真变得真实的关键。你想想看,真实场景里哪有空荡荡的桌面?总得有几个箱子、柱子、或者别的机器人吧。

下面是一个简单的箱子障碍物:

<model name="obstacle_box">
  <static>true</static>
  <pose>1.5 0.5 0.05 0 0 0</pose>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>0.4 0.4 0.4</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>0.4 0.4 0.4</size>
        </box>
      </geometry>
      <material>
        <ambient>1 0 0 1</ambient>
        <diffuse>1 0 0 1</diffuse>
      </material>
    </visual>
  </link>
</model>

注意 <static>true</static>——这意味着这个箱子不会被撞飞。如果你想让障碍物可以被推动,改成 false 就行。我曾经犯过一个错误:把障碍物设成非静态,结果机械臂一碰,箱子满世界乱滚,仿真直接崩了。

3.4 使用SDFormat描述模型

SDFormat是Gazebo的亲儿子格式。它比URDF更强大,支持物理属性、传感器、甚至插件。说白了,URDF是给ROS看的,SDF是给Gazebo看的。

一个完整的SDF模型包含:

  • link:刚体,包含碰撞、视觉、惯性
  • joint:连接两个link,定义运动副
  • model:把link和joint打包成一个整体

下面是一个自定义桌子的SDF模型:

<?xml version="1.0" ?>
<sdf version="1.6">
  <model name="table">
    <static>true</static>
    <pose>0 0 0.4 0 0 0</pose>
    <link name="table_top">
      <pose>0 0 0.02 0 0 0</pose>
      <collision name="collision">
        <geometry>
          <box>
            <size>1.2 0.8 0.04</size>
          </box>
        </geometry>
      </collision>
      <visual name="visual">
        <geometry>
          <box>
            <size>1.2 0.8 0.04</size>
          </box>
        </geometry>
        <material>
          <ambient>0.5 0.3 0.1 1</ambient>
          <diffuse>0.5 0.3 0.1 1</diffuse>
        </material>
      </visual>
    </link>
    <link name="leg_1">
      <pose>-0.5 -0.3 -0.38 0 0 0</pose>
      <collision name="collision">
        <geometry>
          <box>
            <size>0.04 0.04 0.76</size>
          </box>
        </geometry>
      </collision>
      <visual name="visual">
        <geometry>
          <box>
            <size>0.04 0.04 0.76</size>
          </box>
        </geometry>
      </visual>
    </link>
    <!-- 其他三条腿类似,省略 -->
  </model>
</sdf>
注意:SDF中的pose是相对于父级坐标系的。写错pose会导致模型「飘」在空中或者穿模。我建议每次写完都用 gz sim 预览一下,别等到最后才发现。

3.5 知识体系一览

下面这张图帮你理清本章的核心逻辑:

Gazebo仿真世界 world文件结构 环境元素配置 SDFormat模型 命名与版本 包含模型 地面 光照 障碍物 link joint 核心:SDFormat是Gazebo的原生语言,掌握它才能自由定制仿真环境 1

3.6 完整world文件示例

把上面所有元素拼起来,就是一个完整的仿真世界:

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="grasping_world">
    <!-- 光照 -->
    <include>
      <uri>model://sun</uri>
    </include>
    
    <!-- 自定义地面 -->
    <model name="ground">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry>
            <box>
              <size>20 20 0.1</size>
            </box>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <box>
              <size>20 20 0.1</size>
            </box>
          </geometry>
          <material>
            <ambient>0.9 0.9 0.9 1</ambient>
            <diffuse>0.9 0.9 0.9 1</diffuse>
          </material>
        </visual>
      </link>
    </model>
    
    <!-- 桌子 -->
    <model name="table">
      <static>true</static>
      <pose>0 0 0.4 0 0 0</pose>
      <link name="table_top">
        <pose>0 0 0.02 0 0 0</pose>
        <collision name="collision">
          <geometry>
            <box>
              <size>1.2 0.8 0.04</size>
            </box>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <box>
              <size>1.2 0.8 0.04</size>
            </box>
          </geometry>
          <material>
            <ambient>0.5 0.3 0.1 1</ambient>
            <diffuse>0.5 0.3 0.1 1</diffuse>
          </material>
        </visual>
      </link>
      <!-- 桌腿省略 -->
    </model>
    
    <!-- 障碍物箱子 -->
    <model name="obstacle">
      <static>true</static>
      <pose>0.6 0.3 0.42 0 0 0</pose>
      <link name="link">
        <collision name="collision">
          <geometry>
            <box>
              <size>0.2 0.2 0.2</size>
            </box>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <box>
              <size>0.2 0.2 0.2</size>
            </box>
          </geometry>
          <material>
            <ambient>0 0 1 1</ambient>
            <diffuse>0 0 1 1</diffuse>
          </material>
        </visual>
      </link>
    </model>
    
    <!-- 物理参数 -->
    <physics type="ode">
      <real_time_update_rate>1000</real_time_update_rate>
      <max_step_size>0.001</max_step_size>
    </physics>
  </world>
</sdf>
启动命令:保存为 my_world.world 后,用 gazebo --verbose my_world.world 启动。如果一切正常,你会看到一个带桌子、箱子和光照的仿真环境。

嗯,到这里你已经掌握了创建仿真世界的核心技能。别小看这些基础配置——我见过不少项目,最后出问题都是因为world文件里某个参数写错了。多花点时间把环境搭扎实,后面做运动规划、抓取仿真的时候,你会感谢自己的。

可可精品资料库