ROS2 launch文件同时引入yaml文件参数和自定义变量参数

0 背景

在ROS中,launch工具可以帮助用户同时启动多个节点,以及引入多种设置如参数导入、节点名重映射等。在ROS1中,launch文件通过xml语言编写,后缀名为.launch;而ROS2在xml的基础上(后缀名为.xml),新增加了两种launch文件的编写方式——python(后缀名为.launch.py)和yaml(后缀名为.yaml)。尽管如此,官方demo以及一些比较受欢迎的ROS2项目,使用的launch文件的编写方式基本为python,而python的灵活性之大与受众之广也是得到众多开发者的认可的。

关于ROS2 launch文件的更多详细资料,笔者把一些常用链接整理如下:

1 问题的提出

铺垫结束,回到本文关注的问题!
如何在ROS2 launch.py文件中,同时引入yaml和自定义变量参数?

之所以会提出这个问题,是因为在实际工程中,笔者发现当进行团队协作或者程序在不同平台上移植的时候,经常会遇到目录不统一的问题,而这些目录是作为参数存放在yaml中的。比如,某个文件存放在张三的/home/zhangsan/resource下面,而李四要将yaml文件中的该路径修改为/home/lisi/resource。又比如,某个文件存放在张三的/home/zhangsan/folder_A/folder_B,而李四习惯将其存放在/home/lisi/folder_C。如果每次从GitHub或者GitLab pull下他人的代码,都需要修改 ,显然不是长久之际。于是自然地想到,有没有这样一种方式,能够避免路径修改,使代码即拿即用呢?

2 问题的解决

通过阅读官方文档,可以知道,在launch文件中启动节点,可以直接定义ros参数:
在这里插入图片描述

也可以通过下面这种方式导入yaml文件:
在这里插入图片描述

一般我们使用yaml文件时,都是因为参数数量比较大。用yaml整理,更加简洁直观。但是,对于上述情况,笔者还是推荐将涉及路径的参数写在launch文件里,毕竟ROS2已经支持python了,使用python的优势马上就会介绍到。

  • 第一个,只需知道yaml文件相对路径

    from ament_index_python.packages import get_package_share_directory
    config = os.path.join(
      get_package_share_directory('launch_tutorial'),
      'config',
      'turtlesim.yaml'
      )
    

    这个get_package_share_memory会获取功能包所在的路径(在install文件夹内,而不是源代码文件夹),而yaml文件在功能包内部,只需知道相对路径即可,从而避免对yaml绝对路径的编辑与修改。

  • 第二个,其他资源文件可以用~代替主目录

    import os
    resource_path = os.path.expanduser('~/demo/resource/dataset') # 在.launch.py中可以使用~表示主目录,从而提高程序的可移植性
    
  • 第三个,yaml文件可以和自定义参数同时使用,从而实现一般参数和特殊参数(如路径)的统一
    见下面的例程,这里使用的是composable node,但是对于一般的node也是可以的,可以看上面的两个代码截图,有parameter项。

    扫描二维码关注公众号,回复: 15669643 查看本文章
    import launch
    from launch_ros.actions import ComposableNodeContainer
    from launch_ros.descriptions import ComposableNode
    
    # 以下两个包是额外import进来的,用于处理目录和字符串
    import os
    from ament_index_python.packages import get_package_share_directory
    
    
    def generate_launch_description():
        """Generate launch description with multiple components."""
    
        yaml_file = os.path.join(get_package_share_directory("onboard_pkg"), "config", "onboard_pkg.yaml")
        # yaml文件在config目录下,记得在CMakeLists.txt中写install相关内容(config目录需要加入到install目录中去)
    
        resource_path = os.path.expanduser('~/demo/resource/dataset') # 在.launch.py中可以使用~表示主目录,从而提高程序的可移植性
        # 建议涉及到目录的参数都在launch文件中写,以提高程序的可移植性
        individual_params = {
          
          "resource_path": resource_path} # 用字典自定义参数,key为resource_path,值为路径
        lidar_node_params = [individual_params,yaml_file] # 列表元素可以是自定义参数,也可以是yaml文件的路径
        
        container = ComposableNodeContainer(
                name='my_container',
                namespace='',
                package='rclcpp_components',
                executable='component_container',
                composable_node_descriptions=[ # 多个节点的情况,可以在List中写多个ComposableNode
                    ComposableNode( # 只需要修改这个实例的内容
                        package='onboard_pkg',
                        plugin='onboard_pkg::Lidar',
                        name='lidar_node', 
                        parameters=lidar_node_params)
                ],
                output='screen',
        )
    
        return launch.LaunchDescription([container])
    

3 多问一句为什么

为什么可以把launch文件中自定义的参数和yaml中给的参数同时传给节点?
选中Node中的parameter跳转到其定义,再在类的初始化函数中找到parameter相关的函数normalize_parameters函数进行跳转,会发现parameter list支持三种类型,并且会根据元素的类型,进行相应的处理,从而可以支持同时传入yaml和自定义参数
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45910027/article/details/131452988