【ROS2】launch启动文件编写(重点探讨python方式)

ros1和ros2其中一个很大区别之一就是launch的编写方式。在ros1中采用xml格式编写launch,而ros2保留了xml格式launch,还另外引入了python和yaml编写方式。选择哪种编写取决于每位开发人员的爱好,但是ros2官方推荐使用python方式编写,理由如下:

1.Python 是一种脚本语言,使用灵活,python库强大,可以直接引入。
2.ros2 launch框架本身是用python编写,由于亲和力高python可以直接调用一些高级特性 ,而XML 和 YAML 可能无法调用。

当然了,用python编写也不是全部都是优点没有缺点,不然还要引入xml,yaml干嘛对吧?答案就是用 Python 编写的启动文件可能比 XML 或 YAML 编写的启动文件更复杂、更冗长。

一.运行方式

1.启动方式

而不管是哪种格式的launch,启动方式一样:
ros2 launch <package_name> <launch_file_name>
或者还可以直接启动指定路径文件:
ros2 launch <path_to_launch_file>

2.设置参数

ros2 launch <package_name> <launch_file_name> background_r:=255
或者这样:
ros2 launch <path_to_launch_file> background_r:=255

二、瞻仰:官网示例 (不想看的可直接看目录三 ^ . ^)

1.python编写launch示例

# example.launch.py

import os

from ament_index_python import get_package_share_directory

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.actions import GroupAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.actions import Node
from launch_ros.actions import PushRosNamespace


def generate_launch_description():

    # args that can be set from the command line or a default will be used
    background_r_launch_arg = DeclareLaunchArgument(
        "background_r", default_value=TextSubstitution(text="0")
    )
    background_g_launch_arg = DeclareLaunchArgument(
        "background_g", default_value=TextSubstitution(text="255")
    )
    background_b_launch_arg = DeclareLaunchArgument(
        "background_b", default_value=TextSubstitution(text="0")
    )
    chatter_ns_launch_arg = DeclareLaunchArgument(
        "chatter_ns", default_value=TextSubstitution(text="my/chatter/ns")
    )

    # include another launch file
    launch_include = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(
                get_package_share_directory('demo_nodes_cpp'),
                'launch/topics/talker_listener.launch.py'))
    )
    # include another launch file in the chatter_ns namespace
    launch_include_with_namespace = GroupAction(
        actions=[
            # push_ros_namespace to set namespace of included nodes
            PushRosNamespace('chatter_ns'),
            IncludeLaunchDescription(
                PythonLaunchDescriptionSource(
                    os.path.join(
                        get_package_share_directory('demo_nodes_cpp'),
                        'launch/topics/talker_listener.launch.py'))
            ),
        ]
    )

    # start a turtlesim_node in the turtlesim1 namespace
    turtlesim_node = Node(
            package='turtlesim',
            namespace='turtlesim1',
            executable='turtlesim_node',
            name='sim'
        )

    # start another turtlesim_node in the turtlesim2 namespace
    # and use args to set parameters
    turtlesim_node_with_parameters = Node(
            package='turtlesim',
            namespace='turtlesim2',
            executable='turtlesim_node',
            name='sim',
            parameters=[{
                "background_r": LaunchConfiguration('background_r'),
                "background_g": LaunchConfiguration('background_g'),
                "background_b": LaunchConfiguration('background_b'),
            }]
        )

    # perform remap so both turtles listen to the same command topic
    forward_turtlesim_commands_to_second_turtlesim_node = Node(
            package='turtlesim',
            executable='mimic',
            name='mimic',
            remappings=[
                ('/input/pose', '/turtlesim1/turtle1/pose'),
                ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
            ]
        )

    return LaunchDescription([
        background_r_launch_arg,
        background_g_launch_arg,
        background_b_launch_arg,
        chatter_ns_launch_arg,
        launch_include,
        launch_include_with_namespace,
        turtlesim_node,
        turtlesim_node_with_parameters,
        forward_turtlesim_commands_to_second_turtlesim_node,
    ])

2.xml编写launch示例

<!-- example.launch.xml -->

<launch>

  <!-- args that can be set from the command line or a default will be used -->
  <arg name="background_r" default="0"/>
  <arg name="background_g" default="255"/>
  <arg name="background_b" default="0"/>
  <arg name="chatter_ns" default="my/chatter/ns"/>

  <!-- include another launch file -->
  <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener.launch.py"/>
  <!-- include another launch file in the chatter_ns namespace-->
  <group>
    <!-- push_ros_namespace to set namespace of included nodes -->
    <push_ros_namespace namespace="$(var chatter_ns)"/>
    <include file="$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener.launch.py"/>
  </group>

  <!-- start a turtlesim_node in the turtlesim1 namespace -->
  <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim1"/>
  <!-- start another turtlesim_node in the turtlesim2 namespace
      and use args to set parameters -->
  <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim2">
    <param name="background_r" value="$(var background_r)"/>
    <param name="background_g" value="$(var background_g)"/>
    <param name="background_b" value="$(var background_b)"/>
  </node>
  <!-- perform remap so both turtles listen to the same command topic -->
  <node pkg="turtlesim" exec="mimic" name="mimic">
    <remap from="/input/pose" to="/turtlesim1/turtle1/pose"/>
    <remap from="/output/cmd_vel" to="/turtlesim2/turtle1/cmd_vel"/>
  </node>
</launch>

3.yaml编写launch示例

# example.launch.yaml

launch:

# args that can be set from the command line or a default will be used
- arg:
    name: "background_r"
    default: "0"
- arg:
    name: "background_g"
    default: "255"
- arg:
    name: "background_b"
    default: "0"
- arg:
    name: "chatter_ns"
    default: "my/chatter/ns"


# include another launch file
- include:
    file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener.launch.py"

# include another launch file in the chatter_ns namespace
- group:
    - push_ros_namespace:
        namespace: "$(var chatter_ns)"
    - include:
        file: "$(find-pkg-share demo_nodes_cpp)/launch/topics/talker_listener.launch.py"

# start a turtlesim_node in the turtlesim1 namespace
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "sim"
    namespace: "turtlesim1"

# start another turtlesim_node in the turtlesim2 namespace and use args to set parameters
- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "sim"
    namespace: "turtlesim2"
    param:
    -
      name: "background_r"
      value: "$(var background_r)"
    -
      name: "background_g"
      value: "$(var background_g)"
    -
      name: "background_b"
      value: "$(var background_b)"

# perform remap so both turtles listen to the same command topic
- node:
    pkg: "turtlesim"
    exec: "mimic"
    name: "mimic"
    remap:
    -
        from: "/input/pose"
        to: "/turtlesim1/turtle1/pose"
    -
        from: "/output/cmd_vel"
        to: "/turtlesim2/turtle1/cmd_vel"

三、自己操刀编写一个launch.py

假设有三个package分别为node1_package、node2_package、node3_package,三个包里面各自有一个节点node1、node2、node3

#!/usr/bin/python3

import launch
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='node1_package',
            executable='node1',
            name='my_node1',
            output='screen'
        ),
        Node(
            package='node2_package',
            executable='node2',
            name='my_node2',
            output='screen'
        ),
        Node(
            package='node3_package',
            executable='node3',
            name='my_node3',
            output='screen'
        )
    ])

当然了LaunchDescription有类方法add_action,可以把Node对象和ExecuteProcess对象添加进入已经创建的LaunchDescription类对象实例中:

#!/usr/bin/python3

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess

def generate_launch_description():
    ld = LaunchDescription()
    
    node_1 = Node(
        package='node1_package',
        executable='node1',
        name='my_node1',
        output='screen'
        ),
    node_2 = Node(
        package='node2_package',
        executable='node2',
        name='my_node2',
        output='screen'
        ),
    node_3 = Node(
        package='node3_package',
        executable='node3',
        name='my_node3',
        output='screen'
        )
    
    ld.add_action(node_1)
    ld.add_action(node_2)
    ld.add_action(node_3)
    
    return ld

也还可以这样玩,借助ExecuteProcess 用于执行外部进程的动作

#!/usr/bin/python3

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess

def generate_launch_description():

    node1_start = ExecuteProcess(
        cmd=['ros2', 'run', 'node1_package', 'node1']
    ),
    node2_start = ExecuteProcess(
        cmd=['ros2', 'run', 'node2_package', 'node2']
    ),
    node3_start = ExecuteProcess(
        cmd=['ros2', 'run', 'node3_package', 'node3']
    )
    
    return LaunchDescription([
        node1_start,
        node2_start,
        node3_start 
    ])

以上是一个简单的使用python编写ros2 launch启动文件示例,更多特性和功能可以参考目录二中的官方示例和加入一些python自己的特性以达到自己的需求。

作者:费码程序猿
欢迎技术交流:QQ:255895056
转载请注明出处,如有不当欢迎指正

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值