3、Gazebo世界构建:创建包含静态障碍物(墙壁、箱子)的仿真世界,学习.world文件格式

各位同学,欢迎来到第三章。

上一章我们把机器人模型搭好了,但光有机器人不行啊,你得给它一个舞台。这个舞台就是Gazebo里的仿真世界。说白了,就是告诉Gazebo:地面长什么样,墙在哪,箱子放哪。

我个人习惯,在写代码之前,先想清楚这个世界要长什么样。你想想看,如果连障碍物位置都没想好,后面调避障参数就是瞎调。

3.1 什么是.world文件?

.world文件,就是Gazebo世界的描述文件。它用SDF(Simulation Description Format)格式写成,本质上就是XML。

我刚开始接触Gazebo时,以为.world文件很复杂。后来发现,它其实就是几大块拼在一起:

  • 光照:太阳光、环境光,让世界亮起来
  • 地面:机器人跑的地方
  • 物理参数:重力、摩擦力这些
  • 模型:静态障碍物、动态物体

嗯,就这么简单。但简单归简单,坑可不少。我曾经在一个项目里,因为.world文件里少写了一个标签,导致机器人直接穿透地面掉下去。那叫一个尴尬。

3.2 从零开始:写一个最简单的.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>

保存成 empty_world.world,然后用 gazebo empty_world.world 启动。你会看到一个空荡荡的世界,只有地面和阳光。

小提示: 我建议你每次新建.world文件时,都从这个模板开始。先确认地面和光照正常,再往里加东西。这样出了问题,你至少知道不是基础环境的问题。

3.3 添加静态障碍物:墙壁

空世界没意思,我们得加墙。在Gazebo里,静态障碍物用 <model> 标签定义,并且要设置 <static>true</static>。

为什么是静态?因为墙壁不会动,你也不希望机器人一撞就把墙撞飞了吧?

<model name="wall_01">
  <static>true</static>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>0.2 3.0 1.0</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>0.2 3.0 1.0</size>
        </box>
      </geometry>
      <material>
        <ambient>0.8 0.2 0.2 1.0</ambient>
      </material>
    </visual>
  </link>
  <pose>1.5 0.0 0.5 0 0 0</pose>
</model>

这段代码创建了一面红色的墙,长3米、高1米、厚0.2米,放在(1.5, 0.0)的位置。

注意看,我同时定义了 <collision> 和 <visual>。这是Gazebo的一个关键点:碰撞模型和视觉模型可以不一样。比如你可以用一个简单的盒子做碰撞检测,但显示一个复杂的3D模型。不过对于墙壁,两者保持一致就行。

避坑指南: 我曾经犯过一个错误——只写了<visual>,忘了写<collision>。结果机器人直接穿墙而过,跟幽灵一样。记住:没有碰撞模型,物体就是空气。

3.4 添加箱子:更复杂的障碍物

箱子比墙壁小,但用法一样。我们加几个不同颜色的箱子,模拟室内场景。

<model name="box_01">
  <static>true</static>
  <link name="link">
    <collision name="collision">
      <geometry>
        <box>
          <size>0.5 0.5 0.5</size>
        </box>
      </geometry>
    </collision>
    <visual name="visual">
      <geometry>
        <box>
          <size>0.5 0.5 0.5</size>
        </box>
      </geometry>
      <material>
        <ambient>0.2 0.8 0.2 1.0</ambient>
      </material>
    </visual>
  </link>
  <pose>-1.0 1.5 0.25 0 0 0</pose>
</model>

这是一个绿色箱子,边长0.5米,放在(-1.0, 1.5)的位置。高度0.25是因为箱子中心在0.25米处,底部刚好贴地。

你可以复制粘贴,改改颜色和位置,多放几个箱子。我个人习惯用不同颜色区分障碍物的重要性——红色是必须避开的,绿色是次要的。

3.5 完整的世界布局

下面是一个完整的示例,包含两面墙和三个箱子。这个布局我经常用在避障测试中。

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="obstacle_world">
    
    <include>
      <uri>model://sun</uri>
    </include>
    
    <include>
      <uri>model://ground_plane</uri>
    </include>
    
    <!-- 墙壁1:左侧长墙 -->
    <model name="wall_left">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry><box><size>0.2 5.0 1.0</size></box></geometry>
        </collision>
        <visual name="visual">
          <geometry><box><size>0.2 5.0 1.0</size></box></geometry>
          <material><ambient>0.8 0.2 0.2 1.0</ambient></material>
        </visual>
      </link>
      <pose>-2.5 0.0 0.5 0 0 0</pose>
    </model>
    
    <!-- 墙壁2:前方短墙 -->
    <model name="wall_front">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry><box><size>3.0 0.2 1.0</size></box></geometry>
        </collision>
        <visual name="visual">
          <geometry><box><size>3.0 0.2 1.0</size></box></geometry>
          <material><ambient>0.8 0.2 0.2 1.0</ambient></material>
        </visual>
      </link>
      <pose>0.0 2.5 0.5 0 0 0</pose>
    </model>
    
    <!-- 箱子1 -->
    <model name="box_01">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry><box><size>0.6 0.6 0.6</size></box></geometry>
        </collision>
        <visual name="visual">
          <geometry><box><size>0.6 0.6 0.6</size></box></geometry>
          <material><ambient>0.2 0.8 0.2 1.0</ambient></material>
        </visual>
      </link>
      <pose>1.0 1.0 0.3 0 0 0</pose>
    </model>
    
    <!-- 箱子2 -->
    <model name="box_02">
      <static>true</static>
      <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>0.2 0.2 0.8 1.0</ambient></material>
        </visual>
      </link>
      <pose>-1.0 -1.5 0.2 0 0 0</pose>
    </model>
    
    <!-- 箱子3 -->
    <model name="box_03">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry><box><size>0.8 0.3 0.5</size></box></geometry>
        </collision>
        <visual name="visual">
          <geometry><box><size>0.8 0.3 0.5</size></box></geometry>
          <material><ambient>0.8 0.8 0.2 1.0</ambient></material>
        </visual>
      </link>
      <pose>1.5 -1.0 0.25 0 0 0.3</pose>
    </model>
    
  </world>
</sdf>

这个世界的布局大致如下:

物体 位置 (x, y) 尺寸 (长, 宽, 高) 颜色
左墙 (-2.5, 0.0) 0.2 x 5.0 x 1.0 红色
前墙 (0.0, 2.5) 3.0 x 0.2 x 1.0 红色
箱子1 (1.0, 1.0) 0.6 x 0.6 x 0.6 绿色
箱子2 (-1.0, -1.5) 0.4 x 0.4 x 0.4 蓝色
箱子3 (1.5, -1.0) 0.8 x 0.3 x 0.5 黄色

注意箱子3的朝向:pose 最后一项是0.3弧度,大约17度。这意味着箱子是斜着放的。为什么?因为实际场景中,障碍物不会总是整整齐齐的。斜着放更能考验避障算法的鲁棒性。

3.6 世界结构图

下面这张图展示了.world文件的核心结构,以及各个元素之间的关系。我建议你把它保存下来,写文件时对照着看。

.world 文件结构 <sdf> <world> 光照 (sun) 地面 (ground_plane) 模型 (model) static=true link (碰撞+视觉) pose (位置+朝向) 每个模型必须包含:碰撞模型、视觉模型、位置姿态

3.7 调试技巧:如何验证世界文件

写完了.world文件,怎么知道对不对?我一般用三步验证法:

  1. 语法检查:用 gazebo --verbose my_world.world 启动,看终端有没有报错。如果有红色错误信息,通常是XML标签没闭合。
  2. 视觉检查:启动后,用鼠标旋转视角,从各个方向看障碍物位置是否合理。我习惯从正上方俯视,确认布局和设计一致。
  3. 碰撞检查:手动拖一个机器人模型进世界,让它撞一下墙。如果穿模了,说明碰撞模型有问题。
核心要点: .world文件是Gazebo仿真的基础。静态障碍物的关键参数有三个:碰撞模型(决定物理交互)、视觉模型(决定显示效果)、位置姿态(决定空间布局)。三者缺一不可。

好了,这一章的内容就到这里。你现在应该能自己写一个包含墙壁和箱子的.world文件了。下一章我们会把机器人放进这个世界,开始真正的避障仿真。

可可精品资料库