ROS2与launch入门教程

您所在的位置:网站首页 ros启动launch文件 ROS2与launch入门教程

ROS2与launch入门教程

2023-03-03 01:30| 来源: 网络整理| 查看: 265

ROS2与launch入门教程-使用ROS2 launch文件启动大型项目

说明:  

学习使用ROS 2 launch文件管理大型项目的最佳实践

背景:

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

介绍:

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

顶层结构

编写启动文件过程中的目标之一应该是使它们尽可能可重用。 这可以通过将相关节点和配置集群到单独的启动文件中来完成。 之后,可以编写专用于特定配置的顶级启动文件。 这将允许在完全不更改启动文件的情况下在相同的机器人之间移动。 即使是从真正的机器人移动到模拟机器人这样的变化,也只需进行少量更改即可完成。 我们现在将讨论使这成为可能的顶级启动文件结构。 首先,我们将创建一个launch文件,该文件将调用单独的启动文件。 为此,让我们在 launch_tutorial 包的 /launch 文件夹中创建一个 launch_turtlesim.launch.py 文件。 cd ~/launch_ws/src/launch_tutorial/launch vim 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 节点。 设计提示:顶级启动文件应该很短,包含与应用程序的子组件相对应的其他文件的包含,以及经常更改的参数。 用以下方式编写启动文件可以很容易地换出系统的一部分,我们稍后会看到。 但是,由于性能和使用原因,某些节点或启动文件必须单独启动。 设计提示:在决定您的应用程序需要多少顶级启动文件时,请注意权衡取舍。

参数:

我们将首先编写一个启动文件,该文件将启动我们的第一个 turtlesim 模拟。 1.首先,创建一个名为 turtlesim_world_1.launch.py 的新文件 cd ~/launch_ws/src/launch_tutorial/launch vim 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.从YAML 文件加载参数 在第二次启动中,我们将使用不同的配置启动第二次 turtlesim 模拟。 现在创建一个 turtlesim_world_2.launch.py 文件。 cd ~/launch_ws/src/launch_tutorial/launch vim 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] ) ]) 此启动文件将使用直接从 YAML 配置文件加载的参数值启动相同的 turtlesim_node。 在 YAML 文件中定义参数和参数可以轻松存储和加载大量变量。 此外,YAML 文件可以很容易地从当前的 ros2 参数列表中导出。 要了解如何执行此操作,请参阅了解 ROS 2 参数教程。 现在让我们在包的 /config 文件夹中创建一个配置文件,turtlesim.yaml,它将由我们的启动文件加载。 mkdir -p ~/launch_ws/src/launch_tutorial/config cd ~/launch_ws/src/launch_tutorial/config vim turtlesim.yaml 内容如下: /turtlesim2/sim: ros__parameters: background_b: 255 background_g: 86 background_r: 150

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

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

3.在 YAML 文件中使用通配符

有时我们想在多个节点中设置相同的参数。

这些节点可能具有不同的命名空间或名称,但仍具有相同的参数。

定义显式定义命名空间和节点名称的单独 YAML 文件效率不高。

一种解决方案是使用通配符,作为文本值中未知字符的替换,将参数应用于几个不同的节点。

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

cd ~/launch_ws/src/launch_tutorial/launch vim turtlesim_world_3.launch.py 增加如下内容 ... Node( package='turtlesim', executable='turtlesim_node', namespace='turtlesim3', name='sim', parameters=[config] ) 然而,加载相同的 YAML 文件不会影响第三个 turtlesim 世界的外观。 原因是它的参数存储在另一个命名空间下,如下所示: /turtlesim3/sim: background_b background_g background_r 因此,我们可以使用通配符语法,而不是为使用相同参数的同一节点创建新配置。 /** 将分配每个节点中的所有参数,尽管节点名称和命名空间不同。 我们现在将按照以下方式更新 /config 文件夹中的 turtlesim.yaml: /**: ros__parameters: background_b: 255 background_g: 86 background_r: 150 现在在我们的主启动文件中包含 turtlesim_world_3.launch.py 启动描述。 在我们的启动描述中使用该配置文件会将 background_b、background_g 和 background_r 参数分配给 turtlesim3/sim 和 turtlesim2/sim 节点中的指定值。

命名空间

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

但是,如果启动文件包含大量节点,则为每个节点定义命名空间可能会变得乏味。

为了解决这个问题,可以使用 PushRosNamespace 操作为每个启动文件描述定义全局命名空间。

每个嵌套节点都会自动继承该命名空间。

为此,首先,我们需要从 turtlesim_world_2.launch.py 文件中删除 namespace='turtlesim2' 行。

之后,我们需要更新 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, ] ) 最后,我们在 return LaunchDescription 语句中将 turtlesim_world_2 替换为 turtlesim_world_2_with_namespace。 因此,turtlesim_world_2.launch.py 启动描述中的每个节点都会有一个 turtlesim2 命名空间。

重用节点

现在创建一个 broadcaster_listener.launch.py 文件。 cd ~/launch_ws/src/launch_tutorial/launch vim 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')} ] ), ])

 - 在这个文件中,我们用默认值turtle1 声明了target_frame 启动参数。

默认值意味着启动文件可以接收一个参数转发给它的节点,或者如果没有提供参数,它会将默认值传递给它的节点。 之后,我们在启动期间使用不同的名称和参数两次使用 turtle_tf2_broadcaster 节点。 这允许我们复制同一个节点而不会发生冲突。 我们还启动了一个 turtle_tf2_listener 节点并设置了我们在上面声明和获取的 target_frame 参数。

参数覆盖

回想一下,我们在顶级启动文件中调用了 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。

重新映射

现在创建一个mimic.launch.py 文件 cd ~/launch_ws/src/launch_tutorial/launch vim 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'), ] ) ]) 该启动文件将启动模拟节点,该节点将向一个turtlesim 发出命令以跟随另一个。 该节点旨在接收话题/输入/姿势上的目标姿势。 在我们的例子中,我们想要从 /turtle2/pose 话题重新映射目标姿势。 最后,我们将 /output/cmd_vel 话题重新映射到 /turtlesim2/turtle1/cmd_vel。 这样,我们的turtlesim2 模拟世界中的turtle1 将跟随我们最初的turtlesim 世界中的turtle2。

配置文件

现在让我们创建一个名为 turtlesim_rviz.launch.py 的文件 cd ~/launch_ws/src/launch_tutorial/launch vim 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] ) ]) 此启动文件将使用在 turtle_tf2_py 包中定义的配置文件启动 RViz。 此 RViz 配置将设置世界框架,启用 TF 可视化,并以自上而下的视图启动 RViz。

环境变量

现在让我们在包中创建最后一个名为 fixed_broadcaster.launch.py 的启动文件 cd ~/launch_ws/src/launch_tutorial/launch vim 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'], ), ]) 该启动文件显示了在启动文件中可以调用环境变量的方式。 环境变量可用于定义或推送命名空间,以区分不同计算机或机器人上的节点。

重新编译:

打开 setup.py 并添加以下行,以便安装 launch/ 文件夹中的启动文件和 config/ 中的配置文件。 cd ~/launch_ws/src/launch_tutorial/ vim setup.py data_files 字段现在应该如下所示: 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'))), ], 指定构建launch_tutorial 包,并使用symlink的方式 cd ~/launch_ws/ colcon build --symlink-install --packages-select launch_tutorial 加载工作空间 . ~/launch_ws/install/local_setup.bash 要最终查看我们的代码的结果,请构建包并使用以下命令启动顶级启动文件: ros2 launch launch_tutorial launch_turtlesim.launch.py 您现在将看到两个turtlesim 模拟已启动。 第一个有两只乌龟,第二个有一只。 在第一个模拟中,turtle2 生成在世界的左下角。 它的目标是到达相对于turtle1 框架在x 轴上5 米远的carrot1 框架。 第二个中的turtlesim2/turtle1 旨在模仿turtle2 的行为。 如果要控制turtle1,请运行teleop 节点。 ros2 run turtlesim turtle_teleop_key 效果图:

请输入图片描述

除此之外,RViz 应该已经启动。 它将显示相对于世界框架的所有海龟框架,其原点位于左下角。

请输入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3