

我的整体学习流程就是从启动仿真环境、MoveIt配置和视觉模块开始逐步介绍。
代码的第一步就是执行:ros2 launch ur_bringup simulation.launch.py,所以从simulation.launch.py开始进行讲解。
在ROS 2中,launch文件用于启动和配置多个节点、参数、服务等。它们帮助我们自动化复杂的系统启动过程。
执行launch文件的工作流程#
- 当执行 ros2 launch ur_bringup simulation.launch.py 命令后,ROS 2 launch系统会寻找并执行 generate_launch_description() 函数,这是每个launch文件的入口点。
def generate_launch_description():
declared_arguments = []
return LaunchDescription(declared_arguments + [OpaqueFunction(function=launch_setup)])pythondeclared_arguments = [],这里创建了一个空列表,用于存储launch文件可以接受的参数。在这个例子中,没有声明任何参数,所以列表是空的。如果需要添加参数,可以使用DeclareLaunchArgument。
return LaunchDescription(declared_arguments + [OpaqueFunction(function=launch_setup)]),这行代码有几个重要部分:
-
LaunchDescription:这是ROS 2 launch系统的核心类,它描述了要启动什么。它接受一个动作(actions)列表作为参数。
-
declared_arguments:这是我们之前创建的参数列表(虽然现在是空的)。
-
OpaqueFunction(function=launch_setup):这是一个特殊类型的action,它允许我们在launch过程中执行自定义的Python函数。
-
launch_setup是我们自定义的函数,它包含了实际要执行的launch逻辑
-
使用OpaqueFunction的好处是我们可以在函数中编写复杂的逻辑,而不仅仅是在顶层写静态的launch描述,详细理解参考博客 ↗
generate_launch_description函数的工作流程#
当运行ros2 launch ur_bringup simulation.launch.py时,以下步骤会依次发生:
- ROS 2找到ur_bringup包下的simulation.launch.py并执行generate_launch_description()函数
- generate_launch_description()返回一个LaunchDescription对象,其中包含一个OpaqueFunction
- ROS 2执行OpaqueFunction,这会导致调用launch_setup函数
- launch_setup函数执行实际的launch逻辑,定义要启动的所有节点和配置
补充一下什么是动作列表:
-
ROS 2 Launch系统中的动作(Action) 在ROS 2的launch系统中,“动作列表”(actions list)是指一系列要执行的操作或任务。这些操作可以是启动节点、设置参数、执行命令等。每个动作都是一个对象,描述了要执行的具体任务。
-
动作(Action)是什么? 动作是ROS 2 launch系统的基本构建块。每个动作代表一个特定的操作,例如
- Node - 启动一个节点
- IncludeLaunchDescription - 包含并执行另一个launch文件
- DeclareLaunchArgument - 声明一个参数
- SetParameter - 设置参数值
- ExecuteProcess - 执行一个进程
- LogInfo - 输出日志信息
- OpaqueFunction - 执行自定义Python函数
- TimerAction - 延迟执行动作
- RegisterEventHandler - 注册事件处理器
launch_setup函数的工作流程#
首先查看launch_setup函数代码:
def launch_setup(context, *args, **kwargs):
"""
启动设置函数,用于配置和启动机器人仿真环境
该函数整合了多个子系统,包括机器人控制、MoveIt运动规划、
深度图像处理、视觉检测和八叉树地图构建等模块
参数:
context: 启动上下文信息
*args: 额外的位置参数
**kwargs: 额外的关键字参数
返回:
list: 包含所有待启动节点的列表
"""
print(f"\033[95mcontext:{context}\033[0m")
## 加载URDF模型并启动Gazebo仿真环境
dual_ur5e_gripper_control_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[FindPackageShare("ur5e_gripper_moveit_config"), "/launch", "/ur5e_gripper_sim_control.launch.py"]
),
launch_arguments={
"launch_rviz": "true",
}.items(),
)
## 加载MoveIt配置
dual_ur5e_gripper_moveit_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[FindPackageShare("ur5e_gripper_moveit_config"), "/launch", "/ur5e_gripper_moveit.launch.py"]
),
launch_arguments={
"use_sim_time": "true",
}.items(),
)
## 注册深度图像(与彩色图像对齐)
register_depth_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[FindPackageShare("vision"), "/launch", "/register_depth.launch.py"]
),
)
## 启动视觉处理模块
vision_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[FindPackageShare("vision"), "/launch", "/seg_and_det.launch.py"]
),
)
## 八叉树地图构建模块(当前被注释掉)
octo_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
[FindPackageShare("octo_bringup"), "/launch", "/octomap.launch.py"]
),
)
## 组装所有需要启动的节点
nodes_to_launch = [
dual_ur5e_gripper_control_launch,
dual_ur5e_gripper_moveit_launch,
register_depth_launch,
vision_launch,
## octo_launch,
]
print(f"\033[95mnodes_to_launch:{nodes_to_launch}\033[0m")
return nodes_to_launchpython通过执行代码打印信息如下:
补充解释一下IncludeLaunchDescription和PythonLaunchDescriptionSource:
- IncludeLaunchDescription:允许你在一个launch文件中嵌入或调用另一个launch文件
- PythonLaunchDescriptionSource:是IncludeLaunchDescription的一个来源类,它指定所包含的launch文件是Python格式的
- launch_arguments:如果被包含的launch文件有定义可选参数(使用DeclareLaunchArgument),你可以通过 launch_arguments 字典传递值。 .items() 是将字典转换为列表的键值对,用于满足函数要求。
- 好处是支持模块化、代码复用、参数传递,适用于复杂项目中的启动管理。详解见博客 ↗
总结#
总体流程如下:执行ros2 launch ur_bringup simulation.launch.py->找到simulation.launch.py的generate_launch_description函数并执行->通过OpaqueFunction执行自定义函数launch_setup(该函数返回动作列表)->launch_setup函数调用其他的launch文件。