ROS 2 的 launch 文件
ROS 2 的 launch 文件
ROS 2 的 Launch 文件支持 Python(.launch.py)、XML(.launch.xml) 和 YAML(.launch.yaml) 三种格式。其中,Python 格式是官方和社区的首选,因为它提供了无与伦比的灵活性。
- 为什么首选 Python?
- 灵活性高:可以使用完整的 Python 逻辑,如条件判断、循环、函数调用,实现动态和复杂的启动逻辑。
- 社区标准:已成为 ROS 2 社区的事实标准,资源和示例最丰富。
- 功能强大:能适配从简单到复杂的所有项目场景。
注意:XML 和 YAML 格式主要用于极简单的场景,在复杂项目中不推荐使用。
一、launch 文件的核心概念
- launch 文件是纯 Python 脚本:无需编译,
ros2 launch直接解释执行。 generate_launch_description():每个.launch.py都必须定义这个函数,它是 launch 文件的入口点,返回值是一个LaunchDescription对象。LaunchDescription:一个动作(Actions)列表,定义了要启动的节点、要设置的环境变量、要执行的进程等。- Action(动作):
LaunchDescription里的每个元素。最常用的是Node(启动一个节点),此外还有DeclareLaunchArgument(声明启动参数)、ExecuteProcess(执行任意命令)等。
基本骨架如下:
from launch import LaunchDescription
def generate_launch_description():
# 在这里定义你的动作 (Actions)
return LaunchDescription([
# 动作列表,例如启动一个节点
])
二、基础启动 — 启动单个节点
2.1 写法 A:完整限定名
使用 import launch_ros.actions,调用时写全名 launch_ros.actions.Node。
# import 方式一:import launch_ros.actions,使用时写全名 launch_ros.actions.Node
from launch import LaunchDescription
import launch_ros.actions
def generate_launch_description():
# 在这里定义你的动作 (Actions)
return LaunchDescription([
# 动作列表,例如启动一个节点
# --- 最小示例:只指定 package + executable 两个必填字段 ---
launch_ros.actions.Node(
# ---- 必须的字段 ----
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
),
])
2.2 写法 B:直接导入 Node 类(推荐)
# import 方式二(推荐):from ... import Node,使用时直接写 Node
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
# 在这里定义你的动作 (Actions)
return LaunchDescription([
# 动作列表,例如启动一个节点
# --- 最小示例:只指定 package + executable ---
Node(
# ---- 必须的字段 ----
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
)
])
与写法 A 效果完全一样,只是代码更简洁、更符合规范。两种写法二选一即可,本文其余章节均采用写法 B。
保存与运行:把上面的内容(写法 A 或 B 任选其一)保存为
~/ros2_launch_demo/01_basic.launch.py,然后在终端运行:ros2 launch ~/ros2_launch_demo/01_basic.launch.py运行后会弹出一个 turtlesim 窗口。本文的示例文件统一放在
~/ros2_launch_demo/目录下,都可以直接复制粘贴保存后运行。
三、完整节点配置 — 展示所有常用字段
本节把所有字段堆在一起展示,实际使用时可按需删减。
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
# 在这里定义你的动作 (Actions)
return LaunchDescription([
# 动作列表,例如启动一个节点
Node(
# ---- 必须的字段 ----
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
# ---- 可选字段 ----
name='my_turtle', # 给节点改名(不指定则默认用 executable 的名字)
namespace='ns1', # 放入命名空间 → 节点全名变成 /ns1/my_turtle
# 话题也随之变成 /ns1/...,用于多实例隔离
output='screen', # 将 stdout/stderr 打印到终端(也可选 'log' 或 'both')
# ---- 设置 ROS 参数 ----
# 参数会在节点启动时加载,可用 ros2 param list 查看
# 数字会按 YAML 规则处理:字符串数字(如 '255')会自动转成 int
parameters=[{
'background_r': 255, # 背景色 R 通道 (0-255)
'background_g': 165, # 背景色 G 通道 (0-255)
'background_b': 0, # 背景色 B 通道 (0-255)
}],
# ---- 话题重映射 (Remapping) ----
# 每个元素是 (旧话题, 新话题),相当于把 /cmd_vel "改名为" /turtle1/cmd_vel
remappings=[
('/cmd_vel', '/turtle1/cmd_vel'),
],
# ---- 额外 ROS 参数(透传给 --ros-args) ----
# arguments 里的内容会"原样"拼接到节点命令后面(等价于 ros2 run 之后
# 再加的参数),必须以 --ros-args 开头,用来控制 ROS 2 运行时的行为。
# 常见用法:
# --log-level INFO 设置日志级别 (DEBUG/INFO/WARN/ERROR/FATAL)
# --params-file config.yaml 从 YAML 文件加载参数(等价于 parameters 字段)
# -p key:=value 直接设置单条参数
# -r 旧话题:=新话题 话题重映射(等价于 remappings 字段)
# 注意:parameters / remappings 字段本质上是这些 --ros-args 的
# "结构化便捷写法",最终都会被转换成 --ros-args 透传给节点。
arguments=['--ros-args', '--log-level', 'INFO'],
),
])
保存与运行:保存为
~/ros2_launch_demo/02_full.launch.py,运行:ros2 launch ~/ros2_launch_demo/02_full.launch.py这次节点名变成了
/ns1/my_turtle(命名空间 + 改名生效),背景色是橙色。
四、多节点启动 + 命名空间隔离
规则:
- 同一
package+ 同一executable+ 同一name→ 需要不同namespace- 同一
package+ 同一executable+ 同一namespace→ 需要不同name
4.1 写法 A:节点直接内联在 LaunchDescription 列表中
from launch import LaunchDescription
import launch_ros.actions # 写法 A:import 包,使用时写全名 launch_ros.actions.Node
def generate_launch_description():
return LaunchDescription([
# ---- 第一个节点:放入 turtlesim1 命名空间 ----
launch_ros.actions.Node(
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
namespace='turtlesim1', # 节点全名变成 /turtlesim1/turtle1
# 通过 namespace 隔离,两个同类型节点可以共存
name='turtle1', # 节点名
parameters=[{'background_r': 255, 'background_g': 165, 'background_b': 0}], # 设置背景色参数
),
# ---- 第二个节点:放入 turtlesim2 命名空间 ----
launch_ros.actions.Node(
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名
namespace='turtlesim2', # 与第一个节点同名,靠不同 namespace 区分
name='turtle1', # 节点名(与第一个节点相同)
parameters=[{'background_r': 0, 'background_g': 255, 'background_b': 100}], # 设置背景色参数
),
])
4.2 写法 B:先把每个节点赋给变量,再放入列表(便于复用/修改)
from launch import LaunchDescription
from launch_ros.actions import Node # 写法 B:直接导入 Node 类,使用时写 Node
def generate_launch_description():
# 先把每个节点赋给变量,再统一放入列表(便于复用/修改)
turtlesim1_node = Node( # 第一个节点:放入 turtlesim1 命名空间
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
namespace='turtlesim1', # 节点全名变成 /turtlesim1/turtle1
# 通过 namespace 隔离,两个同类型节点可以共存
name='turtle1', # 节点名
parameters=[{'background_r': 255, 'background_g': 165, 'background_b': 0}], # 设置背景色参数
)
turtlesim2_node = Node( # 第二个节点:放入 turtlesim2 命名空间
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名
namespace='turtlesim2', # 与第一个节点同名,靠不同 namespace 区分
name='turtle1', # 节点名(与第一个节点相同)
parameters=[{'background_r': 0, 'background_g': 255, 'background_b': 100}], # 设置背景色参数
)
return LaunchDescription([
turtlesim1_node, # 组装到 LaunchDescription
turtlesim2_node
])
保存与运行:保存为
~/ros2_launch_demo/03_multi.launch.py(写法 A、B 任选其一),运行:ros2 launch ~/ros2_launch_demo/03_multi.launch.py会同时弹出两个 turtlesim 窗口,分别对应
/turtlesim1/turtle1和/turtlesim2/turtle1。
五、节点参数配置 — 命令行传参与 YAML 参数文件
通过 DeclareLaunchArgument 声明可配置参数,再用 LaunchConfiguration 获取其值,即可在启动时动态传入参数:
ros2 launch ~/ros2_launch_demo/04_param_single.launch.py background_r:=200
5.1 单参数版
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument # 定义可配置参数
from launch.substitutions import LaunchConfiguration # 参数获取
def generate_launch_description():
# 1. 声明一个名为 background_r 的启动参数
background_r_arg = DeclareLaunchArgument(
'background_r', # 参数名称(必需)
default_value='150', # 默认值(可选,若不提供则用户必须传入,且必须是字符串)
description='背景色 R 通道 (0-255)' # 描述信息(可选,用于 --show-args)
)
# 2. 获取参数的实际值(占位符)
background_r = LaunchConfiguration('background_r')
# 3. 定义 turtlesim 节点,使用 background_r 参数
turtlesim_node = Node(
namespace='ns1', # 放入指定的命名空间(可选)
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 要运行的可执行文件名
name='my_turtle', # 给这个节点起一个新名字(可选)
output='screen', # 将日志打印到屏幕(而不是日志文件)
parameters=[{'background_r': background_r}], # 设置参数
)
# 4. 组装到 LaunchDescription
return LaunchDescription([
background_r_arg,
turtlesim_node
])
保存与运行:保存为
~/ros2_launch_demo/04_param_single.launch.py,运行(可自定义背景色):ros2 launch ~/ros2_launch_demo/04_param_single.launch.py background_r:=200
5.2 多参数版(写法更紧凑)
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument # 定义可配置参数
from launch.substitutions import LaunchConfiguration # 参数获取
def generate_launch_description():
"""支持命令行传参的写法,例如:
ros2 launch ~/ros2_launch_demo/04_param.launch.py background_r:=200 background_g:=100 background_b:=50
"""
# 1. 声明有哪些启动参数及其默认值
declare_bg_r = DeclareLaunchArgument('background_r', default_value='150')
declare_bg_g = DeclareLaunchArgument('background_g', default_value='86')
declare_bg_b = DeclareLaunchArgument('background_b', default_value='255')
# 2. 用 LaunchConfiguration 获取占位符(此时值还未确定,运行时才替换)
bg_r = LaunchConfiguration('background_r')
bg_g = LaunchConfiguration('background_g')
bg_b = LaunchConfiguration('background_b')
# 3. 传给节点
node = Node(
package='turtlesim',
executable='turtlesim_node',
parameters=[{
'background_r': bg_r,
'background_g': bg_g,
'background_b': bg_b,
}],
)
# 4. 注意:LaunchDescription 中必须先列出 DeclareLaunchArgument,再列出 Node
return LaunchDescription([
declare_bg_r,
declare_bg_g,
declare_bg_b,
node,
])
保存与运行:保存为
~/ros2_launch_demo/04_param.launch.py,运行(多参数版):ros2 launch ~/ros2_launch_demo/04_param.launch.py background_r:=200 background_g:=100 background_b:=50
5.3 从 YAML 参数文件加载(参数多时的最佳实践)
前面两小节是通过命令行传参。但当参数很多(比如几十上百个)时,每次都写命令行很痛苦。更常见的做法是:把参数集中写在一个 YAML 参数文件里,让节点启动时直接加载。
第一步:准备 YAML 参数文件 ~/ros2_launch_demo/config/turtlesim.yaml:
# config/turtlesim.yaml
# ROS 2 参数文件的格式:最外层是"节点全名",下面固定是 ros__parameters 键
/turtlesim2/sim: # 节点全名 = 命名空间/节点名(必须与 launch 里节点一致)
ros__parameters: # 固定写法:所有参数都放在这个键下面
background_b: 255 # 背景色 B 通道 (0-255)
background_g: 86 # 背景色 G 通道 (0-255)
background_r: 150 # 背景色 R 通道 (0-255)
参数文件的顶层键是节点全名。上例对应一个
namespace='turtlesim2'、name='sim'的节点(全名/turtlesim2/sim)。
第二步:launch 文件 ~/ros2_launch_demo/05_yaml.launch.py:
# 05_yaml.launch.py
# 作用:演示从 YAML 参数文件加载参数(parameters 里放文件路径,而不是字典)
import os # 用于拼接文件路径
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
# 1. 定位 YAML 参数文件的绝对路径
# os.path.dirname(__file__) 返回当前文件所在目录(本文件与 config/ 同目录)
config = os.path.join(
os.path.dirname(__file__), # 当前 launch 文件所在目录
'config', # config 子目录
'turtlesim.yaml' # 参数文件名
)
# 2. 启动节点,parameters 直接放"文件路径"(字符串)
# 节点名(namespace + name)必须与 YAML 顶层键 /turtlesim2/sim 一致
return LaunchDescription([
Node(
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名
namespace='turtlesim2', # 命名空间(与 YAML 顶层键对应)
name='sim', # 节点名(与 YAML 顶层键对应)
parameters=[config] # 从 YAML 文件加载参数(放路径而非字典)
),
])
保存与运行:先把上面的 YAML 保存为
~/ros2_launch_demo/config/turtlesim.yaml,再把 launch 保存为~/ros2_launch_demo/05_yaml.launch.py,运行:ros2 launch ~/ros2_launch_demo/05_yaml.launch.py运行后可用以下命令确认参数确实从 YAML 加载成功:
ros2 param get /turtlesim2/sim background_r # 应输出 150(来自 YAML 文件) ros2 param list /turtlesim2/sim # 列出该节点所有参数
关键点说明:
parameters里放字典{...}→ 表示”直接设置参数”;放字符串路径 → 表示”从 YAML 文件加载”。两者可以混用:parameters=[config, {'background_r': 0}] # 先加载 YAML,再用字典覆盖其中一个- YAML 的顶层键(节点全名)必须和 launch 里节点的命名空间 + 节点名完全一致,否则参数不会生效(ROS 会报”没有找到该节点对应的参数”)。
- 如果节点没设置
namespace/name,则全名就是/executable名,YAML 顶层键要写成/turtlesim_node:。
常见坑:
- 节点名对不上:YAML 顶层键写的是
/turtlesim2/sim,但 launch 里节点没写namespace='turtlesim2'和name='sim'→ 参数静默不加载。用ros2 param list检查是否为空即可发现。- 功能包里的 YAML 忘了安装:如果把 YAML 放进功能包并用
get_package_share_directory()定位(生产环境推荐),记得像第九节那样在setup.py/CMakeLists.txt里安装 config 目录,否则启动时报”参数文件不存在”。本功能包的setup.py已有glob('config/*.yaml')安装规则。- 生产环境定位方式:示例用
os.path.dirname(__file__)方便快速测试;功能包里应改用:from ament_index_python.packages import get_package_share_directory config = os.path.join( get_package_share_directory('launch_tutorial'), 'config', 'turtlesim.yaml')
三种设置节点参数的方式对比:
方式 写法 适用场景 命令行传参(5.1 / 5.2 节) DeclareLaunchArgument+parameters=[{'key': LaunchConfiguration('key')}]参数少、需要每次启动临时改 直接写死 parameters=[{'key': value}]参数少且固定不变 YAML 参数文件(本节) parameters=[文件路径]参数多、需集中管理
六、条件启动 (Conditional Launch)
使用 IfCondition 可以根据命令行参数值决定是否启动某个节点。
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.conditions import IfCondition # 条件启动
from launch.substitutions import LaunchConfiguration # 获取命令行参数值
def generate_launch_description():
# 读取命令行参数 launch_turtlesim(默认 true),如:ros2 launch 05_condition.launch.py launch_turtlesim1:=false
launch_turtlesim1 = LaunchConfiguration('launch_turtlesim1', default='true')
launch_turtlesim2 = LaunchConfiguration('launch_turtlesim2', default='true')
return LaunchDescription([
# ---- 第一个节点 ----
Node(
package='turtlesim',
executable='turtlesim_node',
name='turtle1', # 节点改名为 turtle1
# IfCondition 会把 "true"/"1" 视为真、"false"/"0" 视为假,直接传参即可
condition=IfCondition(launch_turtlesim1),
),
# ---- 第二个节点(改名为 turtle2,与第一个节点区分开)----
Node(
package='turtlesim',
executable='turtlesim_node',
name='turtle2', # 节点改名为 turtle2
condition=IfCondition(launch_turtlesim2),
),
])
保存与运行:保存为
~/ros2_launch_demo/05_condition.launch.py,运行:# 默认启动两个乌龟 ros2 launch ~/ros2_launch_demo/05_condition.launch.py # 只启动 turtle1,不启动 turtle2 ros2 launch ~/ros2_launch_demo/05_condition.launch.py launch_turtlesim2:=false
七、运行 launch 文件
# 基本运行(以第五节的多参数版为例)
ros2 launch ~/ros2_launch_demo/04_param.launch.py
# 覆盖启动参数(对应第五节)
ros2 launch ~/ros2_launch_demo/04_param.launch.py background_r:=200
# 查看可用的启动参数及其默认值/描述
ros2 launch ~/ros2_launch_demo/04_param.launch.py --show-args
八、常用字段速查表
| 字段 | 说明 | 示例 |
|---|---|---|
package |
功能包名(必填) | 'turtlesim' |
executable |
可执行文件名(必填) | 'turtlesim_node' |
name |
节点名(重命名) | 'my_node' → /ns1/my_node |
namespace |
命名空间(隔离多实例) | 'ns1' |
parameters |
参数列表 | [{'key': value}] |
remappings |
话题重映射 | [('/旧', '/新')] |
arguments |
透传给节点的额外 ROS 命令行参数(需以 --ros-args 开头,如设置日志级别/加载参数文件) |
['--ros-args', '--log-level', 'DEBUG'] |
output |
日志输出位置 | 'screen' | 'log' | 'both' |
condition |
条件启动 | IfCondition(LaunchConfiguration(...)) |
respawn |
节点退出后自动重启 | True | False |
respawn_delay |
重启前的等待秒数 | 3.0 |
关于
executable名字的来源:executable填的是节点程序的可执行文件名,它取决于节点包的类型:
- C++ 包(
ament_cmake):在CMakeLists.txt中用add_executable()定义并安装;- Python 包(
ament_python):在setup.py的console_scripts中定义入口点。本文统一使用 turtlesim 包自带的 C++ 节点程序
turtlesim_node,所以直接填'turtlesim_node'即可。
九、如何让功能包安装 launch 文件
上面的例子都是在某个已有的功能包里写 launch 文件。如果你在自己的 Python 功能包(ament_python)里放了 launch 文件,还需要在 setup.py 里声明安装规则,否则 ros2 launch 会报”文件找不到”(launch 文件没有被复制进 install/ 目录)。
在 setup.py 中增加以下配置(这是最关键的一行):
import os # 用于拼接安装路径
from glob import glob # 用于匹配 launch 目录下的文件
from setuptools import setup # 打包配置
package_name = 'my_package' # 功能包名(与目录名一致)
setup(
# 其他配置参数 ...
data_files=[
# 其他需要安装的数据文件 ...
# 安装所有 launch 文件(最关键的一行)
(os.path.join('share', package_name), glob('launch/*.launch.py'))
]
)
要点说明:
glob('launch/*.launch.py'):把launch/目录下所有以.launch.py结尾的文件收集起来(注意是点.launch,不是下划线_launch)。os.path.join('share', package_name):安装目标路径,即share/<包名>/下,ros2 launch会去那里查找 launch 文件。- 配置好后必须重新构建并 source 工作区:
cd ~/ros_ws
colcon build --packages-select my_package
source install/setup.bash
ros2 launch my_package my_launch.launch.py
9.1 C++ 包(ament_cmake)的调整
如果你用的是 C++ 功能包(构建类型为 ament_cmake),则在 CMakeLists.txt 中通过 install() 命令把 launch/ 目录整体安装到 share/<包名>/ 下。在文件末尾、ament_package() 之前加上:
# 安装 launch 目录到 share/<包名>/launch
install(DIRECTORY launch
DESTINATION share/${PROJECT_NAME}
)
也可以用 install(FILES ...) 逐个指定(launch 文件不多时更清晰):
install(FILES
launch/my_launch.launch.py
DESTINATION share/${PROJECT_NAME}/launch
)
要点说明:
DESTINATION share/${PROJECT_NAME}:安装目标路径,${PROJECT_NAME}会自动替换成包名(即project(...)里定义的名字),与 Python 包的os.path.join('share', package_name)等价。install(DIRECTORY launch ...)会把整个 launch 目录(含里面的所有文件)复制过去,是最省事的方式;install(FILES ...)则只装你列出的那几份。- 同样,改完
CMakeLists.txt后要重新构建:
cd ~/ros_ws
colcon build --packages-select my_package
source install/setup.bash
ros2 launch my_package my_launch.launch.py
小结:无论 Python 还是 C++ 包,本质都是把
launch/*.launch.py装进install/<包名>/share/<包名>/launch/。Python 包在setup.py的data_files里配置,C++ 包在CMakeLists.txt的install()里配置。
常见坑:
- 文件名命名:launch 文件建议统一命名为
xxx.launch.py(带点)。如果写成xxx_launch.py(下划线),glob('launch/*.launch.py')匹配不到,文件不会被安装,ros2 launch同样报”文件找不到”。- 忘了重新构建:修改
setup.py后要重新colcon build,否则install/里还是旧内容。- 忘了
package.xml依赖:如果 launch 文件里用了launch_ros.actions.Node,记得在package.xml加<exec_depend>ros2launch</exec_depend>。
十、一个 launch 文件调用另一个 launch 文件并传递参数
实际项目中,一个 launch 文件常常需要调用另一个 launch 文件(例如先启动公共的机器人驱动、传感器驱动),并且把当前文件的参数传递给它。这用 IncludeLaunchDescription + launch_arguments 就能实现。
下面用一个完整例子演示:父 launch 文件 调用 子 launch 文件,并把背景色参数传过去。两个文件都放在同一个目录(如 ~/ros2_launch_demo/)下。
10.1 子 launch 文件:声明参数并使用
先写子文件 child.launch.py:它声明一组参数,并用 LaunchConfiguration 把参数值应用到 turtlesim 节点上。
# 子 launch 文件:child.launch.py
# 作用:声明一组参数,并用 LaunchConfiguration 把参数值传给节点
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument # 定义可配置参数
from launch.substitutions import LaunchConfiguration # 获取参数值
from launch_ros.actions import Node
def generate_launch_description():
# 1. 声明三个可配置参数,并给出默认值
declare_bg_r = DeclareLaunchArgument(
'background_r', # 参数名称
default_value='150', # 默认值(必须是字符串)
description='背景色 R 通道 (0-255)', # 描述信息
)
declare_bg_g = DeclareLaunchArgument('background_g', default_value='86')
declare_bg_b = DeclareLaunchArgument('background_b', default_value='255')
# 2. 用 LaunchConfiguration 获取参数的实际值(占位符,运行时才替换)
bg_r = LaunchConfiguration('background_r')
bg_g = LaunchConfiguration('background_g')
bg_b = LaunchConfiguration('background_b')
# 3. 把参数值应用到 turtlesim 节点上
turtlesim_node = Node(
package='turtlesim', # 节点所在的功能包名
executable='turtlesim_node', # 可执行文件名(这里直接用 turtlesim 包自带的节点程序)
parameters=[{
'background_r': bg_r, # 使用上面获取的参数占位符
'background_g': bg_g,
'background_b': bg_b,
}],
)
# 4. 组装 LaunchDescription(先声明参数,再放节点)
return LaunchDescription([
declare_bg_r,
declare_bg_g,
declare_bg_b,
turtlesim_node,
])
这个子文件也可以单独运行:
ros2 launch ~/ros2_launch_demo/child.launch.py background_r:=200 background_g:=100 background_b:=50
10.2 父 launch 文件:声明参数并透传给子文件
再写父文件 parent.launch.py:它自己先声明一组参数(这样命令行能直接传参),再通过 IncludeLaunchDescription 包含子文件,并用 launch_arguments 把参数值透传给子文件。
# 父 launch 文件:parent.launch.py
# 作用:声明参数 → 包含(调用)child.launch.py → 把参数传递给它
import os # 用于拼接文件路径
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription # 声明参数 + 包含另一个 launch 文件
from launch.launch_description_sources import PythonLaunchDescriptionSource # 指定 Python launch 文件源
from launch.substitutions import LaunchConfiguration # 获取参数值
def generate_launch_description():
# 1. 定位子 launch 文件的绝对路径
# os.path.dirname(__file__) 返回当前文件所在目录(本文件与 child.launch.py 同目录)
child_launch = os.path.join(
os.path.dirname(__file__), # 当前文件所在目录
'child.launch.py' # 子 launch 文件名
)
# 2. 父文件也声明一组参数(这样命令行可以直接传参给父文件)
declare_bg_r = DeclareLaunchArgument('background_r', default_value='150')
declare_bg_g = DeclareLaunchArgument('background_g', default_value='86')
declare_bg_b = DeclareLaunchArgument('background_b', default_value='255')
# 3. 获取参数的实际值(占位符,运行时才替换)
bg_r = LaunchConfiguration('background_r')
bg_g = LaunchConfiguration('background_g')
bg_b = LaunchConfiguration('background_b')
# 4. 包含(调用)子 launch 文件,并把父文件的参数值传递给它
include_child = IncludeLaunchDescription(
PythonLaunchDescriptionSource(child_launch), # 指定要包含的 launch 文件
launch_arguments={ # 传给子 launch 文件的参数(键值对)
'background_r': bg_r, # 把父文件的参数值透传给子文件
'background_g': bg_g,
'background_b': bg_b,
}.items(), # 注意:launch_arguments 需要 .items() 转成键值对列表
)
# 5. 组装 LaunchDescription(先声明参数,再放 IncludeLaunchDescription)
return LaunchDescription([
declare_bg_r,
declare_bg_g,
declare_bg_b,
include_child,
])
说明:子文件里
DeclareLaunchArgument声明的参数,父文件不一定要全部传。没传的参数会使用子文件里的默认值。
10.3 运行
两个文件都放在同一个目录下(本示例用 ~/ros2_launch_demo/),直接运行父文件即可:
# 运行父文件 → 自动包含子文件,参数默认 150/86/255(青色系)
ros2 launch ~/ros2_launch_demo/parent.launch.py
# 通过命令行给父文件传参 → 父文件透传给子文件 → 背景变红色
ros2 launch ~/ros2_launch_demo/parent.launch.py background_r:=255 background_g:=0 background_b:=0
# 传蓝色
ros2 launch ~/ros2_launch_demo/parent.launch.py background_r:=0 background_g:=0 background_b:=255
运行后 turtlesim 窗口的背景色会变成你通过命令行传给父文件的颜色。可以用 ros2 param get /turtlesim background_r 实时确认参数值。
常见坑:
IncludeLaunchDescription里的路径必须写成 绝对路径。这里用os.path.dirname(__file__)动态获取当前文件所在目录,所以无论把两个文件放到哪里都能正确找到子文件。
小结:
IncludeLaunchDescription(PythonLaunchDescriptionSource(路径), launch_arguments={...}.items())就是”调用另一个 launch 文件”的标准写法。- 父文件先自己声明参数(
DeclareLaunchArgument),再用LaunchConfiguration把值塞进launch_arguments,从而把命令行参数透传给子文件——这就是”调用参数并传递给另一个 launch 文件”。- 子文件通过
DeclareLaunchArgument+LaunchConfiguration接收并使用参数。- 参数值必须是字符串(数字也要写成
'255'这样的字符串;LaunchConfiguration替换后自动转成字符串)。- 用
os.path.dirname(__file__)拿到当前文件所在目录,再拼接出同目录下子 launch 文件的绝对路径,方便快速测试。- 生产项目中,子文件通常安装在功能包里,改用
get_package_share_directory('包名')来定位(见第九节)。
十一、高级用法:执行任意命令(ExecuteProcess)与定时动作(TimerAction)
前几节的 Node 用来启动节点、DeclareLaunchArgument 用来声明参数。但实际项目中还有两类需求:
- 执行任意命令:启动后需要跑一条”一次性命令”,比如
ros2 service call调用服务、ros2 param set设置参数、运行一个脚本。 - 延迟执行:某些动作要等节点启动完成、或等上一步参数生效后再执行,而不是一上来就抢跑。
这两类需求分别对应 ExecuteProcess(执行任意命令)和 TimerAction(定时动作)。两者都是 launch.actions 里的标准 Action,可以直接放进 LaunchDescription。
11.1 ExecuteProcess —— 在 launch 中执行任意命令
作用:在 launch 启动时执行任意 shell 命令(不限于 ROS 命令)。
最小示例:
from launch import LaunchDescription
from launch.actions import ExecuteProcess
def generate_launch_description():
return LaunchDescription([
# 执行一条简单命令
ExecuteProcess(
cmd=['echo', 'hello from launch'], # 命令 + 参数,列表形式
),
])
关键参数:
| 参数 | 说明 | 示例 |
|---|---|---|
cmd |
要执行的命令,字符串列表 | ['echo', 'hello'] 或 ['ros2', 'node', 'list'] |
shell |
是否通过 shell 执行。为 True 时可使用 |、>、&& 等 shell 语法 |
shell=True |
output |
命令输出打印到哪里 | 'screen' / 'log' / 'both' |
condition |
条件执行(配合 IfCondition) |
IfCondition(...) |
env |
为命令设置额外环境变量 | {'MY_VAR': 'value'} |
两种 cmd 写法:
# 写法 1:普通列表 —— 每个元素是一个参数
ExecuteProcess(
cmd=['ros2', 'node', 'list'],
)
# 写法 2:嵌套列表 —— 常用于"多个片段拼接"(配合 Substitution 替换)
# launch 会把内层列表逐段解析,允许混入 LaunchConfiguration 等占位符
ExecuteProcess(
cmd=[[
'ros2 param set ',
turtlesim_ns, # 这里可以是 LaunchConfiguration 占位符
'/sim background_r ',
'120'
]],
shell=True,
)
用
FindExecutable定位命令(推荐):直接写'ros2'依赖 PATH;更健壮的做法是用FindExecutable(name='ros2')动态查找其绝对路径,不依赖 PATH 环境变量:from launch.substitutions import FindExecutable ExecuteProcess( cmd=[[ FindExecutable(name='ros2'), # 自动找到 ros2 的绝对路径 ' service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2}"' ]], shell=True, )
11.2 TimerAction —— 定时延迟执行动作
作用:把一组动作延迟 period 秒后再执行。适合”等节点就绪后再发指令”的场景。
最小示例:
from launch import LaunchDescription
from launch.actions import ExecuteProcess, TimerAction
def generate_launch_description():
delayed_cmd = ExecuteProcess(
cmd=['echo', 'delayed 3 seconds'],
)
return LaunchDescription([
# 3 秒后才执行 delayed_cmd
TimerAction(
period=3.0, # 延迟秒数(浮点数)
actions=[delayed_cmd], # 延迟后要执行的动作列表
),
])
关键参数:
| 参数 | 说明 | 示例 |
|---|---|---|
period |
延迟的秒数(浮点数) | 2.0 |
actions |
到点后要执行的动作列表(可以是 ExecuteProcess、Node、LogInfo 等) |
[cmd1, cmd2] |
注意:
TimerAction的actions接收的是动作对象列表。如果你已经有ExecuteProcess/Node等动作变量,直接把它们放进来即可。period可以用浮点(如0.5)。
11.3 综合示例:07_example_substitutions.launch.py
下面把上面两类用法和前面的知识(参数替换、条件启动)组合起来,完成一个自动演示:启动 turtlesim → 生成海龟 → 改背景色(其中一步延迟 2 秒且带条件)。
# 子 launch 文件:07_example_substitutions.launch.py
# 作用:演示 launch 文件的高级用法 —— 参数替换(Substitutions)、
# 执行任意命令(ExecuteProcess)、条件启动(IfCondition)、定时动作(TimerAction)
# 用于创建节点(Node)
from launch_ros.actions import Node
# launch 基础
from launch import LaunchDescription
# 声明启动参数 + 执行任意命令 + 定时延迟动作
from launch.actions import DeclareLaunchArgument, ExecuteProcess, TimerAction
# 条件启动:满足条件才执行某个动作
from launch.conditions import IfCondition
# 参数替换:获取启动参数值 + 计算 Python 表达式
from launch.substitutions import LaunchConfiguration, PythonExpression
def generate_launch_description():
# ---- 1. 用 LaunchConfiguration 获取启动参数的值(占位符,运行时才替换)----
turtlesim_ns = LaunchConfiguration('turtlesim_ns') # 命名空间
use_provided_red = LaunchConfiguration('use_provided_red') # 是否使用命令行传入的红色值
new_background_r = LaunchConfiguration('new_background_r') # 新的背景色 R 值
# ---- 2. 声明三个可配置的启动参数,并给出默认值 ----
# 注意:必须先声明,才能在命令行用 参数名:=值 覆盖
turtlesim_ns_launch_arg = DeclareLaunchArgument(
'turtlesim_ns', # 参数名称
default_value='turtlesim1' # 默认值(必须是字符串)
)
use_provided_red_launch_arg = DeclareLaunchArgument(
'use_provided_red',
default_value='False'
)
new_background_r_launch_arg = DeclareLaunchArgument(
'new_background_r',
default_value='200'
)
# ---- 3. 创建 turtlesim 节点,并放入指定命名空间 ----
# namespace 用 LaunchConfiguration 替换 → 节点名由命令行参数决定
turtlesim_node = Node(
package='turtlesim', # 节点所在的功能包名
namespace=turtlesim_ns, # 命名空间(命令行传 turtlesim_ns:=xxx 可覆盖)
executable='turtlesim_node', # 可执行文件名
name='sim' # 节点名 → 全名变成 /<namespace>/sim
)
# ---- 4. ExecuteProcess:在 launch 中直接执行任意 shell 命令 ----
# 在 turtlesim 中生成一只新海龟(调用 /spawn 服务)
spawn_turtle = ExecuteProcess(
# cmd 是一个字符串列表,逐段拼接成完整命令
cmd=[[
'ros2 service call ', # 调用服务命令
turtlesim_ns, # 命名空间(替换成实际值)
'/spawn ', # 服务名
'turtlesim/srv/Spawn ', # 服务类型
'"{x: 2, y: 2, theta: 0.2}"' # 服务参数(YAML 格式)
]],
shell=True # 通过 shell 执行(允许重定向、管道等)
)
# 无条件修改背景色 R 为 120
change_background_r = ExecuteProcess(
cmd=[[
'ros2 param set ', # 设置参数命令
turtlesim_ns, # 命名空间(替换成实际值)
'/sim background_r ', # 节点/参数名
'120' # 要设置的参数值
]],
shell=True
)
# 条件修改背景色 R 为命令行传入的值(仅在满足条件时执行)
change_background_r_conditioned = ExecuteProcess(
# ---- 5. IfCondition + PythonExpression:根据表达式结果决定是否执行 ----
# PythonExpression 会把各片段拼成表达式求值(如 "200 == 200 and True")
condition=IfCondition(
PythonExpression([
new_background_r, # 值 1(如 "200")
' == 200', # 比较
' and ', # 逻辑与
use_provided_red # 值 2(如 "True"/"False")
])
),
cmd=[[
'ros2 param set ', # 设置参数命令
turtlesim_ns, # 命名空间(替换成实际值)
'/sim background_r ', # 节点/参数名
new_background_r # 命令行传入的新值(替换成实际值)
]],
shell=True
)
# ---- 6. 组装 LaunchDescription(先声明参数,再放节点和动作)----
return LaunchDescription([
# 声明启动参数(必须放在使用它们的动作之前)
turtlesim_ns_launch_arg,
use_provided_red_launch_arg,
new_background_r_launch_arg,
# 启动 turtlesim 节点
turtlesim_node,
# 生成新海龟
spawn_turtle,
# 无条件把背景色 R 设为 120
change_background_r,
# ---- 7. TimerAction:延迟 2 秒后再执行条件修改背景色 ----
# 保证前面步骤执行完、参数设置生效后再修改
TimerAction(
period=2.0, # 延迟 2 秒
actions=[change_background_r_conditioned] # 延迟后要执行的动作列表
)
])
运行(保存为 ~/ros2_launch_demo/07_example_substitutions.launch.py):
# 默认:命名空间 turtlesim1,不改背景色
ros2 launch ~/ros2_launch_demo/07_example_substitutions.launch.py
# 命令行覆盖:命名空间 turtlesim3 + 满足条件(200 == 200 and True)→ 延迟 2 秒后背景色 R 设为 200
ros2 launch ~/ros2_launch_demo/07_example_substitutions.launch.py \
turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200
执行时序(以第二行命令为例):
t=0s 启动 turtlesim 节点(/turtlesim3/sim)
t≈0s 调用 /spawn 生成新海龟(x=2, y=2)
t≈0s ros2 param set /turtlesim3/sim background_r 120 ← 无条件先设成 120
t=2s (TimerAction 到期)条件成立 → 再设为 200 ← 覆盖成命令行传入的值
小结:
ExecuteProcess(cmd=列表, shell=True):在 launch 里执行任意命令。cmd可以是普通列表或嵌套列表;嵌套列表里可混入LaunchConfiguration等占位符实现动态拼命令。TimerAction(period=秒, actions=[...]):把一组动作延迟指定秒数再执行,常用于”等节点就绪后再操作”。FindExecutable(name='xxx'):动态查找命令的绝对路径,比硬编码命令名更健壮。- 两者都是普通 Action,可自由与
DeclareLaunchArgument、LaunchConfiguration、IfCondition、PythonExpression组合,实现复杂的自动化启动流程。