1. 机器人仿真的格式差异
机器人仿真技术的核心价值在于,在无需物理硬件的前提下,快速验证机器人的运动学、动力学特性以及控制算法的有效性。ROS 2作为当前最主流的机器人操作系统,提供了完整的消息通信、节点管理和工具链生态;而Gazebo则提供了高精度的物理引擎、丰富的传感器模型和逼真的3D渲染能力。两者的结合,构成了现代机器人开发的标准仿真平台。
然而,这两个系统在模型描述格式上存在根本性差异:
- ROS 2 :使用统一机器人描述格式(Unified Robot Description Format, URDF),这是一种XML格式,专门用于描述机器人的连杆、关节、传感器和执行器
- Gazebo :使用仿真描述格式(Simulation Description Format, SDF),这是一种更通用的XML格式,不仅可以描述机器人,还可以描述整个仿真世界、光照、地形等
这一格式差异导致了一个问题:如何将ROS生态中大量存在的URDF模型无缝导入Gazebo进行仿真,同时保证模型的装配关系、物理属性和传感器配置的完整性
ros_gz_sim 正是ROS官方为解决这一问题而推出的核心工具,实现了URDF到SDF的自动转换,并提供了一套高层接口,让开发者可以从ROS 2端轻松控制Gazebo仿真的整个生命周期。
2. 从gazebo_ros_pkgs到ros_gz生态
在深入了解ros_gz_sim之前,有必要回顾ROS与Gazebo集成方案的演变,这有助于我们理解当前方案的设计理念和优势。
2.1 ROS 1时代:gazebo_ros_pkgs
在ROS 1时代,ROS与Gazebo Classic(Gazebo 11及以下版本)的集成主要通过gazebo_ros_pkgs元包实现。该方案采用紧耦合架构,通过在Gazebo进程中加载ROS插件的方式实现通信。这种架构虽然简单直接,但存在以下问题:
- 稳定性差:插件崩溃会导致整个Gazebo进程崩溃
- 可扩展性有限:难以支持多机器人和分布式仿真
- 兼容性问题:随着Gazebo和ROS的版本更新,兼容性问题日益突出
2.2 Gazebo重构:从Ignition到Gazebo Sim
2018年,Open Robotics启动了Gazebo的全面重构项目,代号为"Ignition"。新一代Gazebo采用了模块化设计,拆分为多个独立的库(如物理引擎、渲染引擎、通信库等),并引入了全新的传输协议Gazebo Transport,替代了原来的ROS通信。
2023年,Ignition正式更名为Gazebo Sim,标志着新一代仿真平台的成熟。然而,这一重构也导致了与旧版gazebo_ros_pkgs的完全不兼容。
2.3 新生态诞生:ros_gz元包
为了适配新一代Gazebo,ROS官方推出了全新的ros_gz元包,全面替代旧版的gazebo_ros_pkgs。ros_gz采用松耦合架构,通过独立的通信桥梁实现ROS 2 DDS与Gazebo Transport之间的消息转换。
ros_gz元包包含以下核心子包:
ros_gz_bridge:双向通信桥梁,实现ROS 2与Gazebo之间的消息类型转换和传输ros_gz_image:图像传输专用桥梁,支持image_transport压缩传输ros_gz_interfaces:定义ROS 2与Gazebo之间交互的标准消息和服务接口ros_gz_sim:提供Gazebo仿真启动、模型生成和生命周期管理的高层接口ros_gz_sim_demos:官方提供的各种ROS-Gazebo集成示例ros_gz_point_cloud:点云数据传输专用插件
3. ros_gz_sim的定位与架构
3.1 定位
ros_gz_sim是ROS 2与Gazebo之间的"调度层",它不直接处理物理仿真或底层通信,而是专注于提供一套便捷的高层接口,让开发者可以从ROS 2端轻松控制Gazebo仿真的各个方面。
其核心职责包括:
- 从ROS 2启动Gazebo仿真器并传递参数
- 自动将URDF模型转换为SDF格式并加载到Gazebo世界中
- 管理仿真模型的生命周期(生成、删除、位姿调整)
- 提供标准的ROS 2 launch文件接口,便于集成到复杂的机器人系统中
3.2 架构原理
ros_gz_sim的架构设计遵循松耦合原则,确保Gazebo仿真器与ROS 2系统完全独立运行,通过标准接口进行交互。
ros_gz_bridge
Gazebo 端
ROS 2 端
SDF 描述
命令行启动
ros_gz_sim
create 可执行文件
gz_sim.launch.py
gz sim
物理引擎 DART
渲染引擎 OGRE
DDS ↔ Gazebo Transport
关键设计要点:
- 进程隔离:ros_gz_sim与Gazebo运行在不同的进程中,一方崩溃不会影响另一方
- 命令行调用 :ros_gz_sim通过命令行调用
gz sim启动Gazebo,确保与Gazebo的原生接口完全一致 - 转换内置:URDF到SDF的转换逻辑内置在ros_gz_sim中,无需额外工具
- 通信分离 :所有ROS 2与Gazebo之间的消息通信都通过独立的
ros_gz_bridge节点完成
4. 核心功能
4.1 一键启动Gazebo仿真
ros_gz_sim提供了官方的launch文件gz_sim.launch.py,可以直接启动Gazebo并加载指定的世界文件,同时自动配置仿真时间等参数。
基本用法:
bash
# 启动空世界
ros2 launch ros_gz_sim gz_sim.launch.py
# 启动自定义世界
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:="my_world.world"
# 启动并自动开始仿真(不暂停)
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:="-r empty.world"
# 仅启动仿真服务器(无图形界面,适合远程部署)
ros2 launch ros_gz_sim gz_sim.launch.py server_only:=true
常用launch参数:
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
gz_args |
string | "" | 传递给gz sim命令的参数 |
use_sim_time |
bool | true | 是否使用仿真时间 |
server_only |
bool | false | 是否只启动服务器,不启动图形界面 |
gui_args |
string | "" | 传递给Gazebo GUI的参数 |
world |
string | "" | 要加载的世界文件路径 |
4.2 URDF到SDF自动转换
ros_gz_sim使得我们可以直接使用ROS生态中的URDF模型,无需手动转换为SDF格式,同时保证模型的完整性和准确性。
基本用法:
bash
# 在 Gazebo 仿真器里 spawn(生成 / 加载)一个 URDF 格式的机器人模型。
ros2 run ros_gz_sim create -file robot.urdf
# 读取 robot.urdf 文件的全部内容 → 变成一个长字符串 → 传给 Gazebo 直接生成机器人
ros2 run ros_gz_sim create -string "$(cat robot.urdf)"
# 指定模型名称和初始位姿
ros2 run ros_gz_sim create -file robot.urdf -name my_robot -x 1.0 -y 0.0 -z 0.5 -Y 1.57
完整参数列表:
bash
Usage: create [options]
Options:
-h, --help 显示帮助信息并退出
-file FILE SDF或URDF文件的路径
-string STRING SDF或URDF的XML字符串
-name NAME 模型名称
-allow_renaming 如果名称已存在,允许自动重命名
-x X 初始X坐标(米)
-y Y 初始Y坐标(米)
-z Z 初始Z坐标(米)
-R R 初始滚转角(弧度)
-P P 初始俯仰角(弧度)
-Y Y 初始偏航角(弧度)
-world WORLD 要加载模型的世界名称
-robot_namespace ROBOT_NAMESPACE
机器人命名空间
-sdf_version SDF_VERSION
生成的SDF版本
转换流程 :
当执行create命令时,ros_gz_sim内部会执行以下5个步骤:
- 解析输入:读取URDF文件或字符串,验证XML语法
- 扩展处理 :解析URDF中的
<gazebo>扩展标签,提取Gazebo特定配置 - 格式转换:将URDF的各个元素(link、joint、material等)映射为对应的SDF元素
- 路径转换 :将ROS风格的
package://路径转换为Gazebo风格的model://路径 - 模型加载:通过Gazebo Transport将生成的SDF发送到Gazebo服务器,完成模型加载
4.3 模型生命周期管理
除了生成模型,ros_gz_sim还提供了删除模型的功能,结合Gazebo提供的服务接口,可以实现完整的模型生命周期管理。
删除模型:
bash
ros2 run ros_gz_sim delete -name my_robot
调整模型位姿:
bash
ros2 service call /world/default/set_pose gazebo_msgs/SetEntityPose \
'{name: "my_robot", pose: {position: {x: 2.0, y: 1.0, z: 0.0}, orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}'
4.4 仿真状态控制
通过ros_gz_bridge,可以从ROS 2端控制Gazebo仿真的运行状态:
暂停仿真:
bash
ros2 service call /world/default/control gazebo_msgs/ControlWorld '{pause: true}'
继续仿真:
bash
ros2 service call /world/default/control gazebo_msgs/ControlWorld '{pause: false}'
重置世界:
bash
ros2 service call /world/default/reset gazebo_msgs/ResetWorld
5. URDF到SDF无损转换
对于工业机器人仿真而言,保证模型的装配关系和物理属性的准确性至关重要。任何微小的误差都可能导致仿真结果与实际情况不符,甚至影响控制算法的有效性。
5.1 URI路径转换:保证官方模型装配关系的核心
工业机器人厂商(如FANUC、ABB、KUKA等)提供的官方URDF模型中,所有的mesh文件路径都采用package://格式,指向官方支持包中的mesh文件。这些mesh文件经过了厂商的精确校准,其原点与对应link的坐标系原点完全重合。
当Gazebo加载这些mesh文件时,会将mesh的原点与link的坐标系原点对齐,从而保证各个link之间的装配关系完全正确。
如果在转换过程中,URI被修改为绝对路径或相对路径,可能会导致以下问题:
- 加载了修改过的、未校准的mesh文件
- mesh文件的单位不匹配(如毫米vs米)
- mesh文件的原点偏移
- 最终导致模型出现"零件错位"、"关节间隙过大"等问题
ros_gz_sim的URI转换规则 :
ros_gz_sim在转换URDF时,对URI的处理遵循以下严格规则:
| URDF中的URI格式 | 转换后的SDF中的URI格式 |
|---|---|
package://fanuc_m20ia_support/meshes/link_1.dae |
model://fanuc_m20ia_support/meshes/link_1.dae |
model://fanuc_m20ia_support/meshes/link_1.dae |
保持不变 |
file:///home/user/ros_ws/src/fanuc_m20ia_support/meshes/link_1.dae |
保持不变 |
meshes/link_1.dae(相对路径) |
转换为绝对路径 |
关键特性 :ros_gz_sim会自动将ROS风格的package://转换为Gazebo风格的model://。不需要修改官方URDF中的任何路径,ros_gz_sim会自动完成格式转换,确保Gazebo能够正确找到并加载官方提供的mesh文件。
5.2 固定关节处理:避免层次结构破坏
默认行为的问题 :
Gazebo的SDF解析器有一个默认行为:自动合并所有通过固定关节连接的link。这是为了优化物理仿真性能,减少需要计算的刚体数量。
然而,对于工业机器人而言,这一默认行为会导致严重的问题:
- 官方URDF中通常包含多个通过固定关节连接的link(如base_link、base、flange、tool0等)
- 转换后这些link会被合并成一个单一的link
- 原始的关节层次结构被破坏
- 无法获取各个固定关节的TF变换
- 导致后续的运动学计算和工具坐标系标定失败
解决方案 :
ros_gz_sim支持Gazebo的<preserveFixedJoint>扩展标签,可以禁止合并特定的固定关节。
全局禁止所有固定关节合并:
xml
<robot>
<!-- 其他URDF内容 -->
<gazebo>
<preserveFixedJoint>true</preserveFixedJoint>
</gazebo>
</robot>
针对特定关节禁止合并:
xml
<joint name="joint_6-flange" type="fixed">
<origin rpy="0 0 0" xyz="0 0 0" />
<parent link="link_6" />
<child link="flange" />
<gazebo>
<preserveFixedJoint>true</preserveFixedJoint>
</gazebo>
</joint>
ros_gz_sim会正确解析这个标签,并在转换后的SDF中保留固定关节,不会合并对应的link。
5.3 其他转换细节
除了URI和固定关节,ros_gz_sim还会完整转换以下URDF元素:
- 惯性参数 :完整保留
<inertial>标签中的质量和惯性矩阵 - 碰撞体积 :完整保留
<collision>标签中的几何形状和物理属性 - 关节限制 :完整保留
<limit>标签中的位置、速度和加速度限制 - 材质属性 :将URDF的
<material>标签转换为Gazebo支持的格式 - 传感器配置 :解析
<gazebo>标签中的传感器配置,转换为Gazebo插件格式
6. 使用流程
以FANUC M-20iA工业机器人为例,展示一套ros_gz_sim使用流程。这套流程遵循"先启动仿真世界,再加载机器人模型"的原则,最大限度地保证了仿真的稳定性和模型的准确性。
6.1 环境准备
首先,安装必要的软件包。本文以ROS 2 Humble和Gazebo Garden为例,这是目前工业界最稳定的组合之一。
步骤1:安装Gazebo Garden
bash
sudo apt install lsb-release wget gnupg
sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt update
sudo apt install gz-garden
步骤2:安装ros_gz元包
bash
sudo apt install ros-humble-ros-gz
步骤3:安装FANUC机器人支持包
bash
sudo apt install ros-humble-fanuc-m20ia-support
6.2 基于ExecuteProcess的Launch文件写法
在工业级应用中,推荐使用ExecuteProcess的方式编写launch文件。这种方式可以精确控制每个步骤的执行顺序,避免依赖话题传输带来的不确定性,同时便于调试。
python
import os
from launch import LaunchDescription
from launch.actions import ExecuteProcess, RegisterEventHandler
from launch.event_handlers import OnProcessExit
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
# ==============================
# 1. 启动Gazebo仿真世界
# ==============================
gz_sim_process = ExecuteProcess(
cmd=['gz', 'sim', '-r', 'empty.world'],
output='screen',
name='gz_sim'
)
# ==============================
# 2. 编译Xacro生成URDF
# ==============================
xacro_file_path = os.path.join(
get_package_share_directory('fanuc_m20ia_support'),
'urdf',
'fanuc_m20ia.urdf.xacro'
)
urdf_output_path = '/tmp/fanuc_m20ia.urdf'
xacro_process = ExecuteProcess(
cmd=['xacro', xacro_file_path, '-o', urdf_output_path],
output='screen',
name='xacro_compiler'
)
# ==============================
# 3. 生成机器人模型到Gazebo
# ==============================
spawn_robot_process = ExecuteProcess(
cmd=[
'ros2', 'run', 'ros_gz_sim', 'create',
'-file', urdf_output_path,
'-name', 'fanuc_m20ia',
'-x', '0.0', '-y', '0.0', '-z', '0.0'
],
output='screen',
name='spawn_robot'
)
# ==============================
# 4. 启动ROS 2核心节点
# ==============================
# 机器人状态发布者
robot_state_publisher_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{'robot_description': open(urdf_output_path).read()}]
)
# 关节状态发布者GUI(用于测试关节运动)
joint_state_publisher_gui_node = Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
name='joint_state_publisher_gui',
output='screen'
)
# ==============================
# 5. 启动ROS-Gazebo通信桥梁
# ==============================
# 时钟同步桥梁(必须,否则ROS节点无法使用仿真时间)
clock_bridge_node = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
#话题名 @ ROS消息类型 [ Gazebo消息类型
arguments=['/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock'],
output='screen',
name='clock_bridge'
)
# 关节状态桥梁(将Gazebo中的关节状态发布到ROS)
joint_state_bridge_node = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
#话题名 @ ROS消息类型 [ Gazebo消息类型
arguments=['/world/default/model/fanuc_m20ia/joint_state@sensor_msgs/msg/JointState[gz.msgs.Model'],
output='screen',
name='joint_state_bridge'
)
# ==============================
# 6. 事件处理:确保执行顺序
# ==============================
# 等待Xacro编译完成后,再启动模型生成和ROS节点
spawn_after_xacro = RegisterEventHandler(
event_handler=OnProcessExit(
target_action=xacro_process,
on_exit=[
spawn_robot_process,
robot_state_publisher_node,
joint_state_publisher_gui_node,
clock_bridge_node,
joint_state_bridge_node
]
)
)
# ==============================
# 构建Launch描述
# ==============================
return LaunchDescription([
gz_sim_process,
xacro_process,
spawn_after_xacro
])
Gazebo仿真与ROS2的时间同步
/clock 时钟话题名(ROS 2 和 Gazebo 共用这个话题名)
rosgraph_msgs/msg/Clock ROS 2 端的时钟消息类型
gz.msgs.Clock Gazebo 端的时钟消息类型
单向传输方向:Gazebo → ROS 2 补充符号方向: \[ = Gazebo → ROS 2(单向,本代码用这个) \] = ROS 2 → Gazebo(单向) = = 双向转发
- 精确的执行顺序控制 :通过
OnProcessExit事件处理器,确保Xacro编译完成后再生成模型,避免了竞争条件 - 不依赖话题传输 :直接将URDF文件传递给
create命令,避免了通过/robot_description话题传输可能导致的数据丢失或延迟 - 独立的进程输出:每个进程都有独立的输出,便于定位问题
- 与Gazebo原生接口一致 :直接调用
gz sim命令,确保与Gazebo的所有原生功能兼容 - 易于扩展:可以方便地添加更多的节点和进程,构建复杂的仿真系统
6.3 装配关系验证
启动仿真后,需要进行以下验证步骤,确保模型的装配关系和功能正常:
- 视觉检查:在Gazebo中观察机器人模型,各个连杆之间应该严丝合缝,没有明显的缝隙或重叠
- 关节运动测试 :拖动
joint_state_publisher_gui中的滑块,观察机器人各个关节的运动是否正常,是否存在卡顿或错位 - TF变换检查 :运行
ros2 run tf2_tools view_frames,生成TF树,检查所有关节的变换关系是否正确 - 碰撞体积检查:在Gazebo中点击"View"->"Collisions",显示碰撞体积,检查是否与视觉模型一致
- 惯性参数检查:在Gazebo中点击"View"->"Inertias",显示惯性框,检查是否与连杆的中心对齐
7. 常见问题诊断与解决方案
7.1 模型显示为红色方块
问题现象:机器人模型在Gazebo中显示为红色方块,无法正常渲染。
问题原因:Gazebo无法找到mesh文件。
解决方案:
-
检查mesh文件是否存在于正确的路径
-
确保Gazebo模型路径包含你的模型目录:
bashexport GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/ -
检查转换后的SDF中的URI是否正确,可以通过以下命令查看生成的SDF:
bashros2 run ros_gz_sim create -file robot.urdf -output-
7.2 零件错位,装配关系错误
问题现象:机器人的各个连杆之间出现明显的错位,关节运动时轨迹异常。
问题原因:
- mesh文件的原点与link坐标系原点不重合
- 固定关节被自动合并
- 关节的origin参数转换错误
解决方案:
- 确保使用官方提供的、未修改的mesh文件
- 在URDF中添加
<preserveFixedJoint>true</preserveFixedJoint>标签 - 手动检查转换后的SDF中的关节origin参数是否与URDF一致
7.3 模型生成失败,无错误信息
问题现象 :执行create命令后,没有任何输出,Gazebo中也没有出现模型。
问题原因:URDF文件存在语法错误,或者缺少必要的标签。
解决方案:
-
使用
check_urdf工具检查URDF语法:bashcheck_urdf robot.urdf -
确保URDF包含至少一个link和一个joint
-
检查所有的mesh路径和引用是否正确
7.4 多机器人仿真命名冲突
问题现象:在多机器人仿真中,第二个及以后的模型无法生成,或者出现关节状态混乱。
问题原因:多个模型使用了相同的名称或命名空间。
解决方案:
-
为每个模型指定唯一的名称:
bashros2 run ros_gz_sim create -file robot1.urdf -name robot1 ros2 run ros_gz_sim create -file robot2.urdf -name robot2 -
为每个模型指定唯一的命名空间:
bashros2 run ros_gz_sim create -file robot1.urdf -robot_namespace robot1
8. 新旧方案对比:ros_gz_sim vs gazebo_ros_pkgs
| 特性 | ros_gz_sim(新版) | gazebo_ros_pkgs(旧版) |
|---|---|---|
| 支持的Gazebo版本 | Gazebo Fortress及以上 | Gazebo Classic 11及以下 |
| 架构 | 松耦合,Gazebo与ROS完全独立 | 紧耦合,通过插件在Gazebo进程中运行ROS代码 |
| 通信方式 | 基于ros_gz_bridge(DDS ↔ Gazebo Transport) | 基于ROS话题直接通信 |
| URDF转换 | 内置自动转换,支持preserveFixedJoint标签 | 需要spawn_model节点,默认合并固定关节 |
| URI处理 | 自动将package://转换为model:// | 保留package://,依赖ROS环境解析 |
| 稳定性 | 高,进程隔离,一方崩溃不影响另一方 | 低,插件崩溃会导致整个Gazebo崩溃 |
| 可扩展性 | 好,支持分布式仿真和多机器人 | 有限,难以支持大规模仿真 |
| 维护状态 | 活跃,官方持续维护 | 逐渐停止维护,仅修复关键bug |
| 工业适用性 | 高,适合复杂工业机器人仿真 | 一般,适合简单的教育和研究场景 |
总结
-
ros_gz_sim是ROS 2与新一代Gazebo集成的官方标准方案,替代了旧版的gazebo_ros_pkgs,采用松耦合架构,具有更高的稳定性和可扩展性。
-
URDF到SDF的自动转换是ros_gz_sim最核心的功能,它通过自动转换URI路径、支持preserveFixedJoint标签等机制,保证了工业机器人官方模型的装配关系完整性。
-
保留官方
model://URI是保证装配关系不变的关键,因为它确保Gazebo加载的是厂商精确校准过的mesh文件。 -
基于ExecuteProcess的launch文件写法是工业级应用的最佳实践,它可以精确控制执行顺序,避免依赖话题传输带来的不确定性。
-
禁止合并固定关节是保证机器人层次结构完整的必要步骤,特别是对于包含多个固定关节的工业机器人模型。