4、Gazebo仿真世界构建:创建简单障碍物世界、创建室内走廊世界、创建复杂工厂世界

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

上一章我们把Nav2的框架搭起来了,也跑通了第一个仿真。但说实话,那个平坦的空旷世界,连个柱子都没有,根本没法验证导航算法的鲁棒性。你想想看,一个机器人如果只能在空地上跑,那跟玩具车有什么区别?

所以这一章,我们亲手来造几个像样的仿真世界。我会带你从最简单的障碍物世界开始,逐步升级到室内走廊,最后挑战一个复杂的工厂环境。这三个世界,基本覆盖了90%的导航测试场景。

本章核心目标:掌握Gazebo世界文件的编写逻辑,学会用模型拼接的方式快速构建复杂环境。

4.1 简单障碍物世界:从零开始搭一个测试场

我个人习惯,每次测试新算法前,先跑一个「障碍物世界」。这个世界不需要大,但要有几个关键元素:墙壁、柱子和一些随机摆放的箱子。

我们先看一个最简单的.world文件长什么样:

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

    <!-- 一个静态的墙壁 -->
    <model name="wall_01">
      <static>true</static>
      <pose>2.0 0.0 0.5 0 0 0</pose>
      <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>
    </model>

    <!-- 一个圆柱体柱子 -->
    <model name="pillar_01">
      <static>true</static>
      <pose>-1.5 -1.5 0.5 0 0 0</pose>
      <link name="link">
        <collision name="collision">
          <geometry>
            <cylinder>
              <radius>0.3</radius>
              <length>1.0</length>
            </cylinder>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <cylinder>
              <radius>0.3</radius>
              <length>1.0</length>
            </cylinder>
          </geometry>
        </visual>
      </link>
    </model>
  </world>
</sdf>

这段代码干了什么呢?说白了就是在地面上放了一面墙和一个柱子。注意看<static>true</static>,这个标记告诉Gazebo:这玩意儿是固定的,别被机器人撞飞了。我在项目中遇到过有人忘了加这个,结果机器人一启动,墙壁满天飞……那场面,挺滑稽的。

小技巧:调试阶段,把墙壁颜色设成红色(ambient 0.8 0.2 0.2),这样一眼就能看清障碍物分布。正式用的时候再改成灰色。

4.2 室内走廊世界:模拟真实办公环境

走廊环境是室内导航的经典场景。窄、长、有拐角,这对机器人的路径规划和定位都是考验。

我建议用「模块化」的思路来构建走廊。什么意思呢?就是先定义一段标准走廊,然后通过旋转和拼接,组成L型、T型甚至回字形结构。

下面是一个L型走廊的核心片段:

<model name="corridor_segment_01">
  <static>true</static>
  <pose>0 0 0 0 0 0</pose>
  <link name="wall_left">
    <pose>-1.5 0 0.5 0 0 0</pose>
    <collision name="collision">
      <geometry><box><size>0.1 10.0 1.0</size></box></geometry>
    </collision>
    <visual name="visual">
      <geometry><box><size>0.1 10.0 1.0</size></box></geometry>
    </visual>
  </link>
  <link name="wall_right">
    <pose>1.5 0 0.5 0 0 0</pose>
    <collision name="collision">
      <geometry><box><size>0.1 10.0 1.0</size></box></geometry>
    </collision>
    <visual name="visual">
      <geometry><box><size>0.1 10.0 1.0</size></box></geometry>
    </visual>
  </link>
</model>

你看,一个走廊段就是左右两面墙,中间留出3米宽的通道。然后我在拐角处再加一段垂直的走廊,用<pose>调整位置和朝向就行。

嗯,这里要注意:走廊的宽度一定要大于机器人的直径。我的机器人是0.5米宽,走廊留3米,绰绰有余。但如果你做的是叉车AGV,那走廊至少得留2米以上。

避坑指南:我曾经在走廊拐角处忘记加墙壁封口,结果机器人直接从「墙壁」穿过去了。后来我养成了一个习惯:所有拐角处,必须用一个小方块把缺口堵上。别偷懒,否则定位会飘。

4.3 复杂工厂世界:多楼层与动态障碍物

工厂环境,才是真正考验Nav2功底的地方。这里有货架、传送带、叉车,甚至还有移动的人。

我们先看一个工厂世界的整体结构图:

复杂工厂世界结构图 原料区 货架 x 6 通道宽度 2.5m 加工区 传送带 x 2 工作站 x 4 成品区 托盘区 动态障碍物区 AGV小车 x 3(随机路径) 人员通道 模拟行人 x 2(来回走动) R 机器人起点 导航路径

构建工厂世界时,我推荐用model://引用现成的模型库,而不是自己手写几何体。Gazebo官方模型库里有货架、传送带、叉车等模型,直接拿来用就行。

<!-- 引用货架模型 -->
<include>
  <uri>model://shelf_unit</uri>
  <name>shelf_01</name>
  <pose>2.0 3.0 0 0 0 0</pose>
</include>

<!-- 引用传送带模型 -->
<include>
  <uri>model://conveyor_belt</uri>
  <name>belt_01</name>
  <pose>5.0 2.0 0 0 0 0</pose>
</include>

动态障碍物怎么做?我一般用libActorPlugin.so插件,或者直接写一个简单的Python脚本,让模型沿着预设路径来回移动。下面是一个用插件实现移动障碍物的例子:

<model name="moving_obstacle_01">
  <static>false</static>
  <pose>0 0 0.5 0 0 0</pose>
  <link name="body">
    <collision name="collision">
      <geometry><box><size>0.5 0.5 1.0</size></box></geometry>
    </collision>
    <visual name="visual">
      <geometry><box><size>0.5 0.5 1.0</size></box></geometry>
    </visual>
  </link>
  <plugin name="move_plugin" filename="libActorPlugin.so">
    <path>0 0 0; 5 0 0; 5 5 0; 0 5 0</path>
    <speed>0.5</speed>
  </plugin>
</model>

这个插件会让障碍物沿着四个点循环移动,速度0.5米/秒。嗯,速度别设太快,否则Nav2的全局规划器可能来不及重规划。

我的经验:测试动态障碍物时,先把速度调到0.2米/秒,确认导航能稳定避障后,再逐步加快。直接上高速,机器人容易「懵圈」。

4.4 世界文件的组织与管理

最后聊一下文件管理。一个复杂的工厂世界,可能有几十个模型。我习惯把模型文件放在~/.gazebo/models/目录下,然后.world文件里只写<include>引用。这样世界文件本身很干净,也方便复用。

文件/目录 说明
~/.gazebo/models/ 存放自定义模型,每个模型一个文件夹
worlds/simple.world 简单障碍物世界
worlds/corridor.world 室内走廊世界
worlds/factory.world 复杂工厂世界

启动时,用gazebo worlds/factory.world即可。如果你用launch文件,可以这样写:

<node name="gazebo" pkg="gazebo_ros" exec="gazebo" args="$(find-pkg-share my_nav2_bringup)/worlds/factory.world" output="screen"/>

好了,这一章的内容就到这里。三个世界,三种难度,足够你测试Nav2的各种功能了。记住,仿真世界越真实,算法在真机上跑起来就越稳。别嫌麻烦,多花点时间把世界搭好,后面调试会省很多事。