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

news2024/12/25 23:15:44

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

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

1 编写启动文件

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

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

import os

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource


def 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, TextSubstitution

from launch_ros.actions import Node


def 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 os

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch_ros.actions import Node


def 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: 255
      background_g: 86
      background_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_b
   background_g
   background_r

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

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

/**:
   ros__parameters:
      background_b: 255
      background_g: 86
      background_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 LaunchConfiguration

from launch_ros.actions import Node


def 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 Node


def 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 os

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch_ros.actions import Node


def 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 Node


def 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.coloradmin.cn/o/1638725.html

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

相关文章

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

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

罗宾斯《管理学》第15版笔记/课后习题/考研真题答案

第Ⅰ篇 管理导论 第1章 工作场所中的管理者和你 1.1 知识结构导图 1.2 考点难点归纳 1.3 课后习题详解 1.4 考研真题详解 附加模块一 管理史 知识结构导图 考点难点归纳 课后习题详解 考研真题详解 第2章 决 策 2.1 知识结构导图 2.2 考点难点归纳 2.3 课后习题详解…

C++string类使用大全

目录 温馨提示:这篇文章有约两万字 什么是string类? 一. 定义和初始化string对象 1.string的构造函数的形式: 2.拷贝赋值运算符 3.assign函数 二.string对象上的操作 1.读写string对象 2.读取未知数量的string对象 3.使用getline …

STM32中断系统详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 中断基本概念 2. STM32中断 3. NVIC的基本组件 3.1 NVIC的基本组件 3.2 NVIC的优先级 4. EXTI外部中断 4.1 基本概念 4.2 基本结构 5. AFIO 1. 中断基本概念 中断(Interrupt&…

信息技术内涵及意义

一、信息技术及其演进趋势 (一)信息技术概况概念 信息技术(Information Technology,IT)指“应用在信息加工和处理中的科学、技术与工程的训练方法与管理技巧;上述方法和技巧的应用;计算机及其…

mac idea 下载spring 源码遇到的问题

一、Kotlin: warnings found and -Werror specified 这个问题网上看了很多文章多说是缺少cglib、objenesis包。然后执行了 实际还是没有什么用 解决: 最后自己看了一下前面一个警告。说的就是版本太低。所以我觉得是这个前置问题导致的 然后搜索了改这个Kotlin版本…

《ElementUI 基础知识》el-tabs header 监听鼠标中键滚动时左右滑动(ElementPlus同样适用)

前言 收到需求,可监听 el-tabs 头在鼠标 hover 时。滑动鼠标中键,可左右滑动! 效果 鼠标中键上下滑动时;向上滑,向左移动;向下滑,向右移动; 实现 代码56 - 60行,添加…

前端开发框架Vue

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl Vue概述 Vue.js(简称Vue)是由尤雨溪(Evan You)创建并维护的一款开源前端开发框架。Vue以其轻量级、易上手和高度灵活的特点&…

【Transformer系列(1)】self-attention自注意力

一、self-attention流程 自注意力机制和注意力机制的区别在于,注意力机制中的Q(查询向量),K(键向量),V(值向量)是同源的,而一般的注意力机制,Q和…

【MATLAB】GUI初步设计

MATLAB界面设计 前言一、基本步骤1.1 创建GUI文件1.2 界面设计 总结 前言 为了完成图像处理的作业,简直就是生活不易啊 找到一个很棒的教学视频 基于MATLAB的GUI界面设计流程讲解 一、基本步骤 1.1 创建GUI文件 由于在写博文之前我已经创建好文件了,…

Istio基础知识

一、什么是Istio Istio 提供⼀种简单的⽅式来为已部署的服务建⽴⽹络,该⽹络具有 负载均衡、服务间认证、监控等功能,只需要对服务的代码进⾏⼀点或不需要做任何改动。想要让服务⽀持 Istio,只需要在您的环境中部署⼀个特殊的 sidecar 代 理&…

【Linux驱动】USB协议

🐱作者:一只大喵咪1201 🐱专栏:《Linux驱动》 🔥格言:你只管努力,剩下的交给时间! USB协议 😼认识USB🙀USB的电气信号低速和全速信号电平低速和全速的数据信…

2024-5-1我把QQ群聊天记录分析工具重写了一下

【下载地址】 https://www.lanzoub.com/b00rn0g47e 密码:9hww 【项目背景】 2020年我用Tkinter写过一个QQ群聊天记录分析的工具exe,后续也写过一个纯JS前端的版本,前阵子有个用户反馈不能用了,顺便看能不能加入一个分析关键词的功能&…

基于Python的在线学习与推荐系统设计与实现(论文+源码)-kaic

题目:在线学习与推荐系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本在线学习与推荐系统就是在这样的大环境下诞生&#xff0…

安装库后JupyterLab一直报ModuleNotFoundError问题解决

背景: 先安装的Python3.10,安装在默认路径: C:\Users\#用户名省略#\AppData\Local\Programs\Python\Python310\ 后安装的Anaconda,更改过路径在D盘: D:\ProgramData\anaconda3 此时C盘Python安装路径下Scripts文件…

十二、视觉内容生成模型

1 判别式模型和生成式模型 1. 判别式模型 学习策略函数 Y f ( X ) Yf(X) Yf(X)或者条件概率 P ( Y ∣ X ) P(Y|X) P(Y∣X)不能反映训练数据本身的特性学习成本低,需要的训练样本少无法转为生成式 2. 生成式模型 学习联合概率密度分布 P ( X ∣ Y ) P(X|Y) P(X∣…

【通信中间件】Fdbus HellWorld实例

Fdbus实例教程 Fdbus简介 Fdbus 全称 Fast Distributed Bus(高速分布式总线),提供IPCRPC功能。适用于多种OS: LinuxQNXAnroidOSWindow Fdbus本质是Socket,IPC基于Unix domain socket,RPC基于TCP。使用G…

Elasticsearch 索引 blocks:深入探讨数据保护

Elasticsearch 作为搜索和分析数据的首选分布式引擎在技术领域脱颖而出,尤其是在处理日志、事件和综合文本搜索时。 它的与众不同之处在于它如何让你使用各种块选项调整对其索引的访问。 这对于那些负责技术项目的人(比如管理员和编码员)来说…

AI手机,走入小径分岔的花园

博尔赫斯在他的成名作《小径分岔的花园》里,描述了一种奇妙的世界观:一个可能性被选择之后,出现了许多不同的后世,许多不同的时间。 在现实世界中,选择不会如此神奇。但站在岔路口的抉择,也一定会带来结果的…