【ROS2 工具】Launch

一、 Launch功能概述

在 ROS 2 中,Launch 系统是用于自动启动、管理和配置多个节点(Node)及其依赖项的核心机制。与 ROS 1 主要依赖 XML 编写 .launch 文件不同,ROS 2 引入了基于 Python 动态脚本的 Launch 系统(同时也支持 XML 和 YAML),极大地提升了复杂的逻辑控制、条件判断和跨节点参数注入的能力。

1. Python Launch 的主要功能

  1. 多节点启动 :避免手动在多个终端逐个运行 ros2 run 命令。

  2. 灵活的参数与 Remap 注入:统一管理节点的命名空间(Namespace)、话题重映射(Remap)以及 YAML 参数配置文件。

  3. 流程与生命周期控制

    • 支持节点的条件启动(如 IfCondition, UnlessCondition)。

    • 支持事件监听(Event Handlers),例如:当"节点 A 启动成功"或"节点 B 退出"时触发后续动作。

    • 原生支持 ROS 2 Lifecycle Node(生命周期节点) 的状态切换管理。

  4. 多语言格式支持 :支持 Python (功能最全、最推荐)、XMLYAML 三种格式。

特性 Python (.launch.py) XML (.launch.xml) YAML (.launch.yaml)
推荐程度 官方首选(主线标准) 简单场景推荐 较少使用
表达能力 极强(支持完整 Python 语法、条件分支、事件处理、逻辑运算) 中等(支持基础参数替换、节点启动、嵌套包含) 较弱(语法结构固定)
编写难度 代码较冗长,存在一定的学习曲线 直观简短,语法类似 ROS 1 极简,但格式缩进要求严格
典型场景 复杂机器人系统、带有条件逻辑和事件监听的启动 纯静态节点堆叠、从 ROS 1 迁移的轻量项目 简单的配置驱动型启动

2. Python Launch 的核心架构组件

编写一个 Python Launch 文件(generate_launch_description())通常包含以下核心元素:

  • LaunchDescription:主入口容器,用于收集并返回所有要执行的 Launch Actions。

  • Node (Action) :定义要启动的节点,包含 package(功能包名)、executable(可执行文件名)、name(节点运行名)、parameters(参数文件/字典)和 remappings(话题重映射列表)。

  • DeclareLaunchArgument :定义外部传入的 Launch 参数(例如可在命令行通过 ros2 launch package_name file.launch.py use_sim_time:=true 传入)。

  • LaunchConfiguration :用于在代码中动态引用通过 DeclareLaunchArgument 声明的变量值。

  • IncludeLaunchDescription :用于嵌套调用并运行其他的 .launch.py 文件。

  • ExecuteProcess :用于在 Launch 时直接执行标准的 Shell 命令或外部二进制程序(如运行 rviz2 或命令行工具)。

  • PythonLaunchDescriptionSource:解析并加载指定路径下的Python 格式 .launch.py 文件,专门配合 IncludeLaunchDescription 动作(Action)来实现 Launch 文件的嵌套调用。

  • get_package_share_directory:根据功能包名称,自动动态获取该包在系统中的 share 安装路径。

3. 标准 Python Launch 代码示例

python 复制代码
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch.conditions import IfCondition
from launch_ros.actions import Node

def generate_launch_description():
    # 1. 获取功能包 share 路径与配置文件位置
    pkg_share = get_package_share_directory('my_robot_bringup')
    default_param_file = os.path.join(pkg_share, 'config', 'params.yaml')

    # 2. 声明外部命令行参数 (Launch Arguments)
    use_sim_time_arg = DeclareLaunchArgument(
        'use_sim_time',
        default_value='false',
        description='Use simulation (Gazebo) clock if true'
    )
    
    rviz_arg = DeclareLaunchArgument(
        'use_rviz',
        default_value='true',
        description='Whether to start RViz2'
    )

    # 3. 配置要启动的 Node (雷达驱动节点)
    driver_node = Node(
        package='my_lidar_driver',
        executable='lidar_node',
        name='lidar_node',
        output='screen',
        parameters=[
            default_param_file,  # 注入 YAML 配置文件
            {'use_sim_time': LaunchConfiguration('use_sim_time')} # 动态注入属性
        ],
        remappings=[
            ('/raw_scan', '/scan')  # 话题重映射
        ]
    )

    # 4. 条件启动 RViz2 节点
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2',
        condition=IfCondition(LaunchConfiguration('use_rviz')), # 条件分支控制
        output='screen'
    )

    # 5. 返回 LaunchDescription 容器
    return LaunchDescription([
        use_sim_time_arg,
        rviz_arg,
        driver_node,
        rviz_node
    ])

二、代码解析

1. 多节点启动

python 复制代码
from launch import LaunchDescription           # launch文件的描述类
from launch_ros.actions import Node            # 节点启动的描述类

def generate_launch_description():             # 自动生成launch文件的函数
    return LaunchDescription([                 # 返回launch文件的描述信息
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_pub', # 节点的可执行文件
        ),
        Node(                                  # 配置一个节点的启动
            package='learning_topic',          # 节点所在的功能包
            executable='topic_helloworld_sub', # 节点的可执行文件名
        ),
    ])

2. 命令行参数配置

python 复制代码
import os

from ament_index_python.packages import get_package_share_directory # 查询功能包路径的方法

from launch import LaunchDescription    # launch文件的描述类
from launch_ros.actions import Node     # 节点启动的描述类


def generate_launch_description():      # 自动生成launch文件的函数
   rviz_config = os.path.join(          # 找到配置文件的完整路径
      get_package_share_directory('learning_launch'),
      'rviz',
      'turtle_rviz.rviz'
      )

   return LaunchDescription([           # 返回launch文件的描述信息
      Node(                             # 配置一个节点的启动
         package='rviz2',               # 节点所在的功能包
         executable='rviz2',            # 节点的可执行文件名
         name='rviz2',                  # 对节点重新命名
         arguments=['-d', rviz_config]  # 加载命令行参数
      )
   ])

3. 资源重映射

python 复制代码
from launch import LaunchDescription      # launch文件的描述类
from launch_ros.actions import Node       # 节点启动的描述类

def generate_launch_description():        # 自动生成launch文件的函数
    return LaunchDescription([            # 返回launch文件的描述信息
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim1',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            namespace='turtlesim2',       # 节点所在的命名空间
            executable='turtlesim_node',  # 节点的可执行文件名
            name='sim'                    # 对节点重新命名
        ),
        Node(                             # 配置一个节点的启动
            package='turtlesim',          # 节点所在的功能包
            executable='mimic',           # 节点的可执行文件名
            name='mimic',                 # 对节点重新命名
            remappings=[                  # 资源重映射列表
                ('/input/pose', '/turtlesim1/turtle1/pose'),         # 将/input/pose话题名修改为/turtlesim1/turtle1/pose
                ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),  # 将/output/cmd_vel话题名修改为/turtlesim2/turtle1/cmd_vel
            ]
        )
    ])

4. ROS参数设置

python 复制代码
from launch import LaunchDescription                   # launch文件的描述类
from launch.actions import DeclareLaunchArgument       # 声明launch文件内使用的Argument类
from launch.substitutions import LaunchConfiguration, TextSubstitution

from launch_ros.actions import Node                    # 节点启动的描述类


def generate_launch_description():                     # 自动生成launch文件的函数
   background_r_launch_arg = DeclareLaunchArgument(
      'background_r', default_value=TextSubstitution(text='0')     # 创建一个Launch文件内参数(arg)background_r
   )
   background_g_launch_arg = DeclareLaunchArgument(
      'background_g', default_value=TextSubstitution(text='84')    # 创建一个Launch文件内参数(arg)background_g
   )
   background_b_launch_arg = DeclareLaunchArgument(
      'background_b', default_value=TextSubstitution(text='122')   # 创建一个Launch文件内参数(arg)background_b
   )

   return LaunchDescription([                                      # 返回launch文件的描述信息
      background_r_launch_arg,                                     # 调用以上创建的参数(arg)
      background_g_launch_arg,
      background_b_launch_arg,
      Node(                                                        # 配置一个节点的启动
         package='turtlesim',
         executable='turtlesim_node',                              # 节点所在的功能包
         name='sim',                                               # 对节点重新命名
         parameters=[{                                             # ROS参数列表
            'background_r': LaunchConfiguration('background_r'),   # 创建参数background_r
            'background_g': LaunchConfiguration('background_g'),   # 创建参数background_g
            'background_b': LaunchConfiguration('background_b'),   # 创建参数background_b
         }]
      ),
   ])

5. 加载参数文件

python 复制代码
import os

from ament_index_python.packages import get_package_share_directory  # 查询功能包路径的方法

from launch import LaunchDescription   # launch文件的描述类
from launch_ros.actions import Node    # 节点启动的描述类


def generate_launch_description():     # 自动生成launch文件的函数
   config = os.path.join(              # 找到参数文件的完整路径
      get_package_share_directory('learning_launch'),
      'config',
      'turtlesim.yaml'
      )

   return LaunchDescription([          # 返回launch文件的描述信息
      Node(                            # 配置一个节点的启动
         package='turtlesim',          # 节点所在的功能包
         executable='turtlesim_node',  # 节点的可执行文件名
         namespace='turtlesim2',       # 节点所在的命名空间
         name='sim',                   # 对节点重新命名
         parameters=[config]           # 加载参数文件
      )
   ])

6. Launch文件包含

在复杂的机器人系统中,launch文件会有很多,此时可以使用类似编程中的include机制,让launch文件互相包含。

python 复制代码
import os

from ament_index_python.packages import get_package_share_directory  # 查询功能包路径的方法

from launch import LaunchDescription                 # launch文件的描述类
from launch.actions import IncludeLaunchDescription  # 节点启动的描述类
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import GroupAction               # launch文件中的执行动作
from launch_ros.actions import PushRosNamespace      # ROS命名空间配置

def generate_launch_description():                   # 自动生成launch文件的函数
   parameter_yaml = IncludeLaunchDescription(        # 包含指定路径下的另外一个launch文件
      PythonLaunchDescriptionSource([os.path.join(
         get_package_share_directory('learning_launch'), 'launch'),
         '/parameters_nonamespace.launch.py'])
      )

   parameter_yaml_with_namespace = GroupAction(      # 对指定launch文件中启动的功能加上命名空间
      actions=[
         PushRosNamespace('turtlesim2'),
         parameter_yaml]
      )

   return LaunchDescription([                        # 返回launch文件的描述信息
      parameter_yaml_with_namespace
   ])
相关推荐
某林2125 小时前
机器人重启失联:DDS 发现机制与传输层静默故障
人工智能·python·机器人·硬件架构·ros2
米糕闯编程1 天前
鱼香ros2(二)运行第一个机器人
机器人·ros2
蝙蝠侠的小蝙蝠2 天前
【ROS2 通信机制】话题、服务、动作
ros2·服务·动作·话题·通信接口
zhangrelay4 天前
答疑-是否软件源问题都必须更换为国内呢-
linux·笔记·学习·ubuntu·ros2
CS_Zero6 天前
激光雷达YDLiDAR驱动与ROS2环境搭建
无人机·飞控·ros2·激光雷达·避障
海阔天空任鸟飞~7 天前
rclpy
ros2
微小冷7 天前
ROS2 URDF机器人建模初步教程
机器人·ros2·urdf·机器人建模
zh路西法12 天前
【玩转VLA具身智能机械臂】(三):视觉避障——从 RGB-D 点云到 MoveIt octomap 的完整落地
c++·ros2·gazebo·moveit2·ompl·octomap
YQ_0112 天前
ROS 2 Humble Nav2 生命周期教程:启动流程、状态监控、超时诊断与自愈设计
linux·机器人·ros2·nav2