预计完成时间:10 分钟
这是把模型生成到 Gazebo Sim 中最推荐的方法。通过这种可控的生成方式,你可以在需要时自动把模型插入 Gazebo Sim。
在 launch 文件夹中创建一个新文件 empty_world.launch.py,并写入相应内容。
在 Web Shell 3 中执行
touch /workspace/src/robot_description/launch/empty_world.launch.py
现在,把下面的代码复制到你刚刚创建的 launch 文件中。
empty_world.launch.py
import os
from ament_index_python.packages import (get_package_prefix, get_package_share_directory)
from launch import LaunchDescription
from launch.actions import (DeclareLaunchArgument, IncludeLaunchDescription)
from launch.substitutions import (PathJoinSubstitution, LaunchConfiguration)
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import SetParameter
# ROS2 Launch System will look for this function definition #
def generate_launch_description():
# Get Package Description and Directory #
package_description = "robot_description"
package_directory = get_package_share_directory(package_description)
# Set the Path to Robot Mesh Models for Loading in Gazebo Sim #
# NOTE: Do this BEFORE launching Gazebo Sim #
install_dir_path = (get_package_prefix(package_description) + "/share")
robot_meshes_path = os.path.join(package_directory, "meshes")
gazebo_resource_paths = [install_dir_path, robot_meshes_path]
if "IGN_GAZEBO_RESOURCE_PATH" in os.environ:
for resource_path in gazebo_resource_paths:
if resource_path not in os.environ["IGN_GAZEBO_RESOURCE_PATH"]:
os.environ["IGN_GAZEBO_RESOURCE_PATH"] += (':' + resource_path)
else:
os.environ["IGN_GAZEBO_RESOURCE_PATH"] = (':'.join(gazebo_resource_paths))
# Load Empty World SDF from Gazebo Sim Package #
world_file = "empty.sdf"
world_config = LaunchConfiguration("world")
declare_world_arg = DeclareLaunchArgument("world",
default_value=["-r ", world_file],
description="SDF World File")
# Declare GazeboSim Launch #
gzsim_pkg = get_package_share_directory("ros_gz_sim")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([gzsim_pkg, "launch", "gz_sim.launch.py"])),
launch_arguments={"gz_args": world_config}.items(),
)
# Create and Return the Launch Description Object #
return LaunchDescription(
[
declare_world_arg,
# Sets use_sim_time for all nodes started below (doesn't work for nodes started from ignition gazebo) #
SetParameter(name="use_sim_time", value=True),
gz_sim,
]
)
注意,在这个 launch 文件中我们使用的是环境变量 IGN_GAZEBO_RESOURCE_PATH,而不是 GZ_SIM_RESOURCE_PATH。为什么会这样?
原因在于,我们现在不再通过 gz sim <world_file> 这个命令直接启动 Gazebo Sim,而是通过 ROS 2 功能包 ros_gz_sim 提供的 gz_sim.launch.py 文件来启动它,如下所示:
gzsim_pkg = get_package_share_directory("ros_gz_sim")
gz_sim = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([gzsim_pkg, "launch", "gz_sim.launch.py"])),
launch_arguments={"gz_args": world_config}.items(),
)
换句话说,Gazebo Sim 现在是通过 ROS 2 启动的,而不是直接运行的。那么,这为什么重要呢?
这很重要,因为本课程使用的 ROS 2 发行版是 ROS 2 Humble,它与 Gazebo Sim Garden(也就是你前面一直在运行的版本)并不兼容。对于 ROS 2 Humble,受支持的版本是 Gazebo Sim Fortress,而它仍然沿用了 Ignition Gazebo 的一些约定。(如果这里有点混乱,可以回看 1.1 单元。)
因此,当你把 Gazebo Sim 和 ROS 2 Humble 配合使用时,仍然需要遵循一些 Ignition 风格的约定。没关系,后面凡是遇到这种情况,我们都会特别指出。
启动仿真:
在 Web Shell 1 中执行
cd /workspace
colcon build --packages-select robot_description && source install/setup.bash
ros2 launch robot_description empty_world.launch.py
你现在启动的是一个空世界。建议把生成机器人的逻辑放到单独的文件中,这样在开发过程中就可以不重启整个仿真,而是直接删除并重新生成机器人。
新建一个 spawn.launch.py 文件,并填入下面的内容:
spawn.launch.py
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import (Command, LaunchConfiguration)
from launch_ros.actions import (Node, SetParameter)
# ROS2 Launch System will look for this function definition #
def generate_launch_description():
# Get Package Description and Directory #
package_description = "robot_description"
package_directory = get_package_share_directory(package_description)
# Load URDF File #
urdf_file = 'robot.urdf'
robot_desc_path = os.path.join(package_directory, "urdf", urdf_file)
print("URDF Loaded !")
# Robot State Publisher (RSP) #
robot_state_publisher_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher_node',
output="screen",
emulate_tty=True,
parameters=[{'use_sim_time': True,
'robot_description': Command(['xacro ', robot_desc_path])}]
)
# Spawn the Robot #
declare_spawn_x = DeclareLaunchArgument("x", default_value="0.0",
description="Model Spawn X Axis Value")
declare_spawn_y = DeclareLaunchArgument("y", default_value="0.0",
description="Model Spawn Y Axis Value")
declare_spawn_z = DeclareLaunchArgument("z", default_value="0.5",
description="Model Spawn Z Axis Value")
gz_spawn_entity = Node(
package="ros_gz_sim",
executable="create",
name="my_robot_spawn",
arguments=[
"-name", "my_robot",
"-allow_renaming", "true",
"-topic", "robot_description",
"-x", LaunchConfiguration("x"),
"-y", LaunchConfiguration("y"),
"-z", LaunchConfiguration("z"),
],
output="screen",
)
# Create and Return the Launch Description Object #
return LaunchDescription(
[
# Sets use_sim_time for all nodes started below (doesn't work for nodes started from ignition gazebo) #
SetParameter(name="use_sim_time", value=True),
robot_state_publisher_node,
declare_spawn_x,
declare_spawn_y,
declare_spawn_z,
gz_spawn_entity,
]
)
这个 CLI 允许你传入三个参数:X、Y 和 Z。运行这个 ros_gz_sim/create 节点后,它会把 URDF 模型转换并插入当前正在运行的 Gazebo 仿真中。
生成机器人:
在 Web Shell 2 中执行
cd /workspace
colcon build --packages-select robot_description && source install/setup.bash
ros2 launch robot_description spawn.launch.py
结果应当类似下图:
| Gazebo Sim 中生成的机器人 | Gazebo Sim 中生成的机器人 - 局部放大 |
|---|---|
![]() |
![]() |
查看 Entity Tree 中的 my_robot 实体。URDF 文件中定义的所有连杆和关节都会显示出来,只有固定关节不会单独列出。机器人模型的详细信息会显示在 Component Inspector 中。对于 SDF 来说,这一步通常不是必须的,因为它会把相关连杆合并显示。

为 launch 文件新增一个参数,用来设置插入模型的名称。它需要把固定写死的 my_robot 名称替换为 CLI 中传入的名称。
完成后,用不同的名称把你创建的机器人生成两次,并检查 Entity Tree,确认功能是否正常。
预期结果是:执行下面的命令后,得到如下效果:
ros2 launch robot_description spawn.launch.py x:=5 y:=5 model_name:=my_robot_2

