机器人系统ros2-开发实践04-ROS 2 启动文件管理大型项目的最佳实践

机器人上的大型应用通常涉及多个互连的节点,每个节点可以有许多参数。海龟模拟器中模拟多只海龟就是一个很好的例子。海龟模拟由多个海龟节点、世界配置以及 TF 广播器和监听器节点组成。在所有节点之间,存在大量影响这些节点的行为和外观的 ROS 参数。 ROS 2启动文件允许我们在一个地方启动所有节点并设置相应的参数。

通俗点就是将机器人各个功能节点启动放在代码里去配置和组合启动,并设置启动节点的一些初始化参数

1 编写启动文件

编写启动文件过程的目标之一应该是使它们尽可能可重用。这可以通过将相关节点和配置聚集到单独的启动文件中来完成。之后,可以编写专用于特定配置的顶级启动文件。这将允许在相同的机器人之间移动而无需更改启动文件。即使是从真实机器人转移到模拟机器人等改变也只需进行一些更改即可完成。

我们现在将回顾使这成为可能的顶级启动文件结构。首先,我们将创建一个启动文件,该文件将调用单独的启动文件。为此,我们在包的文件夹中创建一个launch_turtlesim.launch.py文件。

import osfrom ament_index_python.packages import get_package_share_directoryfrom launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSourcedef generate_launch_description():turtlesim_world_1 = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/turtlesim_world_1.launch.py']))turtlesim_world_2 = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/turtlesim_world_2.launch.py']))broadcaster_listener_nodes = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/broadcaster_listener.launch.py']),launch_arguments={'target_frame': 'carrot1'}.items(),)mimic_node = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/mimic.launch.py']))fixed_frame_node = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/fixed_broadcaster.launch.py']))rviz_node = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/turtlesim_rviz.launch.py']))return LaunchDescription([turtlesim_world_1,turtlesim_world_2,broadcaster_listener_nodes,mimic_node,fixed_frame_node,rviz_node])

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

2 参数配置

2.1 在launch文件中设置参数

我们将首先编写一个启动文件来启动我们的第一个turtlesim 模拟。首先,创建一个名为 的新文件turtlesim_world_1.launch.py。

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, TextSubstitutionfrom launch_ros.actions import Nodedef generate_launch_description():background_r_launch_arg = DeclareLaunchArgument('background_r', default_value=TextSubstitution(text='0'))background_g_launch_arg = DeclareLaunchArgument('background_g', default_value=TextSubstitution(text='84'))background_b_launch_arg = DeclareLaunchArgument('background_b', default_value=TextSubstitution(text='122'))return LaunchDescription([background_r_launch_arg,background_g_launch_arg,background_b_launch_arg,Node(package='turtlesim',executable='turtlesim_node',name='sim',parameters=[{'background_r': LaunchConfiguration('background_r'),'background_g': LaunchConfiguration('background_g'),'background_b': LaunchConfiguration('background_b'),}]),])

该启动文件启动turtlesim_node节点,该节点使用定义并传递给节点的模拟配置参数启动turtlesim模拟。

2.2 从YAML文件加载参数

在第二次启动中,我们将使用不同的配置启动第二次turtlesim 模拟。现在创建一个turtlesim_world_2.launch.py文件。

import osfrom ament_index_python.packages import get_package_share_directoryfrom launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():config = os.path.join(get_package_share_directory('launch_tutorial'),'config','turtlesim.yaml')return LaunchDescription([Node(package='turtlesim',executable='turtlesim_node',namespace='turtlesim2',name='sim',parameters=[config])])

turtlesim_node此启动文件将使用直接从 YAML 配置文件加载的参数值启动相同的文件。在 YAML 文件中定义实参和参数可以轻松存储和加载大量变量。此外,还可以轻松地从当前列表导出 YAML 文件。

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

/turtlesim2/sim:ros__parameters:background_b: 255background_g: 86background_r: 150

如果我们现在启动turtlesim_world_2.launch.py启动文件,我们将以预先配置的背景颜色启动turtlesim_node

2.3 在YAML文件中使用通配符

有时我们想要在多个节点中设置相同的参数。这些节点可以具有不同的命名空间或名称,但仍然具有相同的参数。定义显式定义命名空间和节点名称的单独 YAML 文件效率不高。解决方案是使用通配符(其充当文本值中未知字符的替换)将参数应用于多个不同的节点。

现在让我们创建一个turtlesim_world_3.launch.py类似于turtlesim_world_2.launch.py包含另一个turtlesim_node节点的新文件。

...
Node(package='turtlesim',executable='turtlesim_node',namespace='turtlesim3',name='sim',parameters=[config]
)

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

/turtlesim3/sim:background_bbackground_gbackground_r

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

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

/**:ros__parameters:background_b: 255background_g: 86background_r: 150

现在将turtlesim_world_3.launch.py启动描述包含在我们的主启动文件中。在我们的启动描述中使用该配置文件会将background_b、background_g和参数分配给和节点background_r中的指定值。turtlesim3/simturtlesim2/sim

3 命名空间

您可能已经注意到,我们在turtlesim_world_2.launch.py文件中定义了 turlesim 世界的命名空间。独特的命名空间允许系统启动两个相似的节点,而不会出现节点名称或主题名称冲突。

namespace='turtlesim2',

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

为此,首先,我们需要从文件namespace='turtlesim2’中删除该行turtlesim_world_2.launch.py。之后,我们需要更新launch_turtlesim.launch.py以包含以下行:

from launch.actions import GroupAction
from launch_ros.actions import PushRosNamespace...turtlesim_world_2 = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/turtlesim_world_2.launch.py']))turtlesim_world_2_with_namespace = GroupAction(actions=[PushRosNamespace('turtlesim2'),turtlesim_world_2,])

最后,我们将语句中的turtlesim_world_2to替换掉。因此,启动描述中的每个节点都将有一个命名空间。turtlesim_world_2_with_namespacereturn LaunchDescriptionturtlesim_world_2.launch.pyturtlesim2

4 复用节点

现在创建一个broadcaster_listener.launch.py文件。

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfigurationfrom launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([DeclareLaunchArgument('target_frame', default_value='turtle1',description='Target frame name.'),Node(package='turtle_tf2_py',executable='turtle_tf2_broadcaster',name='broadcaster1',parameters=[{'turtlename': 'turtle1'}]),Node(package='turtle_tf2_py',executable='turtle_tf2_broadcaster',name='broadcaster2',parameters=[{'turtlename': 'turtle2'}]),Node(package='turtle_tf2_py',executable='turtle_tf2_listener',name='listener',parameters=[{'target_frame': LaunchConfiguration('target_frame')}]),])

在此文件中,我们声明了target_frame默认值为 的启动参数turtle1。默认值意味着启动文件可以接收参数以转发到其节点,或者如果未提供参数,它将传递默认值到其节点。

之后,我们turtle_tf2_broadcaster在启动期间使用不同的名称和参数两次使用该节点。这允许我们复制相同的节点而不会发生冲突。

我们还启动一个turtle_tf2_listener节点并设置target_frame上面声明和获取的参数。

5 参数覆盖

回想一下,我们broadcaster_listener.launch.py在顶级启动文件中调用了该文件。除此之外,我们还传递了它的target_frame启动参数,如下所示:

broadcaster_listener_nodes = IncludeLaunchDescription(PythonLaunchDescriptionSource([os.path.join(get_package_share_directory('launch_tutorial'), 'launch'),'/broadcaster_listener.launch.py']),launch_arguments={'target_frame': 'carrot1'}.items(),)

此语法允许我们将默认目标目标框架更改为carrot1。如果您想turtle2跟随turtle1而不是跟随carrot1,只需删除定义的行即可launch_arguments。这将分配target_frame其默认值,即turtle1。

6 重新映射

现在创建一个mimic.launch.py文件。

from launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',executable='mimic',name='mimic',remappings=[('/input/pose', '/turtle2/pose'),('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),])])

该启动文件将启动mimic节点,该节点将向一个turtlesim发出命令以跟随另一个。该节点旨在接收主题上的目标姿势/input/pose。在我们的例子中,我们想要从/turtle2/pose主题重新映射目标姿势。最后,我们将/output/cmd_vel主题重新映射到/turtlesim2/turtle1/cmd_vel。我们的模拟世界turtle1中的这种方式将遵循我们最初的turtlesim世界。turtlesim2turtle2

7 配置文件

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

import osfrom ament_index_python.packages import get_package_share_directoryfrom launch import LaunchDescription
from launch_ros.actions import Nodedef generate_launch_description():rviz_config = os.path.join(get_package_share_directory('turtle_tf2_py'),'rviz','turtle_rviz.rviz')return LaunchDescription([Node(package='rviz2',executable='rviz2',name='rviz2',arguments=['-d', rviz_config])])

此启动文件将使用包中定义的配置文件启动 RViz turtle_tf2_py。此 RViz 配置将设置世界框架、启用 TF 可视化并以自上而下的视图启动 RViz。

8 环境变量

现在让我们创建fixed_broadcaster.launch.py包中调用的最后一个启动文件。

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import EnvironmentVariable, LaunchConfiguration
from launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([DeclareLaunchArgument('node_prefix',default_value=[EnvironmentVariable('USER'), '_'],description='prefix for node name'),Node(package='turtle_tf2_py',executable='fixed_frame_tf2_broadcaster',name=[LaunchConfiguration('node_prefix'), 'fixed_broadcaster'],),])

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

9.运行启动文件

1 更新setup.py
打开setup.py并添加以下行,以便安装该launch/文件夹中的启动文件和配置文件。config/该data_files字段现在应如下所示:

import os
from glob import glob
from setuptools import setup
...data_files=[...(os.path.join('share', package_name, 'launch'),glob(os.path.join('launch', '*.launch.py'))),(os.path.join('share', package_name, 'config'),glob(os.path.join('config', '*.yaml'))),],

9.2 构建并运行

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

ros2 launch launch_tutorial launch_turtlesim.launch.py

您现在将看到两个turtlesim 模拟已启动。第一个有两只乌龟,第二个有一只。在第一个模拟中,turtle2在世界的左下角产生。它的目标是到达carrot1相对于框架在 x 轴上 5 米远的框架turtle1。

第二turtlesim2/turtle1个中的 旨在模仿 的行为turtle2。

如果您想控制turtle1,请运行 teleop 节点。

ros2 run turtlesim turtle_teleop_key

结果,你会看到类似的图片:

在这里插入图片描述

除此之外,RViz 应该已经开始了。它将显示相对于world原点位于左下角的框架的所有海龟框架。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/5847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《与 Apollo 共创生态——Apollo7周年大会干货分享》

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 阿波罗X企业自动驾驶解决方案自动驾驶技术提升与挑战自动驾驶系统功能与性能的详细解析<td alig…

python可视化学习笔记折线图问题-起始点问题

问题描述&#xff1a; 起始点的位置不对 from pyecharts.charts import Line import pyecharts.options as opts # 示例数据 x_data [1,2,3,4,5] y_data [1, 2, 3, 4, 5] # 创建 Line 图表 line Line() line.add_xaxis(x_data) line.add_yaxis("test", y_data) li…

数字阅览室的优势

近年来&#xff0c;随着我国社会经济的快速发展&#xff0c;信息技术的发展也得到了广泛的应用&#xff0c;我国高校图书馆和图书管理系统进入了快速发展的快车道。在这种环境下&#xff0c;数字化阅览室在校园中非常流行。数字阅览室是一套真正意义上的面向中小学图书管理、浏…

Golang | Leetcode Golang题解之第58题最后一个单词的长度

题目&#xff1a; 题解&#xff1a; func lengthOfLastWord(s string) (ans int) {index : len(s) - 1for s[index] {index--}for index > 0 && s[index] ! {ansindex--}return }

python基础语法--函数

一、函数概述 函数就是执行特定任务完成特定功能的一段代码。可以在程序中将某一段代码定义成函数&#xff0c;并指定一个函数名和接收的输入&#xff08;参数&#xff09;&#xff0c;这样就可以在程序的其他地方通过函数名多次调用并执行该段代码了。 每次调用执行后&#…

【华为】路由综合实验(基础)

【华为】路由综合实验 实验需求拓扑配置AR1AR2AR3AR4AR5PC1PC2 查看通信OSPF邻居OSPF路由表 BGPBGP邻居BGP 路由表 配置文档 实验需求 ① 自行规划IP地址 ② 在区域1里面 启用OSPF ③ 在区域1和区域2 启用BGP&#xff0c;使AR4和AR3成为eBGP&#xff0c;AR4和AR5成为iBGP对等体…

AI 图像无损放大器:多平台支持,轻松上手 | 开源日报 No.241

upscayl/upscayl Stars: 25.5k License: AGPL-3.0 upscayl 是一个免费开源的 AI 图像放大器&#xff0c;支持 Linux、MacOS 和 Windows 平台&#xff0c;并且秉承着“Linux 优先”理念构建。 使用先进的 AI 算法对低分辨率图像进行放大和增强在不损失质量的情况下放大图像&am…

ubuntu开启message文件

环境&#xff1a;ubuntu 20.04 1、首先需要修改 /etc/rsyslog.d/50-default.conf 文件&#xff1b;源文件中message被注释&#xff0c;如下图&#xff1a; 2、打开注释&#xff1a; 3、重启服务 systemctl restart rsyslog.service 如此即可&#xff01;

嵌入式学习59-ARM7(自动设备号和混杂设备)

知识零碎&#xff1a; 头文件查找&#xff1a; /arm/路径下的头文件 linux驱动程序的编写&#xff0c;编译&#xff0c;运行过程 -------------------------------------------------------------------------------------------------------------------------------- 1.…

java-函数式编程-函数对象

定义 什么是合格的函数&#xff1f;无论多少次执行函数&#xff0c;只要输入一样&#xff0c;输出就不会改变 对象方法的简写 其实在类中&#xff0c;我们很多参数中都有一个this&#xff0c;被隐藏传入了 函数也可以作为对象传递&#xff0c;lambda就是很好的例子 函数式接口中…

常用的时间序列分析方法总结和代码示例

时间序列是最流行的数据类型之一。视频&#xff0c;图像&#xff0c;像素&#xff0c;信号&#xff0c;任何有时间成分的东西都可以转化为时间序列。 在本文中将在分析时间序列时使用的常见的处理方法。这些方法可以帮助你获得有关数据本身的见解&#xff0c;为建模做好准备并…

搭建vue3组件库(三): CSS架构之BEM

文章目录 1. 通过 JS 生成 BEM 规范名称1.1 初始化 hooks 目录1.2 创建 BEM 命名空间函数1.3 通过 SCSS 生成 BEM 规范样式 2. 测试 BEM 规范 BEM 是由 Yandex 团队提出的一种 CSS 命名方法论&#xff0c;即 Block&#xff08;块&#xff09;、Element&#xff08;元素&#xf…

【C++】哈希的应用---位图

目录 1、引入 2、位图的概念 3、位图的实现 ①框架的搭建 ②设置存在 ③设置不存在 ④检查存在 ​4、位图计算出现的次数 5、完整代码 1、引入 我们可以看一道面试题 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数…

2023 广东省大学生程序设计竞赛(部分题解)

目录 A - Programming Contest B - Base Station Construction C - Trading D - New Houses E - New but Nostalgic Problem I - Path Planning K - Peg Solitaire A - Programming Contest 签到题&#xff1a;直接模拟 直接按照题目意思模拟即可&#xff0c;为了好去…

RCD吸收电路:开关电源高频干扰的有效消除器

开关电源中除了我们常规介绍的差模噪声源和共模噪声源&#xff0c;还存在一些其它的噪声源也应该解决&#xff0c;这些高频噪声源同样会带来电磁兼容问题&#xff0c;因此我们需要关注。这里介绍两种干扰源&#xff0c;一种是MOS管的通断带来的高频振荡噪声&#xff0c;另一种是…

web安全---CSRF漏洞/OWASP-CSRFTester的使用

what 跨站请求伪造 Cross Site Request Forgery how 攻击者诱骗点击恶意网页&#xff0c;盗用&#xff08;伪造&#xff09;受害者的身份&#xff0c;以受害者的名义向服务器发送恶意请求,而这种恶意请求在服务端看起来是正常请求 CSRF&&XSS区别 他们最本质区别就…

十大排序算法之->插入排序

一、插入排序 插入排序的基本思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而形成一个新的、记录数增1的有序表。 排序过程&#xff1a; 1、外层循环&#xff1a;从第二个元素开始&#xff0c;依次选取未排序的元素。 2、内层循环&#xff1a;将当前选取的元素…

C++成员初始化列表

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升&#xff0c;那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题 文章目录 引入成员初始化列表用成员初始化列表优化上面的代码成员初始化列表展开成员初始化列表的潜在危险 参考资料 引入 考虑下面这…

IGM焊接机器人RTE 495伺服电机维修详情一览

在当今科技迅速发展的时代&#xff0c;机器人已成为各行各业不可或缺的重要工具。IGM机器人便是其中之一&#xff0c;其工业机械手伺服马达作为机器人的关键部件&#xff0c;确保机器人能够高效、稳定地运行。当出现IGM焊接机器人RTE 495伺服电机故障问题时&#xff0c;及时进行…

Kafka介绍、安装以及操作

Kafka消息中间件 1.Kafka介绍 1.1 What is Kafka&#xff1f; 官网&#xff1a; https://kafka.apache.org/超过 80% 的财富 100 强公司信任并使用 Kafka &#xff1b;Apache Kafka 是一个开源分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成…