主页 > 人工智能  > 

使用Python、XML和YAML编写ROS2Launch文件

使用Python、XML和YAML编写ROS2Launch文件
系列文章目录

文章目录 系列文章目录前言一、Launch 文件示例1.1 Python 版本1.2 XML 版本1.3 YAML 版本 二、从命令行使用 Launch 文件1. Launching2. 设置参数3. 控制海龟 三、Python、XML 或 YAML: 我应该使用哪种语言?四、为什么要使用 ROS 2 Launch`如果觉得内容不错,请点赞、收藏、关注`


前言

ROS 2 launch 文件可以用 Python、XML 和 YAML 编写。本指南介绍了如何使用这些不同的格式来完成相同的任务,并对何时使用每种格式进行了讨论。


一、Launch 文件示例

下面是一个用 Python、XML 和 YAML 实现的 Launch 文件。每个 Launch 文件都执行以下操作:

使用默认值设置命令行参数

包含另一个 launch 文件

在另一个命名空间中包含另一个启动文件

启动节点并设置其名称空间

启动一个节点,设置其名称空间,并在该节点中设置参数(使用参数)

创建一个节点,将消息从一个话题重新映射到另一个话题

1.1 Python 版本

首先介绍涉及到的几个 类 和 方法

get_package_share_directory(package_name, print_warning=True) 查找并返回给定 ROS 包的 share 文件夹路径 LaunchDescription(LaunchDescriptionEntity) Launch 系统的描述。 描述由一组实体表达,这些实体代表了系统架构师的意图。 该描述可能还有参数,参数由 :py:class:`launch.actions.DeclareLaunchArgument` 操作在该启动描述中声明 DeclareLaunchArgument(Action) 声明一个新的 launch 参数。 启动参数存储在同名的 "启动配置 "中。 请参阅 :py:class:`launch.actions.SetLaunchConfiguration` 和 :py:class:`launch.substitutions.LaunchConfiguration`。 在 :py:class:`launch.LaunchDescription` 中声明的任何启动参数将在包含该启动描述时作为参数显示,例如作为 :py:class:`launch.actions.IncludeLaunchDescription` 操作中的附加参数,或作为使用 ``ros2 launch ...``启动时的命令行参数。 GroupAction(Action) 产生其他操作的操作。 该操作用于嵌套其他操作,而无需包含单独的启动说明,同时还可选择具有条件(与所有其他操作一样)、范围和转发启动配置和环境变量,以及/或只为组及其产生的操作声明启动配置。 IncludeLaunchDescription(Action) 包含启动描述源并在访问时生成其实体的操作。 可以通过 :py:class:`launch.actions.DeclareLaunchArgument` 动作向启动描述传递参数。 PythonLaunchDescriptionSource(LaunchDescriptionSource) 创建 PythonLaunchDescriptionSource。 给定的文件路径应该是一个 ``.launch.py`` 样式的文件。该路径可能应该是绝对路径,因为当前工作目录将是运行启动文件的位置,这可能会根据情况而改变。路径可以由 Substitution 实例组成,这些实例会在调用 :py:meth:`get_launch_description()` 时展开。 LaunchConfiguration(Substitution) 可访问启动配置变量的替代变量。 TextSubstitution(Substitution) 可对单个字符串文本进行替换。 PushROSNamespace(Action) 推送 ros 命名空间的动作。 在有作用域的 `GroupAction` 中使用时,它会自动弹出。没有其他方法可以弹出它。 XMLLaunchDescriptionSource(FrontendLaunchDescriptionSource) 封装 XML 启动文件,可在启动过程中加载。 YAMLLaunchDescriptionSource(FrontendLaunchDescriptionSource) 封装 YAML 启动文件,可在启动过程中加载。 Node(ExecuteProcess) 执行一个 ROS 节点的操作。

Python 代码如下

# example_launch.py import os from ament_index_python import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument from launch.actions import GroupAction from launch.actions import IncludeLaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import LaunchConfiguration from launch.substitutions import TextSubstitution from launch_ros.actions import Node from launch_ros.actions import PushRosNamespace from launch_xml.launch_description_sources import XMLLaunchDescriptionSource from launch_yaml.launch_description_sources import YAMLLaunchDescriptionSource def generate_launch_description(): # args that can be set from the command line or a default will be used background_r_launch_arg = DeclareLaunchArgument( "background_r", default_value=TextSubstitution(text="0") ) background_g_launch_arg = DeclareLaunchArgument( "background_g", default_value=TextSubstitution(text="255") ) background_b_launch_arg = DeclareLaunchArgument( "background_b", default_value=TextSubstitution(text="0") ) chatter_py_ns_launch_arg = DeclareLaunchArgument( "chatter_py_ns", default_value=TextSubstitution(text="chatter/py/ns") ) chatter_xml_ns_launch_arg = DeclareLaunchArgument( "chatter_xml_ns", default_value=TextSubstitution(text="chatter/xml/ns") ) chatter_yaml_ns_launch_arg = DeclareLaunchArgument( "chatter_yaml_ns", default_value=TextSubstitution(text="chatter/yaml/ns") ) # include another launch file launch_include = IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join( get_package_share_directory('demo_nodes_cpp'), 'launch/topics/talker_listener_launch.py')) ) # include a Python launch file in the chatter_py_ns namespace launch_py_include_with_namespace = GroupAction( actions=[ # push_ros_namespace to set namespace of included nodes PushRosNamespace('chatter_py_ns'), IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join( get_package_share_directory('demo_nodes_cpp'), 'launch/topics/talker_listener_launch.py')) ), ] ) # include a xml launch file in the chatter_xml_ns namespace launch_xml_include_with_namespace = GroupAction( actions=[ # push_ros_namespace to set namespace of included nodes PushRosNamespace('chatter_xml_ns'), IncludeLaunchDescription( XMLLaunchDescriptionSource( os.path.join( get_package_share_directory('demo_nodes_cpp'), 'launch/topics/talker_listener_launch.xml')) ), ] ) # include a yaml launch file in the chatter_yaml_ns namespace launch_yaml_include_with_namespace = GroupAction( actions=[ # push_ros_namespace to set namespace of included nodes PushRosNamespace('chatter_yaml_ns'), IncludeLaunchDescription( YAMLLaunchDescriptionSource( os.path.join( get_package_share_directory('demo_nodes_cpp'), 'launch/topics/talker_listener_launch.yaml')) ), ] ) # start a turtlesim_node in the turtlesim1 namespace turtlesim_node = Node( package='turtlesim', namespace='turtlesim1', executable='turtlesim_node', name='sim' ) # start another turtlesim_node in the turtlesim2 namespace # and use args to set parameters turtlesim_node_with_parameters = Node( package='turtlesim', namespace='turtlesim2', executable='turtlesim_node', name='sim', parameters=[{ "background_r": LaunchConfiguration('background_r'), "background_g": LaunchConfiguration('background_g'), "background_b": LaunchConfiguration('background_b'), }] ) # perform remap so both turtles listen to the same command topic forward_turtlesim_commands_to_second_turtlesim_node = Node( package='turtlesim', executable='mimic', name='mimic', remappings=[ ('/input/pose', '/turtlesim1/turtle1/pose'), ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'), ] ) return LaunchDescription([ background_r_launch_arg, background_g_launch_arg, background_b_launch_arg, chatter_py_ns_launch_arg, chatter_xml_ns_launch_arg, chatter_yaml_ns_launch_arg, launch_include, launch_py_include_with_namespace, launch_xml_include_with_namespace, launch_yaml_include_with_namespace, turtlesim_node, turtlesim_node_with_parameters, forward_turtlesim_commands_to_second_turtlesim_node, ]) 1.2 XML 版本 <!-- example_launch.xml --> <launch> <!-- args that can be set from the command line or a default will be used --> <arg name="background_r" default="0" /> <arg name="background_g" default="255" /> <arg name="background_b" default="0" /> <arg name="chatter_py_ns" default="chatter/py/ns" /> <arg name="chatter_xml_ns" default="chatter/xml/ns" /> <arg name="chatter_yaml_ns" default="chatter/yaml/ns" /> <!-- include another launch file --> <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.py" /> <!-- include a Python launch file in the chatter_py_ns namespace--> <group> <!-- push_ros_namespace to set namespace of included nodes --> <push_ros_namespace namespace="$(var chatter_py_ns)" /> <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.py" /> </group> <!-- include a xml launch file in the chatter_xml_ns namespace--> <group> <!-- push_ros_namespace to set namespace of included nodes --> <push_ros_namespace namespace="$(var chatter_xml_ns)" /> <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.xml" /> </group> <!-- include a yaml launch file in the chatter_yaml_ns namespace--> <group> <!-- push_ros_namespace to set namespace of included nodes --> <push_ros_namespace namespace="$(var chatter_yaml_ns)" /> <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.yaml" /> </group> <!-- start a turtlesim_node in the turtlesim1 namespace --> <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim1" /> <!-- start another turtlesim_node in the turtlesim2 namespace and use args to set parameters --> <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim2"> <param name="background_r" value="$(var background_r)" /> <param name="background_g" value="$(var background_g)" /> <param name="background_b" value="$(var background_b)" /> </node> <!-- perform remap so both turtles listen to the same command topic --> <node pkg="turtlesim" exec="mimic" name="mimic"> <remap from="/input/pose" to="/turtlesim1/turtle1/pose" /> <remap from="/output/cmd_vel" to="/turtlesim2/turtle1/cmd_vel" /> </node> </launch> 1.3 YAML 版本 # example_launch.yaml launch: # args that can be set from the command line or a default will be used - arg: name: "background_r" default: "0" - arg: name: "background_g" default: "255" - arg: name: "background_b" default: "0" - arg: name: "chatter_py_ns" default: "chatter/py/ns" - arg: name: "chatter_xml_ns" default: "chatter/xml/ns" - arg: name: "chatter_yaml_ns" default: "chatter/yaml/ns" # include another launch file - include: file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.py" # include a Python launch file in the chatter_py_ns namespace - group: - push_ros_namespace: namespace: "$(var chatter_py_ns)" - include: file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.py" # include a xml launch file in the chatter_xml_ns namespace - group: - push_ros_namespace: namespace: "$(var chatter_xml_ns)" - include: file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.xml" # include a yaml launch file in the chatter_yaml_ns namespace - group: - push_ros_namespace: namespace: "$(var chatter_yaml_ns)" - include: file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener_launch.yaml" # start a turtlesim_node in the turtlesim1 namespace - node: pkg: "turtlesim" exec: "turtlesim_node" name: "sim" namespace: "turtlesim1" # start another turtlesim_node in the turtlesim2 namespace and use args to set parameters - node: pkg: "turtlesim" exec: "turtlesim_node" name: "sim" namespace: "turtlesim2" param: - name: "background_r" value: "$(var background_r)" - name: "background_g" value: "$(var background_g)" - name: "background_b" value: "$(var background_b)" # perform remap so both turtles listen to the same command topic - node: pkg: "turtlesim" exec: "mimic" name: "mimic" remap: - from: "/input/pose" to: "/turtlesim1/turtle1/pose" - from: "/output/cmd_vel" to: "/turtlesim2/turtle1/cmd_vel" 二、从命令行使用 Launch 文件 1. Launching

上述任何启动文件都可以通过 ros2 launch 运行。要在本地试用它们,可以创建一个新软件包,然后使用

ros2 launch <package_name> <launch_file_name>

或通过指定 launch 文件的路径直接运行该文件

ros2 launch <path_to_launch_file> 2. 设置参数

要设置传递给启动文件的参数,应使用 key:=value 语法。例如,可以用以下方式设置 background_r 的值:

ros2 launch <package_name> <launch_file_name> background_r:=255 ros2 launch <path_to_launch_file> background_r:=255 3. 控制海龟

要测试重映射是否有效,可以在另一个终端运行以下命令来控制海龟:

ros2 run turtlesim turtle_teleop_key --ros-args --remap __ns:=/turtlesim1
三、Python、XML 或 YAML: 我应该使用哪种语言?

ROS 1 中的启动文件是用 XML 编写的,因此对于来自 ROS 1 的用户来说,XML 可能是最熟悉的。

对于大多数应用程序来说,选择哪种 ROS 2 启动格式取决于开发人员的偏好。不过,如果你的启动文件需要有 XML 或 YAML 无法实现的灵活性,你可以使用 Python 来编写启动文件。由于以下两个原因,使用 Python 编写 ROS 2 启动文件更为灵活:

Python 是一种脚本语言,因此您可以在启动文件中使用该语言及其库。

ros2/launch(一般启动功能)和 ros2/launch_ros(ROS 2 特定启动功能)都是用 Python 编写的,因此你可以访问 XML 和 YAML 可能无法提供的较低级别的启动功能。

尽管如此,用 Python 编写的启动文件可能比 XML 或 YAML 编写的文件更复杂、更冗长。

四、为什么要使用 ROS 2 Launch

ROS 2 系统通常由运行在多个不同进程(甚至不同机器)上的多个节点组成。虽然每个节点都可以单独运行,但很快就会变得非常麻烦。

ROS 2 中的启动系统旨在通过一条命令自动运行多个节点。它可以帮助用户描述系统配置,然后按描述执行。系统配置包括运行哪些程序、在哪里运行、传递哪些参数,以及 ROS 特有的约定,通过为每个组件提供不同的配置,可以方便地在整个系统中重复使用组件。它还负责监控已启动进程的状态,并对这些进程的状态变化做出报告和/或反应。

上述所有内容都在一个启动文件中指定,该文件可以用 Python、XML 或 YAML 编写。使用 ros2 launch 命令运行该启动文件后,所有指定的节点都将运行。

设计文档详细介绍了 ROS 2 启动系统的设计目标(目前尚未提供所有功能)。


如果觉得内容不错,请点赞、收藏、关注
标签:

使用Python、XML和YAML编写ROS2Launch文件由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“使用Python、XML和YAML编写ROS2Launch文件