Skip to content

ROS 2 集成

本教程介绍如何使用 ROS 2 与 Gazebo 进行通信。这种双向通信非常实用:既可以将 ROS 侧的数据(如关节状态、TF)或控制命令传递给 Gazebo,也可以反过来将 Gazebo 的数据发送到 ROS。此外,还支持在 RViz 中可视化由 Gazebo 世界仿真的机器人模型。

ros_gz_bridge 提供了一个网络桥接,能够在 ROS 2 与 Gazebo Transport 之间交换消息。它只支持特定的消息类型,请查看此 README 确认所需的消息类型是否受支持。

使用示例可在 ros_gz_sim_demos 中找到,其中包含覆盖所有主要驱动和传感器类型的演示 launch 文件。

可以初始化一个双向桥接,让 ROS 作为发布者、Gazebo 作为订阅者,也可以反过来。语法为 /TOPIC@ROS_MSG@GZ_MSG,其中 TOPIC 是 Gazebo 内部话题,ROS_MSG 是该话题的 ROS 消息类型,GZ_MSG 是 Gazebo 消息类型。

例如:

ros2 run ros_gz_bridge parameter_bridge /scan@sensor_msgs/msg/LaserScan@gz.msgs.LaserScan

ros2 run ros_gz_bridge parameter_bridge 命令用于运行 ros_gz_bridge 包中的 parameter_bridge 节点,其后指定消息要发布到的话题 /TOPIC。第一个 @ 符号分隔话题名称与消息类型,紧随其后的第一个类型就是 ROS 消息类型。

ROS 消息类型后面可以跟 @、[ 或 ] 符号,其中:

  • @ 表示双向桥接。
  • [ 表示从 Gazebo 到 ROS 的桥接。
  • ] 表示从 ROS 到 Gazebo 的桥接。

请查看这些 示例,了解如何建立从 ROS 到 Gazebo(以及反向)的通信连接。

也可以将 ros_gz_bridge 与 ROS Launch 配合使用,以 YAML 格式定义话题映射,在启动时交给桥接节点。

- ros_topic_name: "scan"
gz_topic_name: "/scan"
ros_type_name: "sensor_msgs/msg/LaserScan"
gz_type_name: "gz.msgs.LaserScan"
direction: GZ_TO_ROS # BIDIRECTIONAL or ROS_TO_GZ

配置文件是一个 YAML 文件,定义了要桥接的 ROS 话题和 Gazebo 话题之间的映射。每一对要桥接的话题支持以下参数:

  • ros_topic_name:ROS 侧的话题名称。
  • gz_topic_name:Gazebo 侧对应的话题名称。
  • ros_type_name:该 ROS 话题的消息类型。
  • gz_type_name:该 Gazebo 话题的消息类型。
  • subscriber_queue:ROS 订阅者队列的大小。
  • publisher_queue:ROS 发布者队列的大小。
  • lazy:是否启用惰性订阅。如果没有真正的订阅者,桥接节点不会创建内部订阅者,这有助于提升性能。
  • direction:可指定为 GZ_TO_ROS、ROS_TO_GZ 或 BIDIRECTIONAL。

完整的有效配置文件示例见 此文件。

使用 ros_gz_bridge 包自带的 launch 文件启动桥接

Section titled “使用 ros_gz_bridge 包自带的 launch 文件启动桥接”

ros_gz_bridge 包中包含一个名为 ros_gz_bridge.launch.py 的 launch 文件,可用于启动 ROS 2 与 Gazebo 之间的桥接。

下面是一个示例:

Terminal window
ros2 launch ros_gz_bridge ros_gz_bridge.launch.py bridge_name:=ros_gz_bridge config_file:=<path_to_your_YAML_file>

使用组合(composition)启动:

Terminal window
ros2 launch ros_gz_bridge ros_gz_bridge.launch.py bridge_name:=ros_gz_bridge config_file:=<path_to_your_YAML_file> use_composition:=True create_own_container:=True

如果已经有一个正在运行的 container,也可以在启动桥接时通过 container_name 参数传入它的名称。关于组合的更多信息,请参阅 组合。

查看 源代码中的这一段,了解该 launch 文件接受的所有参数。

QoS 覆盖:

QoS 覆盖可以通过 launch 文件的 bridge_params 参数作为额外参数传给桥接节点。示例:

Terminal window
ros2 launch ros_gz_bridge ros_gz_bridge.launch.py bridge_name:=ros_gz_bridge config_file:=<path_to_your_YAML_file> bridge_params:={'qos_overrides./topic_name.publisher.durability': 'transient_local', 'qos_overrides./another_topic_name.publisher.durability': 'transient_local'}

传入 bridge_params 的内容可以省略以下字符:{}、空格( )、"、'。

关于使用 QoS 覆盖的更多信息,请参阅 ROS 文档。

从自定义 XML launch 文件启动桥接

Section titled “从自定义 XML launch 文件启动桥接”

你可以从自定义 launch 文件启动桥接。为此提供了 <ros_gz_bridge/> 标签,可以在 XML 或 YAML launch 文件中使用。此时,参数通过该标签的属性传入。下面是一个简化示例,更完整的示例请参阅 此处:

<launch>
<arg name="bridge_name" />
<arg name="config_file" />
<ros_gz_bridge
bridge_name="$(var bridge_name)"
config_file="$(var config_file)">
</ros_gz_bridge>
</launch>

此时,<ros_gz_bridge> 的参数从命令行读取。这只是其中一种方式——也可以硬编码部分值,或者完全不使用命令行参数。

从自定义 Python launch 文件启动桥接

Section titled “从自定义 Python launch 文件启动桥接”

下面是一个从 Python 加载桥接的简化 launch 文件示例,更完整的示例请参阅 这里:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, TextSubstitution
from ros_gz_bridge.actions import RosGzBridge
def generate_launch_description():
bridge_name = LaunchConfiguration('bridge_name')
config_file = LaunchConfiguration('config_file')
declare_bridge_name_cmd = DeclareLaunchArgument(
'bridge_name', description='Name of ros_gz_bridge node'
)
declare_config_file_cmd = DeclareLaunchArgument(
'config_file', description='YAML config file'
)
# Create the launch description and populate
ld = LaunchDescription([
RosGzBridge(
bridge_name=LaunchConfiguration('bridge_name'),
config_file=LaunchConfiguration('config_file'),
),
])
# Declare the launch options
ld.add_action(declare_bridge_name_cmd)
ld.add_action(declare_config_file_cmd)
return ld

下面使用 Gazebo 的 Key Publisher 插件向 ROS 发送消息。

注意: 确保所有需要的工作空间(ROS、Gazebo 和 ros_gz 等)都已 source。

首先,启动一个 ROS 与 Gazebo 之间的桥接,指定 Key Publisher 插件发送消息的话题以及消息类型,如下所示:

ros2 run ros_gz_bridge parameter_bridge /keyboard/keypress@std_msgs/msg/Int32@gz.msgs.Int32

这里在 /keyboard/keypress 话题上建立了桥接,消息类型为 Int32。ROS 侧为 std_msgs/msg/Int32,Gazebo 侧为 gz.msgs.Int32。

在另一个终端中启动一个 Gazebo 世界,例如 empty.sdf:

gz sim empty.sdf

然后从右上角的下拉菜单中添加 Key Publisher 插件。

添加 Key Publisher 插件后的空世界

在另一个终端中启动 ROS 监听器:

ros2 topic echo /keyboard/keypress

该命令用于监听 /keyboard/keypress 话题上的消息。

在 Gazebo 窗口中按下键盘按键,监听器终端中应能看到接收到的数据。

接下来,可以尝试将数据从 ROS 发送到 Gazebo,或换用不同的数据类型和通信方向。

本教程的视频演示可在 YouTube 频道观看:Gazebo tutorials: ROS 2 Foxy integration。

进一步,可以试试 ros_gz_sim_demos 中的演示。

对于 sdf_parser 演示,需要在 colcon 工作空间中从源码安装 ros_gz 和解析器插件 sdformat_urdf。关于 sdformat_urdf 的更多信息,请参阅 其文档。

设置 RViz 启动参数并运行演示 launch 文件:

Terminal window
ros2 launch ros_gz_sim_demos sdf_parser.launch.py rviz:=True

在 Gazebo 中启动仿真,等待几秒钟让 TF 发布。

在另一个终端中,发送 ROS 或 Gazebo 命令让车辆绕圈行驶:

Terminal window
gz topic -t "/model/vehicle/cmd_vel" -m gz.msgs.Twist -p "linear: {x: 1.0}, angular: {z: -0.1}"
ros2 topic pub /model/vehicle/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 5.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: -0.1}}

然后验证车辆在 Gazebo 和 RViz 中的轨迹是否一致。

Gazebo 与 RViz 同步显示车辆

关于该演示的实现细节,请参阅 ROS 2 互操作。

ROS 2 仿真接口是一组用于控制和交互仿真环境的标准 ROS 2 service、message 和 action 定义。这些接口与具体的仿真器无关,不同的仿真器可以实现相同的接口,同时保持 API 一致。

Gazebo 已经实现了这些接口,支持以下操作:

  • 生成和删除实体
  • 控制仿真状态和时间步进
  • 查询实体和世界状态
  • 获取仿真器信息

完整的教程(包括每个接口的使用示例)请参阅 使用 ROS 2 仿真接口与 Gazebo 交互。