Skip to main content
🚀 Launch GuideUpdated June 2026

ROS 2 Launch File Guide 2026Args · Include · GroupAction · Events

Complete 2026 guide to Python launch files in ROS 2. Covers the basic Node launch pattern, declaring and using LaunchArguments, conditional launch with IfCondition, composing stacks with IncludeLaunchDescription, grouping nodes with namespace and remapping via GroupAction, delayed startup with TimerAction, reacting to process events, and all CLI tools.

LaunchArgumentIncludeLaunchDescriptionGroupActionTimerActionIfCondition

Launch Patterns Reference

Pattern / ClassWhat it does
NodeStart a single ROS 2 node
DeclareLaunchArgumentDefine user-overridable parameters for the launch file
LaunchConfigurationRead a declared argument value at substitution time
IncludeLaunchDescriptionCompose by including another launch file
GroupAction + PushRosNamespaceApply namespace/remaps/params to a set of nodes
TimerActionDelay a node's start until dependencies are ready
IfCondition / UnlessConditionConditionally launch a node based on an argument
RegisterEventHandlerReact to node start/exit events (restart, shutdown, log)
SetEnvironmentVariableSet an env var before nodes start (e.g. RCUTILS_COLORIZED_OUTPUT)
ExecuteProcessRun any arbitrary OS process as part of the launch

Basic Launch Patterns

Minimal Python Launch File

A launch file is a Python module that exports generate_launch_description(). Every launch file follows this pattern.

python
from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    """Return a LaunchDescription — the entry point for every launch file."""
    return LaunchDescription([

        Node(
            package="turtlesim",
            executable="turtlesim_node",
            name="sim",
            output="screen",
        ),

        Node(
            package="turtlesim",
            executable="turtle_teleop_key",
            name="teleop",
            output="screen",
            prefix="xterm -e",  # open in a separate terminal
        ),

    ])

💡 Run with: `ros2 launch <package> <file>.launch.py` or `ros2 launch <file>.launch.py` for a bare file path.

Declare and Use Launch Arguments

Arguments let callers override launch-time values without editing the file. Use LaunchConfiguration to read them.

python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import LaunchConfiguration, PythonExpression
from launch_ros.actions import Node


def generate_launch_description():
    use_sim_time_arg = DeclareLaunchArgument(
        "use_sim_time",
        default_value="false",
        description="Use simulation clock",
        choices=["true", "false"],
    )

    robot_name_arg = DeclareLaunchArgument(
        "robot_name",
        default_value="my_robot",
        description="Name prefix for all nodes",
    )

    log_level_arg = DeclareLaunchArgument(
        "log_level",
        default_value="info",
        choices=["debug", "info", "warn", "error"],
    )

    return LaunchDescription([
        use_sim_time_arg,
        robot_name_arg,
        log_level_arg,

        # Node that reads the launch arguments
        Node(
            package="my_robot",
            executable="my_node",
            name=LaunchConfiguration("robot_name"),
            output="screen",
            parameters=[{
                "use_sim_time": LaunchConfiguration("use_sim_time"),
            }],
            arguments=["--ros-args", "--log-level", LaunchConfiguration("log_level")],
        ),

        # Only launched if use_sim_time:=true
        Node(
            package="gazebo_ros",
            executable="gzserver",
            name="gazebo",
            output="screen",
            condition=IfCondition(LaunchConfiguration("use_sim_time")),
        ),

        # Only launched if use_sim_time:=false (real robot)
        Node(
            package="my_robot",
            executable="hardware_driver",
            name="hardware",
            output="screen",
            condition=UnlessCondition(LaunchConfiguration("use_sim_time")),
        ),
    ])

💡 Call from CLI: `ros2 launch my_pkg bringup.launch.py use_sim_time:=true robot_name:=robot1`

Include & GroupAction

Include Another Launch File

Compose complex bringup by including sub-launch files. Pass arguments through with launch_arguments.

python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
    use_sim_time = LaunchConfiguration("use_sim_time", default="false")

    # Include Nav2 bringup
    nav2_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([
            PathJoinSubstitution([
                FindPackageShare("nav2_bringup"),
                "launch",
                "navigation_launch.py",
            ])
        ]),
        launch_arguments={
            "use_sim_time": use_sim_time,
            "params_file": PathJoinSubstitution([
                FindPackageShare("my_robot_bringup"),
                "config",
                "nav2_params.yaml",
            ]),
        }.items(),
    )

    # Include SLAM toolbox
    slam_launch = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([
            PathJoinSubstitution([
                FindPackageShare("slam_toolbox"),
                "launch",
                "online_async_launch.py",
            ])
        ]),
        launch_arguments={
            "use_sim_time": use_sim_time,
        }.items(),
    )

    return LaunchDescription([
        DeclareLaunchArgument("use_sim_time", default_value="false"),
        nav2_launch,
        slam_launch,
    ])

GroupAction — Namespace and Remapping

GroupAction lets you apply a namespace, parameters, and remappings to a set of nodes at once.

python
from launch import LaunchDescription
from launch.actions import GroupAction
from launch_ros.actions import Node, PushRosNamespace


def generate_launch_description():
    # Wrap multiple nodes in the /robot1 namespace
    robot1_group = GroupAction([
        PushRosNamespace("robot1"),

        Node(
            package="my_robot",
            executable="robot_state_publisher",
            name="robot_state_publisher",
            output="screen",
        ),

        Node(
            package="my_robot",
            executable="controller",
            name="controller",
            remappings=[
                # remap /cmd_vel to /robot1/cmd_vel (namespace handles outer)
                ("odom", "wheel_odom"),  # relative remap inside namespace
            ],
            parameters=[{"use_sim_time": False}],
        ),
    ])

    robot2_group = GroupAction([
        PushRosNamespace("robot2"),

        Node(
            package="my_robot",
            executable="robot_state_publisher",
            name="robot_state_publisher",
            output="screen",
        ),

        Node(
            package="my_robot",
            executable="controller",
            name="controller",
            remappings=[("odom", "wheel_odom")],
            parameters=[{"use_sim_time": False}],
        ),
    ])

    return LaunchDescription([robot1_group, robot2_group])

GroupAction with PushRosNamespace is the standard pattern for multi-robot bringup.

TimerAction & Event Handlers

TimerAction — Delayed Node Start

Start a node after a delay — useful when a dependency needs time to initialize before its subscriber starts.

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


def generate_launch_description():
    # Start the hardware driver immediately
    hardware_node = Node(
        package="my_robot",
        executable="hardware_driver",
        name="hardware",
        output="screen",
    )

    # Wait 3 seconds for hardware to initialize, then start the controller
    controller_node = TimerAction(
        period=3.0,  # seconds
        actions=[
            Node(
                package="my_robot",
                executable="controller",
                name="controller",
                output="screen",
            ),
        ],
    )

    return LaunchDescription([hardware_node, controller_node])

Event Handler — React to Node Exit

Trigger actions when a node exits — restart it, log, or shut down the launch.

python
from launch import LaunchDescription
from launch.actions import RegisterEventHandler, LogInfo, Shutdown
from launch.event_handlers import OnProcessExit, OnProcessStart
from launch_ros.actions import Node


def generate_launch_description():
    my_node = Node(
        package="my_robot",
        executable="my_node",
        name="my_node",
        output="screen",
    )

    return LaunchDescription([
        my_node,

        # Log when the node starts
        RegisterEventHandler(
            OnProcessStart(
                target_action=my_node,
                on_start=[
                    LogInfo(msg="my_node started successfully."),
                ],
            )
        ),

        # Shut down the entire launch when my_node exits
        RegisterEventHandler(
            OnProcessExit(
                target_action=my_node,
                on_exit=[
                    LogInfo(msg="my_node exited — shutting down launch."),
                    Shutdown(),
                ],
            )
        ),
    ])

CLI Tools

Launch CLI Tools

Run, inspect, and debug launch files from the terminal.

bash
# Run a launch file from a package
ros2 launch nav2_bringup navigation_launch.py

# Run a launch file by path
ros2 launch /path/to/bringup.launch.py

# Pass arguments
ros2 launch my_pkg bringup.launch.py use_sim_time:=true robot_name:=robot1

# Show all arguments a launch file accepts
ros2 launch my_pkg bringup.launch.py --show-args

# Show all nodes and their topics (dry run — don't actually launch)
ros2 launch my_pkg bringup.launch.py --print-description

# Debug logging for launch system itself
ros2 launch my_pkg bringup.launch.py --debug

# Install launch package if missing
sudo apt install ros-jazzy-launch ros-jazzy-launch-ros

Related Guides