Skip to content

MoveIt 中的启动文件

MoveIt 的许多教程以及你实际遇到的 MoveIt 包都会使用 ROS 2 启动文件。

本教程详细讲解一个典型的 Python 启动文件,该文件搭建了一个可运行的 MoveIt 示例。我们将通过逐步分析 入门教程启动文件 来说明。

如果你对启动文件本身不熟悉,请先参阅 ROS 2 文档。

MoveIt 需要若干配置参数,包括机器人描述和语义描述文件(URDF 与 SRDF)、运动规划和运动学插件、轨迹执行等。这些参数通常包含在 MoveIt 配置 包中。

在 Python 启动文件中引用 MoveIt 配置包的一种便捷方式是使用 MoveItConfigsBuilder 工具,如下所示:

from moveit_configs_utils import MoveItConfigsBuilder
# Define xacro mappings for the robot description file
launch_arguments = {
"robot_ip": "xxx.yyy.zzz.www",
"use_fake_hardware": "true",
"gripper": "robotiq_2f_85",
"dof": "7",
}
# Load the robot configuration
moveit_config = (
MoveItConfigsBuilder(
"gen3", package_name="kinova_gen3_7dof_robotiq_2f_85_moveit_config"
)
.robot_description(mappings=launch_arguments)
.trajectory_execution(file_path="config/moveit_controllers.yaml")
.planning_scene_monitor(
publish_robot_description=True, publish_robot_description_semantic=True
)
.planning_pipelines(
pipelines=["ompl", "stomp", "pilz_industrial_motion_planner"]
)
.to_moveit_configs()
)

所有 MoveIt 配置参数加载完毕后,你就可以使用这套参数启动 Move Group 接口。

from launch_ros.actions import Node
run_move_group_node = Node(
package="moveit_ros_move_group",
executable="move_group",
output="screen",
parameters=[moveit_config.to_dict()],
)

正如 RViz 快速入门 教程中所介绍的,你可以使用 RViz 可视化机器人模型并执行运动规划任务。

以下代码通过一个启动参数接收 RViz 配置文件名,将其拼接为指向已知包目录的相对路径,并在启动 RViz 可执行文件时作为参数传入。

from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
# Get the path to the RViz configuration file
rviz_config_arg = DeclareLaunchArgument(
"rviz_config",
default_value="kinova_moveit_config_demo.rviz",
description="RViz configuration file",
)
rviz_base = LaunchConfiguration("rviz_config")
rviz_config = PathJoinSubstitution(
[FindPackageShare("moveit2_tutorials"), "launch", rviz_base]
)
# Launch RViz
rviz_node = Node(
package="rviz2",
executable="rviz2",
output="log",
arguments=["-d", rviz_config],
parameters=[
moveit_config.robot_description,
moveit_config.robot_description_semantic,
moveit_config.robot_description_kinematics,
moveit_config.planning_pipelines,
moveit_config.joint_limits,
],
)

ROS 生态中的许多工具使用 tf2 库来表示坐标变换,这是使用 MoveIt 进行运动规划的重要组成部分。

因此,我们的启动文件包含向 tf2 发布静态变换和动态变换的节点。在本例中,我们需要:

  • 在 world 坐标系与机器人描述的基坐标系 base_link 之间发布一个静态变换。
  • 一个 robot state publisher 节点,它监听机器人的关节状态,利用机器人的 URDF 模型计算坐标系变换,并将其发布到 tf2。
# Static TF
static_tf = Node(
package="tf2_ros",
executable="static_transform_publisher",
name="static_transform_publisher",
output="log",
arguments=["--frame-id", "world", "--child-frame-id", "base_link"],
)
# Publish TF
robot_state_publisher = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
name="robot_state_publisher",
output="both",
parameters=[moveit_config.robot_description],
)

MoveIt 通常会生成关节轨迹,然后将其发送给能够执行这些轨迹的机器人控制器。最常见的做法是连接 ros2_control 库来实现这一点。

ros2_control 既可以连接真实机器人硬件,也可以连接 Gazebo、NVIDIA Isaac Sim 等基于物理的模拟器中的机器人。此外,它还提供了 mock components 功能,用于简单、理想化的模拟。在本例中,这是在 URDF 层面通过前面定义的 use_fake_hardware xacro 参数来配置的。核心思想是:无论启动的是模拟硬件还是真实硬件,ros2_control 的启动方式都保持不变。

启动 ros2_control 需要启动一个控制器管理器节点,然后生成轨迹执行所需的一系列控制器。在本例中,我们包含:

  • 一个关节状态广播器(joint state broadcaster),它发布关节状态,这是机器人状态发布器向 tf2 发送坐标系所必需的。
  • 一个用于机械臂执行器的关节轨迹控制器(joint trajectory controller)。
  • 一个用于平行爪式夹爪的夹爪动作控制器(gripper action controller)。
ros2_controllers_path = os.path.join(
get_package_share_directory("kinova_gen3_7dof_robotiq_2f_85_moveit_config"),
"config",
"ros2_controllers.yaml",
)
ros2_control_node = Node(
package="controller_manager",
executable="ros2_control_node",
parameters=[ros2_controllers_path],
remappings=[
("/controller_manager/robot_description", "/robot_description"),
],
output="both",
)
joint_state_broadcaster_spawner = Node(
package="controller_manager",
executable="spawner",
arguments=[
"joint_state_broadcaster",
"--controller-manager",
"/controller_manager",
],
)
arm_controller_spawner = Node(
package="controller_manager",
executable="spawner",
arguments=["joint_trajectory_controller", "-c", "/controller_manager"],
)
hand_controller_spawner = Node(
package="controller_manager",
executable="spawner",
arguments=["robotiq_gripper_controller", "-c", "/controller_manager"],
)

最后,我们可以告诉启动文件实际启动前面各节中描述的所有内容。

# ... all our imports go here
def generate_launch_description():
# ... all our other code goes here
return LaunchDescription(
[
rviz_config_arg,
rviz_node,
static_tf,
robot_state_publisher,
run_move_group_node,
ros2_control_node,
joint_state_broadcaster_spawner,
arm_controller_spawner,
hand_controller_spawner,
]
)