ROS2 launch基本讲解
2026/7/11 20:07:37 网站建设 项目流程

launch 文件不是“机器人控制代码”,它是“启动脚本”:负责一次性启动多个 ROS 节点、传参数、选择真机/仿真、加载控制器。

1.最小的ROS2 lauch长什么样子

from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): node = Node( package="包名", executable="可执行程序名", name="节点名", output="screen", ) return LaunchDescripition([Node])

generate_Launch_description()是ROS2launch的入口函数

LaunchDescription(【】)里面放的是要执行的动作

Node()表示启动一个ROS节点

2.下面是我的一段工程代码,接下来我根据这份代码来进行讲解

import os import sys from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription from launch.conditions import IfCondition, UnlessCondition from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import ( Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution, PythonExpression, ) from launch_ros.actions import Node from launch_ros.parameter_descriptions import ParameterValue from launch_ros.substitutions import FindPackageShare sys.path.append(os.path.join(get_package_share_directory("tide_ctrl_bringup"), "launch")) from controller import engineer_controllers def generate_launch_description(): bringup_dir = get_package_share_directory("tide_ctrl_bringup") sim_mode = LaunchConfiguration("sim_mode") declare_sim_mode = DeclareLaunchArgument( "sim_mode", default_value="true", description="true: MuJoCo simulation, false: real robot via STM32 serial bridge", ) mujoco_headless = LaunchConfiguration("mujoco_headless") declare_mujoco_headless = DeclareLaunchArgument( "mujoco_headless", default_value="false", description="Run MuJoCo without visualization window if true", ) remote_control = LaunchConfiguration("remote_control") declare_remote_control = DeclareLaunchArgument( "remote_control", default_value="true", description="Start VT13 remote control mapper on the real robot", ) use_sim_time = LaunchConfiguration("use_sim_time") declare_use_sim_time = DeclareLaunchArgument( "use_sim_time", default_value=sim_mode, description="Use simulation clock if true", ) real_xacro_file = PathJoinSubstitution( [bringup_dir, "description", "engineer", "engineer_real.xacro"] ) mujoco_xacro_file = PathJoinSubstitution( [bringup_dir, "description", "engineer", "engineer_mujoco.xacro"] ) real_robot_description = ParameterValue( Command([FindExecutable(name="xacro"), " ", real_xacro_file]), value_type=str, ) mujoco_robot_description = ParameterValue( Command( [ FindExecutable(name="xacro"), " ", mujoco_xacro_file, " headless:=", mujoco_headless, ] ), value_type=str, ) real_controller_config = PathJoinSubstitution( [bringup_dir, "config", "engineer", "engineer_real.yaml"] ) mujoco_controller_config = PathJoinSubstitution( [bringup_dir, "config", "engineer", "engineer_mujoco.yaml"] ) remote_control_config = PathJoinSubstitution( [bringup_dir, "config", "engineer", "remote_control.yaml"] ) real_state_publisher = Node( package="robot_state_publisher", executable="robot_state_publisher", name="robot_state_publisher", output="screen", parameters=[ {"use_sim_time": use_sim_time, "robot_description": real_robot_description} ], condition=UnlessCondition(sim_mode), ) mujoco_state_publisher = Node( package="robot_state_publisher", executable="robot_state_publisher", name="robot_state_publisher", output="screen", parameters=[ {"use_sim_time": use_sim_time, "robot_description": mujoco_robot_description} ], condition=IfCondition(sim_mode), ) real_control_node = Node( package="controller_manager", executable="ros2_control_node", parameters=[ { "use_sim_time": use_sim_time, "robot_description": real_robot_description, }, real_controller_config, ], output="screen", condition=UnlessCondition(sim_mode), ) mujoco = IncludeLaunchDescription( PythonLaunchDescriptionSource( [ PathJoinSubstitution( [ FindPackageShare("tide_mujoco"), "launch", "mujoco_start.launch.py", ] ) ] ), launch_arguments={ "use_sim_time": use_sim_time, "controller_config": mujoco_controller_config, }.items(), condition=IfCondition(sim_mode), ) vt13_remote_control = Node( package="tide_hw_interface", executable="vt13_chassis_remote_node", name="vt13_chassis_remote_node", output="screen", parameters=[remote_control_config], condition=IfCondition( PythonExpression(["'", remote_control, "' == 'true' and '", sim_mode, "' == 'false'"]) ), ) return LaunchDescription( [ declare_sim_mode, declare_mujoco_headless, declare_remote_control, declare_use_sim_time, real_state_publisher, mujoco_state_publisher, real_control_node, mujoco, engineer_controllers(), vt13_remote_control, ] )

整体逻辑是:

读取 launch 参数
找到 xacro / yaml 配置文件
根据 sim_mode 判断启动真机还是仿真
启动 robot_state_publisher
启动 ros2_control_node 或 MuJoCo
启动 controller spawner
如果是真机且启用遥控,启动 VT13 遥控节点

最关键入口:

def generate_launch_description():

ROS执行

ros2 launch tide_ctrl_bringup tide_ctrl_bringup.launch.py

时会调用这个函数。

3.launch参数:运行时的开关

sim_mode = LaunchConfiguration("sim_mode")

意思是:我要读取一个叫sim_mode的launch参数

DeclareLaunchArgument(...)

意思是:声明这个参数,并给默认值。

可以这样启动仿真和真机:

ros2 launch tide_ctrl_bringup tide_ctrl_bringup.launch.py sim_mode:=true ros2 launch tide_ctrl_bringup tide_ctrl_bringup.launch.py sim_mode:=false

4.找文件路径:PathJoinSubstitution

real_controller_config = PathJoinSubstitution( [bringup_dir, "config", "engineer", "engineer_real.yaml"] )

本质等价于拼接路径

tide_ctrl_bringup/config/engineer/engineer_real.yaml

ROS launch 推荐用PathJoinSubstitution,因为它可以和 launch 参数、包路径一起动态解析。

5.xacro转robot_description

real_robot_description = ParameterValue( Command([FindExecutable(name="xacro"), " ", real_xacro_file]), value_type=str, )

意思是:执行xacro enginner_real.xacro

把生成结构作为字符串参数 robot_description

ROS里面很多节点都需要知道机器人模型,比如robot_state_publisher,ros2_control_node

它们都需要robot_description

所以后面会传进去:

parameters=[ {"use_sim_time": use_sim_time, "robot_description": real_robot_description} ]

这里的robot_description可以理解成机器人完整的urdf模型文本

6.启动节点

比如真机的robot_state_publisher:

real_state_publisher = Node( package="robot_state_publisher", executable="robot_state_publisher", name="robot_state_publisher", output="screen", parameters=[ {"use_sim_time": use_sim_time, "robot_description": real_robot_description} ], condition=UnlessCondition(sim_mode), )

package:节点来自哪个ROS包

executable:启动哪个可执行程序

name:节点名字

output="screen":日志打印到终端

parameters:传入ROS参数

conditon:满足条件才启动

condition=UnlessCondition(sim_mode)

这句话的意思是sim_mode为false时启动,所以这个是真机专用

7.真机和仿真的分支

真机路径:

real_state_publisher
real_control_node
vt13_remote_control
engineer_controllers()

仿真路径:

mujoco_state_publisher
mujoco
engineer_controllers()

engineer_controllers()目前是无条件执行的,他会在真机和仿真中都启动这些controller:

joint_state_broadcaster chassis_controller arm_controller gripper_controller

8.IncludeLaunchDescription:包含另一个launch

mujoco = IncludeLaunchDescription( PythonLaunchDescriptionSource( [ PathJoinSubstitution( [ FindPackageShare("tide_mujoco"), "launch", "mujoco_start.launch.py", ] ) ] ), launch_arguments={ "use_sim_time": use_sim_time, "controller_config": mujoco_controller_config, }.items(), condition=IfCondition(sim_mode), )

这段的意思是:

如果sim_mode=true,就去启动tide_mujoco包里的mujoco_start.laucnh.py

并传入两个参数进去:

"use_sim_time": use_sim_time
"controller_config": mujoco_controller_config

被包含的文件的接收方式是:

controller_config = LaunchConfiguration("controller_config") use_sim_time = LaunchConfiguration("use_sim_time")

9.controller.py

def _spawner(name): return Node( package="controller_manager", executable="spawner", arguments=[ name, "--controller-manager", "/controller_manager", ], )

这不是普通控制节点,而是执行命令

ros2 run controller_manager spawner chassis_controller --controller-manager /controller_manager

它的作用是:让controller_manager加载并启动controller

def engineer_controllers(): return GroupAction( [ _spawner("joint_state_broadcaster"), _spawner("chassis_controller"), _spawner("arm_controller"), _spawner("gripper_controller"), ] )

然后这里一次启动了四个

10.YAML和spawner的关系

YAML配置了controller

spawner启动了controller

例如

controller_manager: ros__parameters: chassis_controller: type: "mecanum_drive_controller/MecanumDriveController"

这只是告诉controller_manager:

我有一个 controller 叫 chassis_controller,它的类型是 MecanumDriveController

但它不会自动启动。

真正启动它的是:

_spawner("chassis_controller")

所以完整流程是:

ros2_control_node 读取 YAML controller_manager 知道有哪些 controller spawner 请求 controller_manager 启动 controller

11.以后需要新增一个节点

my_node = Node( package="my_package", executable="my_node", name="my_node", output="screen", parameters=[ {"use_sim_time": use_sim_time} ], )

然后一定要放进:

return LaunchDescription( [ ... my_node, ] )

否则你只是定义了变量,没有真正启动

12.以后需要新增一个YAML配置

模板:

my_config = PathJoinSubstitution( [bringup_dir, "config", "engineer", "my_config.yaml"] ) my_node = Node( package="my_package", executable="my_node", parameters=[my_config], )

对应 YAML 结构通常是:

my_node: ros__parameters: param_a: 1 param_b: "hello"

注意:最外层my_node要和节点名匹配。

remote_control.yaml就是这样:

vt13_chassis_remote_node: ros__parameters: joy_topic: "/astra/remote/joy"

因为 launch 里节点名是:

name="vt13_chassis_remote_node"

13. 你以后要新增一个 controller

分三步。

第一步,在 YAML 里声明 controller:

controller_manager: ros__parameters: my_controller: type: "some_controller/SomeController"

第二步,给它写参数:

my_controller: ros__parameters: some_param: 123

第三步,在controller.py里加 spawner:

_spawner("my_controller")

这就是 controller 的标准套路

结语:

第一次写这么长的东西,本人目前也还在学习当中,如果有哪里讲的不对,还请各位大佬们指出

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询