使用替换
目标: 了解 ROS 2 launch 文件中的替换(substitutions)。
教程级别: 中级
时间: 15 分钟
Launch 文件用于启动节点、服务以及执行各种进程。这些操作可以带有参数,从而影响其行为。替换(substitution)可以在参数中使用,为编写可复用的 launch 文件提供更大灵活性。替换本质上是一种变量,仅在 launch description 执行期间才会被求值,可用于获取特定信息(如 launch 配置、环境变量),或用于求值任意 Python 表达式。
本教程将展示 ROS 2 launch 文件中替换的使用示例。
本教程使用 turtlesim 包。此外,本教程假设你已经熟悉如何创建包。
和往常一样,每次打开新终端时不要忘记 source ROS 2。
1 创建并设置包
Section titled “1 创建并设置包”首先,创建一个名为 launch_tutorial 的新包:
创建一个 build_type 为 ament_python 的新包:
$ ros2 pkg create --build-type ament_python --license Apache-2.0 launch_tutorial创建一个 build_type 为 ament_cmake 的新包:
$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 launch_tutorial在该包内部,创建一个名为 launch 的目录:
$ mkdir launch_tutorial/launch$ mkdir launch_tutorial/launch$ md launch_tutorial\launch最后,确保安装 launch 文件:
在包的 setup.py 中做以下修改:
import osfrom glob import globfrom setuptools import find_packages, setup
package_name = 'launch_tutorial'
setup( # Other parameters ... data_files=[ # ... Other data files # Include all launch files. (os.path.join('share', package_name, 'launch'), glob('launch/*')) ])在 CMakeLists.txt 的 ament_package() 之前添加以下代码:
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/)2 父 launch 文件
Section titled “2 父 launch 文件”我们来创建一个 launch 文件,用于调用另一个 launch 文件并传递参数。该 launch 文件可以是 YAML、XML 或 Python 格式。
为此,在 launch_tutorial 包的 launch 文件夹中创建以下文件。
将完整代码复制并粘贴到 launch/example_main_launch.xml 文件中:
<?xml version="1.0" encoding="UTF-8"?><launch> <let name="background_r" value="200" /> <include file="$(find-pkg-share launch_tutorial)/launch/example_substitutions_launch.xml"> <let name="turtlesim_ns" value="turtlesim2" /> <let name="use_provided_red" value="True" /> <let name="new_background_r" value="$(var background_r)" /> </include></launch>$(find-pkg-share launch_tutorial) 替换用于查找 launch_tutorial 包的路径。然后将该路径与 example_substitutions_launch.xml 文件名拼接。
<include file="$(find-pkg-share launch_tutorial)/launch/example_substitutions_launch.xml">background_r 变量以及 turtlesim_ns 和 use_provided_red 参数被传递给 include 操作。$(var background_r) 替换用于将 new_background_r 参数的值定义为 background_r 变量的值。
<let name="turtlesim_ns" value="turtlesim2" /> <let name="use_provided_red" value="True" /> <let name="new_background_r" value="$(var background_r)" />将完整代码复制并粘贴到 launch/example_main_launch.yaml 文件中:
%YAML 1.2---launch: - let: name: "background_r" value: "200" - include: file: "$(find-pkg-share launch_tutorial)/launch/example_substitutions_launch.yaml" let: - name: "turtlesim_ns" value: "turtlesim2" - name: "use_provided_red" value: "True" - name: "new_background_r" value: "$(var background_r)"$(find-pkg-share launch_tutorial) 替换用于查找 launch_tutorial 包的路径。然后将该路径与 example_substitutions_launch.yaml 文件名拼接。
file: "$(find-pkg-share launch_tutorial)/launch/example_substitutions_launch.yaml"background_r 变量以及 turtlesim_ns 和 use_provided_red 参数被传递给 include 操作。$(var background_r) 替换用于将 new_background_r 参数的值定义为 background_r 变量的值。
let: - name: "turtlesim_ns" value: "turtlesim2" - name: "use_provided_red" value: "True" - name: "new_background_r" value: "$(var background_r)"将完整代码复制并粘贴到 launch/example_main_launch.py 文件中:
from launch import LaunchDescriptionfrom launch.actions import IncludeLaunchDescriptionfrom launch.substitutions import PathJoinSubstitutionfrom launch_ros.substitutions import FindPackageShare
def generate_launch_description(): colors = { 'background_r': '200' }
return LaunchDescription([ IncludeLaunchDescription( PathJoinSubstitution([ FindPackageShare('launch_tutorial'), 'launch', 'example_substitutions_launch.py' ]), launch_arguments={ 'turtlesim_ns': 'turtlesim2', 'use_provided_red': 'True', 'new_background_r': colors['background_r'], }.items() ) ])FindPackageShare 替换用于查找 launch_tutorial 包的路径。然后使用 PathJoinSubstitution 替换将该包路径与 example_substitutions_launch.py 文件名拼接。
PathJoinSubstitution([ FindPackageShare('launch_tutorial'), 'launch', 'example_substitutions_launch.py' ]),提示:替换或字符串的列表会被拼接为一个单独的字符串。这通常适用于任何支持替换的地方。
例如,使用 PathJoinSubstitution 时,如果文件名前缀依赖于一个名为 file 的 launch 参数,可以使用替换和字符串的列表来创建文件名:
# Make sure to import LaunchConfiguration:# from launch.substitutions import LaunchConfiguration
PathJoinSubstitution([ FindPackageShare('launch_tutorial'), 'launch', [LaunchConfiguration('file', default='example_substitutions'), '_launch', '.py']])在这种情况下,默认情况下,提供给 PathJoinSubstitution 的最后一个路径组件将解析为 example_substitutions_launch.py,然后与其他路径组件拼接。
launch_arguments 字典以及 turtlesim_ns 和 use_provided_red 参数被传递给 IncludeLaunchDescription 操作。
launch_arguments={ 'turtlesim_ns': 'turtlesim2', 'use_provided_red': 'True', 'new_background_r': colors['background_r'], }.items()3 替换示例 launch 文件
Section titled “3 替换示例 launch 文件”现在在同一文件夹中创建替换 launch 文件:
创建文件 launch/example_substitutions_launch.xml 并插入以下代码:
<?xml version="1.0" encoding="UTF-8"?><launch> <arg name="turtlesim_ns" default="turtlesim1" /> <arg name="use_provided_red" default="False" /> <arg name="new_background_r" default="200" />
<node pkg="turtlesim" namespace="$(var turtlesim_ns)" exec="turtlesim_node" name="sim" /> <executable cmd="ros2 service call $(var turtlesim_ns)/spawn turtlesim_msgs/srv/Spawn '{x: 5, y: 2, theta: 0.2}'" /> <executable cmd="ros2 param set $(var turtlesim_ns)/sim background_r 120" /> <timer period="2.0"> <executable cmd="ros2 param set $(var turtlesim_ns)/sim background_r $(var new_background_r)" if="$(eval '$(var new_background_r) == 200 and $(var use_provided_red)')" /> </timer></launch>这里定义了 turtlesim_ns、use_provided_red 和 new_background_r launch 配置。它们用于将 launch 参数的值存储在上述变量中,并传递给所需的操作。之后,可以在 launch description 的任何位置通过 $(var <name>) 替换获取这些 launch 参数的值。
arg 标签用于定义可以从上面的 launch 文件或从控制台传递的 launch 参数。
<arg name="turtlesim_ns" default="turtlesim1" /> <arg name="use_provided_red" default="False" /> <arg name="new_background_r" default="200" />定义了 turtlesim_node 节点,其 namespace 通过 $(var <name>) 替换设置为 turtlesim_ns launch 配置的值。
<node pkg="turtlesim" namespace="$(var turtlesim_ns)" exec="turtlesim_node" name="sim" />随后定义了一个 executable 操作及对应的 cmd 标签。该命令调用 turtlesim 节点的 spawn 服务。此外,使用 $(var <name>) 替换获取 turtlesim_ns launch 参数的值来构造命令字符串。
<executable cmd="ros2 service call $(var turtlesim_ns)/spawn turtlesim_msgs/srv/Spawn '{x: 5, y: 2, theta: 0.2}'" />ros2 param executable 操作同样用于更改 turtlesim 背景的红色颜色参数。区别在于,timer 内的第二个操作仅在提供的 new_background_r 参数等于 200 且 use_provided_red launch 参数设置为 True 时才执行。if 谓词的求值使用 $(eval <python-expression>) 替换完成。
<executable cmd="ros2 param set $(var turtlesim_ns)/sim background_r 120" /> <timer period="2.0"> <executable cmd="ros2 param set $(var turtlesim_ns)/sim background_r $(var new_background_r)" if="$(eval '$(var new_background_r) == 200 and $(var use_provided_red)')" /> </timer>创建文件 launch/example_substitutions_launch.yaml 并插入以下代码:
%YAML 1.2---launch: - arg: name: "turtlesim_ns" default: "turtlesim1" - arg: name: "use_provided_red" default: "False" - arg: name: "new_background_r" default: "200"
- node: pkg: "turtlesim" namespace: "$(var turtlesim_ns)" exec: "turtlesim_node" name: "sim" - executable: cmd: 'ros2 service call $(var turtlesim_ns)/spawn turtlesim_msgs/srv/Spawn "{x: 5, y: 2, theta: 0.2}"' - executable: cmd: "ros2 param set $(var turtlesim_ns)/sim background_r 120" - timer: period: 2.0 children: - executable: cmd: "ros2 param set $(var turtlesim_ns)/sim background_r $(var new_background_r)" if: '$(eval "$(var new_background_r) == 200 and $(var use_provided_red)")'这里定义了 turtlesim_ns、use_provided_red 和 new_background_r launch 配置。它们用于将 launch 参数的值存储在上述变量中,并传递给所需的操作。之后,可以在 launch description 的任何位置通过 $(var <name>) 替换获取这些 launch 参数的值。
arg 标签用于定义可以从上面的 launch 文件或从控制台传递的 launch 参数。
- arg: name: "turtlesim_ns" default: "turtlesim1" - arg: name: "use_provided_red" default: "False" - arg: name: "new_background_r" default: "200"定义了 turtlesim_node 节点,其 namespace 通过 $(var <name>) 替换设置为 turtlesim_ns launch 配置的值。
- node: pkg: "turtlesim" namespace: "$(var turtlesim_ns)" exec: "turtlesim_node" name: "sim"随后定义了一个 executable 操作及对应的 cmd 标签。该命令调用 turtlesim 节点的 spawn 服务。此外,使用 $(var <name>) 替换获取 turtlesim_ns launch 参数的值来构造命令字符串。
- executable: cmd: 'ros2 service call $(var turtlesim_ns)/spawn turtlesim_msgs/srv/Spawn "{x: 5, y: 2, theta: 0.2}"'ros2 param executable 操作同样用于更改 turtlesim 背景的红色颜色参数。区别在于,timer 内的第二个操作仅在提供的 new_background_r 参数等于 200 且 use_provided_red launch 参数设置为 True 时才执行。if 谓词的求值使用 $(eval <python-expression>) 替换完成。
- executable: cmd: "ros2 param set $(var turtlesim_ns)/sim background_r 120" - timer: period: 2.0 children: - executable: cmd: "ros2 param set $(var turtlesim_ns)/sim background_r $(var new_background_r)" if: '$(eval "$(var new_background_r) == 200 and $(var use_provided_red)")'创建文件 launch/example_substitutions_launch.py 并插入以下代码:
from launch import LaunchDescriptionfrom launch.actions import DeclareLaunchArgument, ExecuteProcess, TimerActionfrom launch.conditions import IfConditionfrom launch.substitutions import LaunchConfiguration, PythonExpressionfrom launch_ros.actions import Node
def generate_launch_description(): turtlesim_ns = LaunchConfiguration('turtlesim_ns') use_provided_red = LaunchConfiguration('use_provided_red') new_background_r = LaunchConfiguration('new_background_r')
return LaunchDescription([ DeclareLaunchArgument( 'turtlesim_ns', default_value='turtlesim1' ), DeclareLaunchArgument( 'use_provided_red', default_value='False' ), DeclareLaunchArgument( 'new_background_r', default_value='200' ), Node( package='turtlesim', namespace=turtlesim_ns, executable='turtlesim_node', name='sim' ), ExecuteProcess( cmd=[[ 'ros2 service call ', turtlesim_ns, '/spawn ', 'turtlesim_msgs/srv/Spawn ', '"{x: 2, y: 2, theta: 0.2}"' ]], shell=True ), ExecuteProcess( cmd=[[ 'ros2 param set ', turtlesim_ns, '/sim background_r ', '120' ]], shell=True ), TimerAction( period=2.0, actions=[ ExecuteProcess( condition=IfCondition( PythonExpression([ new_background_r, ' == 200', ' and ', use_provided_red ]) ), cmd=[[ 'ros2 param set ', turtlesim_ns, '/sim background_r ', new_background_r ]], shell=True ), ], ) ])这里定义了 turtlesim_ns、use_provided_red 和 new_background_r launch 配置。它们用于在上述变量中保存 launch 参数的值,并传递给所需的操作。借助这些 LaunchConfiguration 替换,可以在 launch description 的任何位置获取 launch 参数的值。
DeclareLaunchArgument 用于定义可以从上面的 launch 文件或从控制台传递的 launch 参数。
DeclareLaunchArgument( 'turtlesim_ns', default_value='turtlesim1' ), DeclareLaunchArgument( 'use_provided_red', default_value='False' ), DeclareLaunchArgument( 'new_background_r', default_value='200' ),定义了 turtlesim_node 节点,其 namespace 设置为 turtlesim_ns LaunchConfiguration 替换。
Node( package='turtlesim', namespace=turtlesim_ns, executable='turtlesim_node', name='sim' ),下一个操作 ExecuteProcess 定义了对应的 cmd 参数,用于调用 turtlesim 节点的 spawn 服务。此外,使用 LaunchConfiguration 替换在命令字符串中提供 turtlesim_ns launch 参数的值。
ExecuteProcess( cmd=[[ 'ros2 service call ', turtlesim_ns, '/spawn ', 'turtlesim_msgs/srv/Spawn ', '"{x: 2, y: 2, theta: 0.2}"' ]], shell=True ),同样的方法用于 change_background_r 和 change_background_r_conditioned 操作,它们会更改 turtlesim 背景的红色颜色参数。区别在于,下一个操作仅在提供的 new_background_r 参数等于 200 且 use_provided_red launch 参数设置为 True 时才执行。IfCondition 内的求值使用 PythonExpression 替换完成。
TimerAction( period=2.0, actions=[ ExecuteProcess( condition=IfCondition( PythonExpression([ new_background_r, ' == 200', ' and ', use_provided_red ]) ), cmd=[[ 'ros2 param set ', turtlesim_ns, '/sim background_r ', new_background_r ]], shell=True ), ], )进入 workspace 根目录,构建包:
$ colcon build构建后还要记得 source workspace。
现在你可以使用 ros2 launch 命令来启动。
$ ros2 launch launch_tutorial example_main_launch.yaml$ ros2 launch launch_tutorial example_main_launch.xml$ ros2 launch launch_tutorial example_main_launch.py这将会执行以下操作:
- 启动一个蓝色背景的 turtlesim 节点
- 生成第二只海龟
- 将颜色更改为紫色
- 如果提供的
background_r参数为200且use_provided_red参数为True,则两秒后将颜色更改为粉色
修改 launch 参数
Section titled “修改 launch 参数”如果你想更改提供的 launch 参数,可以更新 example_main_launch.yaml 中的 background_r 变量,或者带上你想要的参数启动 example_substitutions_launch.yaml。要查看可以传递给 launch 文件的参数,运行以下命令:
$ ros2 launch launch_tutorial example_substitutions_launch.yaml --show-args如果你想更改提供的 launch 参数,可以更新 example_main_launch.xml 中的 background_r 变量,或者带上你想要的参数启动 example_substitutions_launch.xml。要查看可以传递给 launch 文件的参数,运行以下命令:
$ ros2 launch launch_tutorial example_substitutions_launch.xml --show-args如果你想更改提供的 launch 参数,可以更新 example_main_launch.py 中 launch_arguments 字典里的参数,或者带上你想要的参数启动 example_substitutions_launch.py。要查看可以传递给 launch 文件的参数,运行以下命令:
$ ros2 launch launch_tutorial example_substitutions_launch.py --show-args这将显示可以传递给 launch 文件的参数及其默认值。
Arguments (pass arguments as '<name>:=<value>'):
'turtlesim_ns': no description given (default: 'turtlesim1')
'use_provided_red': no description given (default: 'False')
'new_background_r': no description given (default: '200')现在你可以按如下方式向 launch 文件传递所需参数:
$ ros2 launch launch_tutorial example_substitutions_launch.yaml turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200$ ros2 launch launch_tutorial example_substitutions_launch.xml turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200$ ros2 launch launch_tutorial example_substitutions_launch.py turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200除了 $(eval <python-expression>) 之外,还有一组专用的布尔替换可用于比较值和组合结果。它们可以在任何允许使用替换的地方使用,包括任何操作的 if 和 unless 属性。
注意:比较是在每个参数的字符串表示上进行的。
| XML / YAML 名称 | Python 类 | 描述 |
|---|---|---|
$(equals A B) | EqualsSubstitution | 如果 A 等于 B 则解析为 'true',否则为 'false'。 |
$(not-equals A B) | NotEqualsSubstitution | 如果 A 不等于 B 则解析为 'true',否则为 'false'。 |
$(and A B) | AndSubstitution | 两个布尔替换的逻辑与。 |
$(or A B) | OrSubstitution | 两个布尔替换的逻辑或。 |
$(any A B ...) | AnySubstitution | 如果任意参数为 true 则解析为 'true'。 |
$(all A B ...) | AllSubstitution | 仅当所有参数都为 true 时才解析为 'true'。 |
上一节中的 if 谓词也可以使用布尔替换而非 Python 表达式来表达:
<executable cmd="ros2 param set /turtlesim background_r $(var new_background_r)" if="$(and $(equals $(var new_background_r) 200) $(var use_provided_red))"/>- executable: cmd: ros2 param set /turtlesim background_r $(var new_background_r) if: $(and $(equals $(var new_background_r) 200) $(var use_provided_red))from launch.conditions import IfConditionfrom launch.substitutions import AndSubstitution, EqualsSubstitution, LaunchConfiguration
ExecuteProcess( cmd=[[ FindExecutable(name='ros2'), ' param set ', '/turtlesim background_r ', LaunchConfiguration('new_background_r'), ]], condition=IfCondition( AndSubstitution( EqualsSubstitution(LaunchConfiguration('new_background_r'), '200'), LaunchConfiguration('use_provided_red'), ) ),)launch 文档提供了有关可用替换的详细信息。
在本教程中,你了解了如何在 launch 文件中使用替换,以及如何借助它们编写可复用的 launch 文件。
接下来可以进一步了解如何在 launch 文件中使用事件处理器——事件处理器用于定义一组复杂的规则,从而动态地修改 launch 文件。