跳转至

Gazebo

5e430036ce538f09f700003a

  • 官方网站:http://gazebosim.org/
  • 支持的物理引擎:ODE/Bullet/Simbody/DART
  • 开源仿真环境
  • 属于ROS生态

引言

Gazebo是目前最广泛使用的机器人仿真环境,最早在2004年由USC Robotics Research Lab (南加州大学机器人实验室) 开发。依托于ROS的发展,Gazebo具有很强的仿真能力,同时也在机器人研究和开发中得到了广泛应用。Gazebo的功能包括:动力学仿真 (Dynamics Simulation)、传感器仿真 (Sensor Simulation)、三维环境仿真 (3D Environment Simulation),同时支持多种机器人模型:包括PR2、Turtlebot、AR.Drone等。

Gazebo Classic 与 Gazebo (Ignition/Gz)

Gazebo项目经历了重要的版本演变。Gazebo Classic(版本1至11)是长期以来被广泛使用的版本,其最终版本Gazebo 11已于2025年停止官方支持。Gazebo(前称Ignition Gazebo,后更名为Gz)是新一代仿真平台,采用模块化架构 (Modular Architecture) 重新设计,提供更灵活的组件化系统。新版Gazebo使用Gz Transport通信层替代了旧版的自定义通信方式,并引入了更先进的渲染引擎Ogre2。

系统架构

Gazebo采用客户端-服务器架构 (Client-Server Architecture):

  • gzserver:负责物理仿真计算、传感器数据生成以及世界状态的维护。该进程在后台运行,不依赖图形界面。
  • gzclient:提供三维可视化界面,用户可以通过图形界面观察仿真场景、插入模型以及调整仿真参数。

这种架构设计使得仿真计算与可视化分离,允许在无头模式 (Headless Mode) 下运行仿真,适合在服务器或CI/CD流水线中进行自动化测试。

传感器支持

Gazebo内置了丰富的传感器模型 (Sensor Models),覆盖了机器人常用的感知设备:

  • 相机 (Camera):包括单目相机、双目相机 (Stereo Camera)、深度相机 (Depth Camera) 以及全景相机
  • 激光雷达 (LiDAR):支持二维和三维激光扫描仪,可配置扫描范围、分辨率和噪声参数
  • 惯性测量单元 (IMU):模拟加速度计和陀螺仪数据
  • GPS:提供全局定位信息仿真
  • 力/力矩传感器 (Force/Torque Sensor):用于检测关节处的力和力矩
  • 接触传感器 (Contact Sensor):检测物体间的碰撞和接触状态

每种传感器均支持噪声模型 (Noise Model) 配置,使仿真数据更加贴近真实传感器的输出特性。

与ROS的集成

Gazebo与ROS (Robot Operating System) 的深度集成是其核心优势之一。通过 gazebo_ros_pkgs 功能包集,Gazebo能够将仿真数据以ROS话题 (Topic)、服务 (Service) 和动作 (Action) 的形式发布,使得相同的ROS节点代码可以无缝地在仿真环境与真实硬件之间切换。对于ROS 2,集成方式通过 ros_gz_bridge 桥接包实现,支持在Gz仿真与ROS 2之间转换消息格式。

模型格式:URDF 与 SDF

Gazebo支持两种主要的模型描述格式:

  • URDF (Unified Robot Description Format):ROS生态中标准的机器人描述格式,用于定义机器人的连杆 (Link)、关节 (Joint) 和运动学结构。URDF的局限性在于仅支持树状结构,不支持闭合运动链 (Closed Kinematic Chain)。
  • SDF (Simulation Description Format):Gazebo原生支持的仿真描述格式,功能比URDF更加丰富。SDF不仅可以描述机器人模型,还可以定义完整的仿真世界 (World),包括光照、地形、物理参数等。SDF支持闭合运动链以及更复杂的传感器定义。

插件系统

Gazebo提供灵活的插件系统 (Plugin System),允许用户在不修改源代码的情况下扩展仿真功能。插件主要分为以下几类:

  • World Plugin:控制仿真世界的全局行为
  • Model Plugin:附加到特定模型上,控制模型的运动或行为
  • Sensor Plugin:自定义传感器的数据处理逻辑
  • Visual Plugin:控制可视化渲染效果

插件使用C++编写,通过共享库 (Shared Library) 的方式动态加载。

安装方式

在Ubuntu系统上,可以通过APT包管理器安装Gazebo。以ROS 2 Humble配合Gz Fortress为例:

sudo apt install ros-humble-ros-gz

安装完成后,可以通过以下命令启动一个空白世界进行验证:

gz sim empty.sdf

优势与局限

优势:

  • 与ROS生态无缝集成,社区庞大,文档资源丰富
  • 支持多种物理引擎,灵活切换
  • 传感器模型种类齐全,噪声模型可配置
  • 插件系统扩展性强

局限:

  • 视觉渲染质量相比游戏引擎 (如Unreal、Unity) 仍有差距
  • 对于大规模场景或高精度接触仿真,性能可能成为瓶颈
  • Gazebo Classic到新版Gazebo的迁移存在一定学习成本

SDF 与 URDF 建模

说明 Gazebo 使用的两种模型格式的具体用法与配置方式。

SDF(Simulation Description Format)

SDF 是 Gazebo 原生格式,比 URDF 功能更丰富。SDF 文件可描述单个机器人模型,也可描述包含多个模型、光源和物理参数的完整仿真世界。一个最简单的机器人 SDF 模型示例:

<?xml version="1.0"?>
<sdf version="1.9">
  <model name="simple_robot">
    <link name="base_link">
      <inertial>
        <mass>1.0</mass>
        <inertia><ixx>0.1</ixx><iyy>0.1</iyy><izz>0.1</izz></inertia>
      </inertial>
      <visual name="visual">
        <geometry><box><size>0.5 0.3 0.2</size></box></geometry>
      </visual>
      <collision name="collision">
        <geometry><box><size>0.5 0.3 0.2</size></box></geometry>
      </collision>
    </link>
  </model>
</sdf>

<inertial> 中的惯性张量参数对物理仿真的稳定性至关重要。对于一个质量为 、边长为 的均质长方体,主轴惯性矩的计算公式为:

URDF 转 SDF

ROS 通常使用 URDF 描述机器人,Gazebo 加载时会自动转换为 SDF,但需要额外的 Gazebo 插件标签。URDF 中添加 Gazebo 特定标签的示例:

<gazebo reference="base_link">
  <material>Gazebo/Blue</material>
  <mu1>0.9</mu1>
  <mu2>0.9</mu2>
</gazebo>

其中 mu1mu2 分别表示 ODE 物理引擎中的静摩擦系数 (Static Friction Coefficient) 和动摩擦系数 (Dynamic Friction Coefficient)。

xacro 宏语言(XML Macro Language)可以简化复杂机器人的 URDF 编写,避免重复代码。使用 xacro 定义可复用的轮子宏示例:

<xacro:macro name="wheel" params="name x_offset">
  <link name="${name}_link">
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.001" ixy="0.0" ixz="0.0"
               iyy="0.001" iyz="0.0" izz="0.001"/>
    </inertial>
    <visual>
      <geometry><cylinder radius="0.05" length="0.04"/></geometry>
    </visual>
    <collision>
      <geometry><cylinder radius="0.05" length="0.04"/></geometry>
    </collision>
  </link>
  <joint name="${name}_joint" type="continuous">
    <parent link="base_link"/>
    <child link="${name}_link"/>
    <origin xyz="${x_offset} 0.22 0" rpy="-1.5708 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>
</xacro:macro>

<!-- 调用宏 -->
<xacro:wheel name="left_wheel" x_offset="0.0"/>
<xacro:wheel name="right_wheel" x_offset="0.0"/>

World 文件结构

World 文件定义了仿真场景,包括物理参数、光源、模型和插件:

<?xml version="1.0"?>
<sdf version="1.9">
  <world name="robot_world">
    <!-- 物理引擎参数 -->
    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>

    <!-- 光源 -->
    <light name="sun" type="directional">
      <direction>-0.5 0.1 -0.9</direction>
      <diffuse>0.8 0.8 0.8 1</diffuse>
    </light>

    <!-- 地面 -->
    <model name="ground_plane">
      <static>true</static>
      <link name="link">
        <collision name="collision">
          <geometry><plane><normal>0 0 1</normal></plane></geometry>
        </collision>
      </link>
    </model>

    <!-- 加载机器人模型 -->
    <include>
      <uri>model://my_robot</uri>
      <pose>0 0 0.5 0 0 0</pose>
    </include>
  </world>
</sdf>

<pose> 标签使用 6 个数值表示模型位姿:前三个为平移分量 (单位:米),后三个为欧拉角 (单位:弧度)。

传感器插件

Gazebo 通过插件系统仿真各类传感器,以下是常用传感器的 SDF/URDF 配置。

激光雷达(LiDAR)

<sensor name="lidar" type="ray">
  <pose>0 0 0.1 0 0 0</pose>
  <always_on>true</always_on>
  <update_rate>10</update_rate>
  <ray>
    <scan>
      <horizontal>
        <samples>360</samples>
        <resolution>1</resolution>
        <min_angle>-3.14159</min_angle>
        <max_angle>3.14159</max_angle>
      </horizontal>
    </scan>
    <range>
      <min>0.12</min>
      <max>30.0</max>
    </range>
    <noise><type>gaussian</type><mean>0.0</mean><stddev>0.01</stddev></noise>
  </ray>
  <plugin name="laser" filename="libgazebo_ros_ray_sensor.so">
    <ros><namespace>/robot</namespace></ros>
    <output_type>sensor_msgs/LaserScan</output_type>
    <frame_name>lidar_link</frame_name>
  </plugin>
</sensor>

激光雷达测量噪声通常建模为高斯噪声 (Gaussian Noise),即实际测距值 与真实距离 之间满足:

上述配置中 stddev 设为 0.01,即标准差 米。

RGB 摄像头

<sensor name="camera" type="camera">
  <update_rate>30</update_rate>
  <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>
    <noise><type>gaussian</type><stddev>0.007</stddev></noise>
  </camera>
  <plugin name="camera_plugin" filename="libgazebo_ros_camera.so">
    <ros><namespace>/robot</namespace></ros>
    <frame_name>camera_link_optical</frame_name>
  </plugin>
</sensor>

horizontal_fov 为水平视场角 (Horizontal Field of View),单位弧度。1.047 弧度约等于 60°。相机的焦距 (像素单位)与视场角 及图像宽度 的关系为:

IMU(惯性测量单元)

<sensor name="imu_sensor" type="imu">
  <always_on>true</always_on>
  <update_rate>200</update_rate>
  <imu>
    <angular_velocity>
      <x><noise type="gaussian"><stddev>2e-4</stddev></noise></x>
    </angular_velocity>
    <linear_acceleration>
      <x><noise type="gaussian"><stddev>1.7e-2</stddev></noise></x>
    </linear_acceleration>
  </imu>
  <plugin filename="libgazebo_ros_imu_sensor.so" name="imu_plugin">
    <ros><namespace>/robot</namespace></ros>
    <frame_name>imu_link</frame_name>
  </plugin>
</sensor>

深度相机(RGB-D)

<sensor name="depth_camera" type="depth">
  <update_rate>30</update_rate>
  <camera>
    <horizontal_fov>1.047</horizontal_fov>
    <image><width>640</width><height>480</height></image>
    <clip><near>0.05</near><far>8.0</far></clip>
  </camera>
  <plugin filename="libgazebo_ros_camera.so" name="depth_plugin">
    <ros><namespace>/robot</namespace></ros>
    <frame_name>camera_depth_optical_frame</frame_name>
    <min_depth>0.05</min_depth>
    <max_depth>8.0</max_depth>
  </plugin>
</sensor>

深度相机(如 Intel RealSense、Microsoft Kinect)同时输出彩色图像和深度图,发布的 ROS 话题通常包括 /robot/depth/image_raw(深度图,单位毫米)和 /robot/depth/points(点云,sensor_msgs/PointCloud2)。

差速驱动插件

移动机器人底盘通常使用差速驱动(Differential Drive)控制插件:

<plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
  <ros><namespace>/robot</namespace></ros>
  <left_joint>left_wheel_joint</left_joint>
  <right_joint>right_wheel_joint</right_joint>
  <wheel_separation>0.44</wheel_separation>
  <wheel_diameter>0.1</wheel_diameter>
  <max_wheel_torque>20</max_wheel_torque>
  <max_wheel_acceleration>1.0</max_wheel_acceleration>
  <publish_odom>true</publish_odom>
  <publish_odom_tf>true</publish_odom_tf>
  <publish_wheel_tf>true</publish_wheel_tf>
  <odometry_frame>odom</odometry_frame>
  <robot_base_frame>base_footprint</robot_base_frame>
</plugin>

差速驱动模型中,给定左轮线速度 与右轮线速度 ,机器人中心的线速度 和角速度 为:

其中 为两轮间距(wheel_separation)。插件接收 geometry_msgs/Twist 消息,将 转换为左右轮速度指令,同时根据编码器积分计算里程计 (Odometry) 数据。

与 ROS 2 集成(Gz + ROS 2)

新版 Gazebo(Gz Harmonic 及以上)与 ROS 2 的集成通过 ros_gz 桥接包实现。

安装

sudo apt install ros-humble-ros-gz

启动 Gazebo 并加载 World

# launch/robot.launch.py
import os
from launch import LaunchDescription
from launch.actions import ExecuteProcess, IncludeLaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    pkg_dir = get_package_share_directory('my_robot_pkg')
    world_file = os.path.join(pkg_dir, 'worlds', 'robot_world.sdf')

    gz_sim = ExecuteProcess(
        cmd=['gz', 'sim', '-r', world_file],
        output='screen'
    )

    # 桥接 Gazebo 话题到 ROS 2
    bridge = Node(
        package='ros_gz_bridge',
        executable='parameter_bridge',
        arguments=[
            '/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock',
            '/robot/scan@sensor_msgs/msg/LaserScan[gz.msgs.LaserScan',
            '/robot/cmd_vel@geometry_msgs/msg/Twist]gz.msgs.Twist',
            '/robot/odom@nav_msgs/msg/Odometry[gz.msgs.Odometry',
        ],
        output='screen'
    )

    return LaunchDescription([gz_sim, bridge])

话题方向符号说明:[ 表示从 Gz 到 ROS 2 的单向桥接,] 表示从 ROS 2 到 Gz 的单向桥接,@ 表示双向桥接。

常用 gz 命令行工具

# 列出运行中的仿真世界
gz service -s /gazebo/worlds --reqtype gz.msgs.Empty --reptype gz.msgs.StringMsg_V --req '' --timeout 2000

# 暂停/恢复仿真
gz service -s /world/robot_world/control --reqtype gz.msgs.WorldControl --reptype gz.msgs.Boolean --req 'pause: true' --timeout 2000

# 查看话题列表
gz topic -l

# 查看指定话题
gz topic -e -t /robot/scan

# 加载新模型到运行中的场景
gz service -s /world/robot_world/create --reqtype gz.msgs.EntityFactory --reptype gz.msgs.Boolean \
  --req 'sdf_filename: "/path/to/model.sdf", pose: {position: {x: 1.0, y: 0.0, z: 0.5}}' --timeout 2000

物理引擎选择

Gazebo 支持多种物理引擎,可在 world 文件中指定:

物理引擎 特点 适用场景
ODE(默认) 稳定成熟,速度较快 通用机器人仿真
Bullet 精确碰撞检测 复杂碰撞场景
DART 接触力计算更准确 人形机器人、操作任务
TPE(Trivial Physics Engine) 运动学仿真,无动力学 非物理运动演示

新版 Gz 默认使用内置物理引擎,并支持通过 DART 插件实现高精度接触仿真。物理引擎的核心任务是在每个仿真时间步长 内求解运动方程:

其中 为质量矩阵, 为科氏力与离心力矩阵, 为重力项, 为广义关节力矩, 为外力(包括接触力)的广义力映射。

性能调优

在复杂场景下提升 Gazebo 仿真性能的方法:

  • 降低更新频率:将不关键的传感器(如相机)update_rate 从 30 Hz 降低到 10 Hz,显著减少计算负载
  • 减少碰撞体复杂度:使用简化的碰撞几何体(如 box、cylinder)代替精细的 mesh
  • 关闭阴影渲染:在训练场景中禁用阴影以提升渲染帧率
  • 无头模式:训练时使用 gz sim -s(仅启动服务器,不启动 GUI)节省 GPU 资源
  • 实时因子控制:设置 real_time_factor 小于 1 可放慢仿真(调试用),大于 1 加速仿真(训练用)

实时因子(Real Time Factor,RTF)定义为仿真时间推进速度与挂钟时间 (Wall Clock Time) 的比值:

RTF 为 1 表示仿真与现实时间同步;RTF 大于 1 表示仿真快于现实,适用于强化学习数据采集场景。

常见问题排查

问题 原因 解决方法
模型加载失败 GAZEBO_MODEL_PATH 未设置 export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:~/models
物理不稳定、模型抖动 时间步长过大或惯性参数错误 减小 max_step_size,检查 <inertia> 数值
传感器不发布数据 插件未正确加载 检查 gz topic -l 是否有对应话题
ROS 收不到 Gz 话题 桥接配置错误 核对 parameter_bridge 的话题方向([]
仿真运行缓慢 场景过于复杂或碰撞体面数过高 简化碰撞几何体,使用无头模式,降低传感器更新频率
关节突然爆炸或穿透 惯性矩阵设置不合理(如全零) 使用物体实际几何形状计算合理的惯性参数

参考资料