Skip to content

使用 ROS 2 Launch 启动 Gazebo

可以通过多种方式从 ROS 2 launch 系统启动 Gazebo。

ros_gz_sim 包提供了两个 launch 文件:gz_server.launch.py 和 gz_sim.launch.py,分别用于启动 Gazebo server,或启动完整的 Gazebo(server 和 GUI)。

Terminal window
ros2 launch ros_gz_sim gz_sim.launch.py gz_args:=empty.sdf

或者只启动 server:

Terminal window
ros2 launch ros_gz_sim gz_server.launch.py world_sdf_file:=empty.sdf

请查看各 launch 文件的参数块(这里 和 这里),了解每个 launch 文件所接受的参数。

也可以从自定义 launch 文件启动 Gazebo。为此提供了自定义的 <gz_server/> 标签,可在 XML launch 文件中使用。此时,参数通过标签内的属性传入。下面是启动 Gazebo server 的示例:

<launch>
<arg name="world_sdf_file" default="empty.sdf" />
<arg name="world_sdf_string" default="" />
<arg name="container_name" default="ros_gz_container" />
<arg name="create_own_container" default="False" />
<arg name="use_composition" default="False" />
<gz_server
world_sdf_file="$(var world_sdf_file)"
world_sdf_string="$(var world_sdf_string)"
container_name="$(var container_name)"
create_own_container="$(var create_own_container)"
use_composition="$(var use_composition)">
</gz_server>
</launch>

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

与 XML launch 文件相比,Python launch 文件提供了更底层的定制和逻辑控制能力。例如,可以设置环境变量,加入 Python 特有的函数和逻辑。下面的示例中,可将示例包、world 和桥接 topic 替换为自己的内容。它更像是一个脚手架,而非可以直接独立运行的文件。

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import SetEnvironmentVariable, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
ros_gz_sim_pkg_path = get_package_share_directory('ros_gz_sim')
example_pkg_path = FindPackageShare('example_package') # Replace with your own package name
gz_launch_path = PathJoinSubstitution([ros_gz_sim_pkg_path, 'launch', 'gz_sim.launch.py'])
return LaunchDescription([
SetEnvironmentVariable(
'GZ_SIM_RESOURCE_PATH',
PathJoinSubstitution([example_pkg_path, 'models'])
),
SetEnvironmentVariable(
'GZ_SIM_PLUGIN_PATH',
PathJoinSubstitution([example_pkg_path, 'plugins'])
),
IncludeLaunchDescription(
PythonLaunchDescriptionSource(gz_launch_path),
launch_arguments={
'gz_args': PathJoinSubstitution([example_pkg_path, 'worlds/example_world.sdf']), # Replace with your own world file
'on_exit_shutdown': 'True'
}.items(),
),
# Bridging and remapping Gazebo topics to ROS 2 (replace with your own topics)
Node(
package='ros_gz_bridge',
executable='parameter_bridge',
arguments=['/example_imu_topic@sensor_msgs/msg/Imu@gz.msgs.IMU',],
remappings=[('/example_imu_topic',
'/remapped_imu_topic'),],
output='screen'
),
])

下面是另一个示例,使用 Python 中的高层 action 来启动 gzserver:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from ros_gz_sim.actions import GzServer
def generate_launch_description():
declare_world_sdf_file_cmd = DeclareLaunchArgument(
'world_sdf_file', default_value='',
description='Path to the SDF world file')
# Create the launch description and populate
ld = LaunchDescription([
GzServer(
world_sdf_file=LaunchConfiguration('world_sdf_file')
),
])
# Declare the launch options
ld.add_action(declare_world_sdf_file_cmd)
return ld

XML 示例 launch 文件可在 此处 查看。 Python 示例 launch 文件可在 此处 查看。

直接从终端使用这些 launch 文件的示例命令:

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

在上面的 launch 文件中,可能会注意到 ros_gz_bridge 的 create_own_container 参数被硬编码为 False。这样做是为了避免创建两个重复的 container(一个用于 gz_server,另一个用于 ros_gz_bridge),而是让 ros_gz_bridge 复用 gz_server 创建的 container。更多信息可参阅 此 PR。

关于 ros_gz_bridge 的更多信息,请参阅 ROS 2 集成。 关于组合的更多信息,请参阅 组合。

关于 ROS 2 Control 的进一步注意事项

Section titled “关于 ROS 2 Control 的进一步注意事项”

如果打算将 ros2_control 与 Gazebo 配合使用,请查看 gz_ros2_control 仓库中的 示例 launch 文件。

为了让 controller_manager 正常工作,必须发布 /clock topic:

gz_bridge = Node(
package="ros_gz_bridge",
executable="parameter_bridge",
arguments=['/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock'],
parameters=[{
"qos_overrides./tf_static.publisher.durability": "transient_local"
}],
output="screen",
)

如果没有发布 /clock topic,controller_manager 会发出类似下面的警告或错误:

Terminal window
[gazebo-1] [WARN] [1744219953.983130822] [controller_manager]: No clock received, using time argument instead! Check your node's clock configuration (use_sim_time parameter) and if a valid clock source is available.

默认情况下,由 gz_ros2_control 启动的 controller_manager 的 use_sim_time 为 true。如果由于某种原因将其设置为 false,它会回退到系统时钟。

这会导致如下日志:

Terminal window
[gazebo-1] [INFO] [1744209678.974210234] [gz_ros_control]: Loading controller_manager
[gazebo-1] [INFO] [1744209679.000651931] [controller_manager]: Using Steady (Monotonic) clock for triggering controller manager cycles.

最终导致致命错误:

Terminal window
[gazebo-1] terminate called after throwing an instance of 'std::runtime_error'
[gazebo-1] what(): can't compare times with different time sources

在使用仿真时间时,请确保 use_sim_time 正确设置为 true,以避免此类不匹配。