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.
Launch Patterns Reference
| Pattern / Class | What it does |
|---|---|
| Node | Start a single ROS 2 node |
| DeclareLaunchArgument | Define user-overridable parameters for the launch file |
| LaunchConfiguration | Read a declared argument value at substitution time |
| IncludeLaunchDescription | Compose by including another launch file |
| GroupAction + PushRosNamespace | Apply namespace/remaps/params to a set of nodes |
| TimerAction | Delay a node's start until dependencies are ready |
| IfCondition / UnlessCondition | Conditionally launch a node based on an argument |
| RegisterEventHandler | React to node start/exit events (restart, shutdown, log) |
| SetEnvironmentVariable | Set an env var before nodes start (e.g. RCUTILS_COLORIZED_OUTPUT) |
| ExecuteProcess | Run 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.
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.
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.
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.
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.
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.
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.
# 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