4. 仿真环境搭建:Gazebo与ROS2安装、基础世界文件创建

好,咱们正式开始动手了。这一章说白了就是给你的机器人搭一个「虚拟训练场」。没有仿真环境,你写的算法就只能对着空气跑,或者直接怼到真车上——嗯,后者我干过,代价是一块激光雷达的镜片。

我个人习惯把仿真环境拆成三块:ROS2 负责通信和调度,Gazebo 负责物理和渲染,世界文件 负责描述场地里有什么。这三块装好了,后面的传感器集成才有地方放。

4.1 为什么选Gazebo + ROS2?

你可能听过别的仿真器,比如 Webots、CoppeliaSim、Unity 加插件。但 Gazebo 在机器人圈子里有个不可替代的优势:它和 ROS 是原生兄弟。消息类型、TF 树、插件接口,几乎不用额外适配。

特性 Gazebo Classic Gazebo Ignition (Fortress+) Webots
ROS2 集成度 高(有 ros_ign_bridge) 原生支持 需 ros2_webots_bridge
物理引擎 ODE / Bullet / DART DART / TPE ODE 定制版
传感器仿真 激光、相机、IMU、声纳 同上 + 更精确的噪声模型 相机、激光、雷达
社区资源 非常丰富 增长中 中等

我建议你直接上 Gazebo Ignition Fortress 或更高版本。为什么?因为 Gazebo Classic 已经停止大版本更新了,新功能都在 Ignition 里。我在一个项目里吃过亏——用 Classic 搭了三个月的仿真,结果发现它不支持多雷达的精确时间戳同步,最后全重写了。

4.2 安装ROS2(Humble为例)

ROS2 的安装其实很机械,但有几个坑。我列一个我验证过的步骤清单,你照着走就行。

  1. 设置语言环境
    sudo apt update
    sudo apt install locales
    sudo locale-gen en_US en_US.UTF-8
    sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
    export LANG=en_US.UTF-8
  2. 添加ROS2源
    sudo apt install software-properties-common
    sudo add-apt-repository universe
    sudo apt update && sudo apt install curl -y
    sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
  3. 安装桌面版(推荐)
    sudo apt update
    sudo apt install ros-humble-desktop

    这个包包含了 rviz2、tf2、常用消息类型。如果你硬盘吃紧,可以装 ros-humble-ros-base,但后面做传感器可视化时还得补装 rviz2,不如一步到位。

  4. 配置环境变量
    echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
    source ~/.bashrc
    小技巧: 我习惯在 .bashrc 里加一行 export ROS_DOMAIN_ID=42。默认是 0,如果你在实验室里同时跑多台机器,容易串消息。设一个不同的 ID 能避免很多诡异问题。
  5. 验证安装
    ros2 run demo_nodes_cpp talker
    # 另开终端
    ros2 run demo_nodes_py listener

    看到 "I heard" 就说明 ROS2 活了。

4.3 安装Gazebo Ignition Fortress

ROS2 装好了,接下来是仿真核心。Gazebo Ignition 的安装比 Classic 简单,但要注意版本匹配。

sudo apt-get update
sudo apt-get install wget
sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
wget http://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add -
sudo apt-get update
sudo apt-get install ignition-fortress
注意: 不要同时装 Gazebo Classic 和 Ignition,它们会抢端口。如果你之前装过 Classic,先 sudo apt remove gazebo* 清理干净。

装完后跑一下:

ign gazebo --version

如果输出 Ignition Fortress X.X.X,就对了。我第一次装的时候忘了加 --version,直接敲 ign gazebo,结果弹出一个空窗口,我还以为死机了——其实它在等世界文件。

4.4 安装ros_ign_bridge

ROS2 和 Gazebo 之间需要一座桥。这座桥负责把 Gazebo 里的传感器数据转成 ROS2 话题。

sudo apt install ros-humble-ros-ign-bridge

装完后你可以测试一下:

# 终端1:启动 Gazebo 空世界
ign gazebo empty.sdf

# 终端2:启动桥接
ros2 run ros_ign_bridge parameter_bridge /lidar@sensor_msgs/msg/LaserScan@ignition.msgs.LaserScan

这个命令的意思是:把 Gazebo 里的 /lidar 话题(Ignition 格式)转成 ROS2 的 LaserScan 消息。后面我们集成激光雷达时,这个命令会反复用到。

4.5 创建你的第一个世界文件

世界文件就是仿真场地的「蓝图」。它用 SDF(Simulation Description Format)描述地面、光照、物体、传感器等。我带你写一个最简版本,包含地面、一堵墙和一个箱子。

<?xml version="1.0" ?>
<sdf version="1.8">
  <world name="terrain_lab">
    <!-- 光照 -->
    <include>
      <uri>https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Sun</uri>
    </include>

    <!-- 地面 -->
    <include>
      <uri>https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Ground_Plane</uri>
    </include>

    <!-- 一堵墙 -->
    <model name="wall">
      <pose>2 0 0.5 0 0 0</pose>
      <link name="body">
        <collision name="collision">
          <geometry>
            <box>
              <size>0.2 2.0 1.0</size>
            </box>
          </geometry>
        </collision>
        <visual name="visual">
          <geometry>
            <box>
              <size>0.2 2.0 1.0</size>
            </box>
          </geometry>
          <material>
            <ambient>0.8 0.2 0.2 1</ambient>
          </material>
        </visual>
      </link>
    </model>

    <!-- 一个箱子 -->
    <model name="box">
      <pose>-1 1 0.25 0 0 0</pose>
      <link name="body">
        <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.6 0.8 1</ambient>
          </material>
        </visual>
      </link>
    </model>
  </world>
</sdf>

保存为 terrain_lab.sdf,然后启动:

ign gazebo terrain_lab.sdf

你会看到一个红色墙和一个蓝色箱子。嗯,就这么简单。但别小看它——后面所有的地形感知实验,都是在这个基础上加传感器和复杂地形。

核心概念: 世界文件里的每个 <model> 就是一个物体。物体可以有 <visual>(看得见的)和 <collision>(物理碰撞的)。这两个可以不一样——比如你可以让一个物体看起来是圆形的,但碰撞体是方形的,这样计算更快。

4.6 知识结构图

下面这张图帮你理清本章的核心逻辑。从安装到运行,每一步都依赖上一步。

第4章 知识结构:仿真环境搭建 目标:可运行的仿真环境 ROS2 Humble Gazebo Ignition ros_ign_bridge 世界文件(SDF) 光照 & 地面 静态物体(墙/箱) 传感器模型 物理属性 可交互的3D仿真场景 提示:后续章节将在此基础上添加机器人模型和传感器插件

4.7 常见问题与避坑

  • Gazebo 启动后黑屏? 大概率是显卡驱动问题。试试 export IGN_GPU_INDEX=0,或者用 ign gazebo --headless-rendering 跑无头模式。
  • ros_ign_bridge 连不上? 检查 ROS_DOMAIN_ID 是否一致。我曾经在两个终端里设了不同的 ID,折腾了半小时才发现。
  • 世界文件加载慢? 第一次启动时 Gazebo 会从 Fuel 下载模型。你可以把常用模型缓存到本地:ign fuel download -u <模型URI>。
重要: 如果你在虚拟机里跑 Gazebo,请确保开启了 3D 加速。否则你会看到 0.5 FPS 的幻灯片——别问我怎么知道的。