管理大型项目
目标 学习使用 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/sim
和 turtlesim2/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,
]
)
最后,我们将 乌龟模拟世界_2
至 turtlesim_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
因此,您会看到类似的图片:

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

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