管理大型项目

目标 学习使用 ROS 2 启动文件管理大型项目的最佳实践。

辅导水平: 中级

时间 20 分钟

背景介绍

本教程介绍了为大型项目编写启动文件的一些技巧。重点在于如何构建启动文件,以便在不同情况下尽可能重复使用。此外,它还涵盖了不同 ROS 2 启动工具的使用示例,如参数、YAML 文件、重映射、命名空间、默认参数和 RViz 配置。

先决条件

本教程使用 海龟乌龟_tf2_py 软件包。本教程还假设您已 创建了一个新软件包 建筑类型 ament_python 人称 启动教程.

导言

机器人上的大型应用通常涉及多个相互连接的节点,每个节点都可能有许多参数。乌龟模拟器中对多只乌龟的模拟就是一个很好的例子。乌龟模拟由多个乌龟节点、世界配置以及 TF 广播员和监听员节点组成。在所有节点之间,有大量的 ROS 参数会影响这些节点的行为和外观。ROS 2 启动文件允许我们在一个地方启动所有节点并设置相应参数。本教程结束时,您将建立 launch_turtlesim_launch.py 启动文件中的 启动教程 软件包。该启动文件将调出不同的节点,负责模拟两个 turtlesim 仿真、启动 TF 广播器和监听器、加载参数并启动 RViz 配置。在本教程中,我们将详细介绍该启动文件及其使用的所有相关功能。

编写启动文件

1 最高级别组织

在编写启动文件的过程中,其中一个目的应该是使文件尽可能可重复使用。为此,可以将相关节点和配置集群到单独的启动文件中。之后,可以编写一个专门用于特定配置的顶级启动文件。这样就可以在完全不改变启动文件的情况下,在相同的机器人之间进行移动。即使是从真实机器人转到模拟机器人这样的改变,也只需少量改动即可完成。

现在,我们将介绍实现这一点的顶层启动文件结构。首先,我们将创建一个启动文件,该文件将调用不同的启动文件。为此,让我们创建一个 launch_turtlesim_launch.py 文件中的 /启动 文件夹中的 启动教程 包装

舶来品 os

 ament_index_python.packages 舶来品 获取软件包共享目录

 启动 舶来品 启动说明
 launch.actions 舶来品 包含启动描述
 launch.launch_description_sources 舶来品 PythonLaunchDescriptionSource


捍卫 生成发射描述():
   乌龟模拟世界_1 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/turtlesim_world_1_launch.py';])
      )
   乌龟模拟世界_2 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/turtlesim_world_2_launch.py';])
      )
   广播员收听节点 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/broadcaster_listener_launch.py';]),
      启动参数={'target_frame': 'carrot1';}.项目(),
      )
   模仿节点 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/mimic_launch.py';])
      )
   固定边框节点 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/fixed_broadcaster_launch.py';])
      )
   rviz_node = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/turtlesim_rviz_launch.py';])
      )

   返回 启动说明([
      乌龟模拟世界_1,
      乌龟模拟世界_2,
      广播员收听节点,
      模仿节点,
      固定边框节点,
      rviz_node
   ])

该启动文件包含一组其他启动文件。每个包含的启动文件都包含节点、参数和可能的嵌套包含,它们与系统的某个部分有关。确切地说,我们启动了两个 turtlesim 模拟世界、TF 广播器、TF 监听器、模仿器、固定帧广播器和 RViz 节点。

备注

设计提示:顶层启动文件应简短,包含与应用程序子组件相对应的其他文件和常用更改参数。

按照下面的方式编写启动文件,可以方便地更换系统的某一部分,我们稍后会看到。不过,在某些情况下,由于性能和使用原因,某些节点或启动文件必须单独启动。

备注

设计提示:在决定应用程序需要多少个顶级启动文件时,请注意权衡利弊。

2 个参数

2.1 在启动文件中设置参数

首先,我们将编写一个启动文件,启动我们的第一个海龟模拟。首先,创建一个名为 turtlesim_world_1_launch.py.

 启动 舶来品 启动说明
 launch.actions 舶来品 声明启动参数
 launch.substitutions 舶来品 启动配置, 文本替换

 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   背景_r_启动参数 = 声明启动参数(
      'background_r', 默认值=文本替换(文本='0')
   )
   背景_g_launch_arg = 声明启动参数(
      'background_g', 默认值=文本替换(文本='84')
   )
   背景_b_启动参数 = 声明启动参数(
      'background_b', 默认值=文本替换(文本='122')
   )

   返回 启动说明([
      背景_r_启动参数,
      背景_g_launch_arg,
      背景_b_启动参数,
      节点(
         包装='turtlesim';,
         可执行='turtlesim_node',
         名字='sim';,
         参数=[{
            'background_r': 启动配置('background_r'),
            'background_g': 启动配置('background_g'),
            'background_b': 启动配置('background_b'),
         }]
      ),
   ])

该启动文件将启动 海龟模拟节点 节点启动 turtlesim 仿真,并将仿真配置参数定义并传递给各节点。

2.2 从 YAML 文件加载参数

在第二次启动中,我们将以不同的配置启动第二个海龟模拟。现在创建一个 turtlesim_world_2_launch.py 锉刀

舶来品 os

 ament_index_python.packages 舶来品 获取软件包共享目录

 启动 舶来品 启动说明
 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   配置 = os..加入(
      获取软件包共享目录('launch_tutorial';),
      'config';,
      'turtlesim.yaml';
      )

   返回 启动说明([
      节点(
         包装='turtlesim';,
         可执行='turtlesim_node',
         命名空间='turtlesim2';,
         名字='sim';,
         参数=[配置]
      )
   ])

该启动文件将启动与 海龟模拟节点 参数值直接从 YAML 配置文件中加载。在 YAML 文件中定义参数,可以方便地存储和加载大量变量。此外,YAML 文件还可以轻松地从当前的 玫瑰2 停止 列表。要了解如何做到这一点,请参阅 了解参数 教程。

现在让我们创建一个配置文件、 turtlesim.yaml/config 文件夹,我们的启动文件将加载该文件夹。

/turtlesim2/sim:
   参数:
      背景_b: 255
      背景_g: 86
      背景_r: 150

如果我们现在启动 turtlesim_world_2_launch.py 启动文件,我们将启动 海龟模拟节点 预设背景颜色。

要了解有关使用参数和 YAML 文件的更多信息,请参阅 了解参数 教程。

2.3 在 YAML 文件中使用通配符

有时,我们希望在多个节点中设置相同的参数。这些节点可能具有不同的命名空间或名称,但仍具有相同的参数。分别定义明确定义名称空间和节点名称的 YAML 文件并不高效。一种解决方案是使用通配符(可替代文本值中的未知字符),将参数应用于多个不同的节点。

现在,让我们创建一个新的 turtlesim_world_3_launch.py 文件,类似于 turtlesim_world_2_launch.py 增加一个 海龟模拟节点 节点。

...
节点(
   包装='turtlesim';,
   可执行='turtlesim_node',
   命名空间='turtlesim3';,
   名字='sim';,
   参数=[配置]
)

但是,加载相同的 YAML 文件不会影响第三个 turtlesim 世界的外观。原因是它的参数存储在另一个命名空间中,如下图所示:

/turtlesim3/sim:
   背景_b
   背景_g
   背景_r

因此,我们可以使用通配符语法,而不是为使用相同参数的同一节点创建新配置。 /** 将为每个节点分配所有参数,尽管节点名称和命名空间不同。

我们现在将更新 turtlesim.yaml/config 文件夹的方式如下:

/**:
   参数:
      背景_b: 255
      背景_g: 86
      背景_r: 150

现在包括 turtlesim_world_3_launch.py 主启动文件中的启动描述。在我们的启动描述中使用该配置文件将分配 背景_b, 背景_g背景_r 中指定的参数值。 turtlesim3/simturtlesim2/sim 节点。

3 命名空间

您可能已经注意到,我们在 turtlesim_world_2_launch.py 文件。唯一命名空间允许系统启动两个相似的节点,而不会出现节点名称或主题名称冲突。

命名空间='turtlesim2';,

但是,如果启动文件包含大量节点,为每个节点定义命名空间就会变得繁琐。为了解决这个问题,可以使用 PushROSNamespace 操作可用于为每个启动文件描述定义全局命名空间。每个嵌套节点都将自动继承该命名空间。

为此,我们首先需要删除 namespace='turtlesim2'turtlesim_world_2_launch.py 文件。之后,我们需要更新 launch_turtlesim_launch.py 包括以下几行:

 launch.actions 舶来品 群组行动
 launch_ros.actions 舶来品 PushROSNamespace

   ...
   乌龟模拟世界_2 = 包含启动描述(
      PythonLaunchDescriptionSource([os..加入(
         获取软件包共享目录('launch_tutorial';), 'launch';),
         '/turtlesim_world_2_launch.py';])
      )
   turtlesim_world_2_with_namespace = 群组行动(
     行动=[
         PushROSNamespace('turtlesim2';),
         乌龟模拟世界_2,
      ]
   )

最后,我们将 乌龟模拟世界_2turtlesim_world_2_with_namespace返回 启动说明 声明。因此,在 turtlesim_world_2_launch.py 发射说明将有一个 turtlesim2 命名空间。

4 重复使用节点

现在创建一个 broadcaster_listener_launch.py 锉刀

 启动 舶来品 启动说明
 launch.actions 舶来品 声明启动参数
 launch.substitutions 舶来品 启动配置

 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   返回 启动说明([
      声明启动参数(
         'target_frame', 默认值='turtle1';,
         描述='目标帧名称;
      ),
      节点(
         包装='turtle_tf2_py',
         可执行='turtle_tf2_broadcaster';,
         名字='broadcaster1',
         参数=[
            {'turtlename';: 'turtle1';}
         ]
      ),
      节点(
         包装='turtle_tf2_py',
         可执行='turtle_tf2_broadcaster';,
         名字='broadcaster2',
         参数=[
            {'turtlename';: 'turtle2';}
         ]
      ),
      节点(
         包装='turtle_tf2_py',
         可执行='turtle_tf2_listener',
         名字='听众';,
         参数=[
            {'target_frame': 启动配置('target_frame')}
         ]
      ),
   ])

在该文件中,我们声明了 target_frame 启动参数,默认值为 乌龟1.默认值表示启动文件可以接收一个参数并转发给它的节点,或者在没有提供参数的情况下,将默认值传递给它的节点。

之后,我们使用 乌龟_TF2_播音员 在启动过程中使用不同的名称和参数重复两次节点。这样我们就可以复制同一个节点,而不会发生冲突。

我们还启动了 乌龟_tf2_监听器 节点,并设置其 target_frame 参数。

5 参数覆盖

回想一下,我们把 broadcaster_listener_launch.py 文件。除此之外,我们还通过 target_frame 启动参数如下所示:

广播员收听节点 = 包含启动描述(
   PythonLaunchDescriptionSource([os..加入(
      获取软件包共享目录('launch_tutorial';), 'launch';),
      '/broadcaster_listener_launch.py';]),
   启动参数={'target_frame': 'carrot1';}.项目(),
   )

通过该语法,我们可以将默认的目标帧改为 胡萝卜1.如果您想 乌龟2 随后 乌龟1 而不是 胡萝卜1,只需删除定义 启动参数.这将分配 target_frame 的默认值,即 乌龟1.

6 重新映射

现在创建一个 mimic_launch.py 锉刀

 启动 舶来品 启动说明
 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   返回 启动说明([
      节点(
         包装='turtlesim';,
         可执行='模仿';,
         名字='模仿';,
         重置=[
            ('/input/pose';, '/turtle2/pose'),
            ('/output/cmd_vel';, '/turtlesim2/turtle1/cmd_vel';),
         ]
      )
   ])

该启动文件将启动 模仿 节点,该节点将向一只乌龟发出指令,让另一只乌龟跟随。该节点旨在接收主题上的目标姿势 /input/pose.在我们的案例中,我们希望将目标姿态从 /turtle2/pose 主题。最后,我们将 /output/cmd_vel 议题 /turtlesim2/turtle1/cmd_vel.这样 乌龟1 在我们的 turtlesim2 模拟世界将随之而来 乌龟2 在我们最初的海龟模拟世界中。

7 配置文件

现在让我们创建一个名为 turtlesim_rviz_launch.py.

舶来品 os

 ament_index_python.packages 舶来品 获取软件包共享目录

 启动 舶来品 启动说明
 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   rviz_config = os..加入(
      获取软件包共享目录('turtle_tf2_py'),
      'rviz';,
      'turtle_rviz.rviz';
      )

   返回 启动说明([
      节点(
         包装='rviz2';,
         可执行='rviz2';,
         名字='rviz2';,
         论点=['-d', rviz_config]
      )
   ])

该启动文件将使用在 乌龟_tf2_py 软件包。该 RViz 配置将设置世界框架,启用 TF 可视化,并以自上而下的视图启动 RViz。

8 环境变量

现在让我们创建最后一个启动文件,名为 fixed_broadcaster_launch.py 在我们的包装中。

 启动 舶来品 启动说明
 launch.actions 舶来品 声明启动参数
 launch.substitutions 舶来品 环境变量, 启动配置
 launch_ros.actions 舶来品 节点


捍卫 生成发射描述():
   返回 启动说明([
      声明启动参数(
            'node_prefix',
            默认值=[环境变量('USER';), '_'],
            描述='节点名称前缀';
      ),
      节点(
            包装='turtle_tf2_py',
            可执行='fixed_frame_tf2_broadcaster';,
            名字=[启动配置('node_prefix'), 'fixed_broadcaster';],
      ),
   ])

该启动文件展示了在启动文件中调用环境变量的方式。环境变量可用于定义或推送命名空间,以区分不同电脑或机器人上的节点。

运行启动文件

1 更新 setup.py

开放 setup.py 并添加以下几行,以便将启动文件从 发射/ 文件夹和配置文件 config/ 将被安装。该系统 数据文件 字段现在应该是这样的:

舶来品 os
 水珠 舶来品 水珠
 设置工具 舶来品 设置
...

数据文件=[
      ...
      (os..加入('分享';, 包名, 'launch';),
         水珠(os..加入('launch';, '*launch.[pxy][yma]*'))),
      (os..加入('分享';, 包名, 'config';),
         水珠(os..加入('config';, '*.yaml';))),
   ],

2 构建和运行

要最终看到代码的结果,请使用以下命令构建软件包并启动顶层启动文件:

ros2 launch launch_tutorial launch_turtlesim_launch.py

现在您将看到两个海龟模拟开始了。第一个模拟中有两只海龟,第二个模拟中有一只。在第一个模拟中 乌龟2 诞生于世界的左下方。它的目标是 胡萝卜1 帧,该帧在 X 轴上相对于 乌龟1 镜框

"(《世界人权宣言》) 乌龟模拟2/乌龟1 的行为。 乌龟2.

如果您想控制 乌龟1运行远程操作节点。

ros2 run turtlesim turtle_teleop_key

因此,您会看到类似的图片:

.././././_images/turtlesim_worlds.png

此外,RViz 应该已经启动。它将显示相对于 世界 帧,其原点位于左下角。

././././_images/turtlesim_rviz.png

摘要

在本教程中,您将了解到使用 ROS 2 启动文件管理大型项目的各种技巧和实践。