ROS 2 launch files: a practical tutorial

Write ROS 2 Python launch files with arguments, YAML parameters, remaps, namespaces and conditions, and fix the errors that stop nodes starting.

Reference
RE-714
Published
Read
6 min

About the author · Sources, testing and AI policy

Laptop showing a ROS 2 launch file with a terminal beside it

A launch file is the difference between a robot that starts and a robot that starts the same way every time. Once a project has more than three nodes, typing commands into five terminals stops being a workflow. A launch file records the whole system: nodes, parameters, remaps, namespaces and order.

This tutorial covers ROS 2 Python launch files from the first node to arguments, includes and conditions, with the mistakes that silently start the wrong robot.

What a launch file replaces

Without a launch fileWith a launch file
Five terminals and a note on a whiteboardOne command, versioned in git
Parameters retyped or forgottenDeclared defaults, overridden per robot
Topic names patched by handRemaps recorded next to the nodes
Two robots collide on one topicNamespaces keep them apart
Startup order left to luckEvent handlers and delays where needed

The smallest useful launch file

A Python launch file returns a LaunchDescription containing actions. The most common action is a Node. Save this as bringup.launch.py inside a package’s launch folder.

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='demo_nodes_cpp',
            executable='talker',
            name='talker',
            output='screen',
        ),
        Node(
            package='demo_nodes_cpp',
            executable='listener',
            name='listener',
            output='screen',
        ),
    ])

Run it with ros2 launch my_package bringup.launch.py. The launch system starts both processes, prefixes their output with the node name and stops everything when you press Ctrl+C.

Arguments make one file serve many robots

Arguments turn a launch file into a template. Declare each one with a default, then read it with LaunchConfiguration. This is how the same file starts a simulation or a real robot, or two robots with different namespaces.

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node

def generate_launch_description():
    use_sim_time = LaunchConfiguration('use_sim_time')
    robot_ns = LaunchConfiguration('robot_ns')

    return LaunchDescription([
        DeclareLaunchArgument('use_sim_time', default_value='false'),
        DeclareLaunchArgument('robot_ns', default_value='robot1'),

        Node(
            package='my_robot',
            executable='controller',
            namespace=robot_ns,
            parameters=[{'use_sim_time': use_sim_time}],
            output='screen',
        ),
    ])

Override at run time with ros2 launch my_package bringup.launch.py use_sim_time:=true robot_ns:=robot2. A boolean argument passed as a string is a classic bug: launch substitutions are strings unless a node declares the parameter type. Declare parameter types in the node or use a YAML parameter file.

Parameters belong in YAML

Long parameter lists do not belong in Python. Put them in a YAML file and pass the path to the node. This keeps the launch file readable and lets you version tuning separately for simulation, lab and production.

my_robot:
  ros__parameters:
    wheel_radius: 0.05
    wheel_separation: 0.32
    max_speed: 0.6
    use_sim_time: false

Load it with parameters=['config/robot.yaml'] in the Node action, or with a ParameterFile substitution if the path is computed. The node name in the YAML must match the node name in the launch file, including any namespace.

Remaps and namespaces

A remap renames a topic, service or action at launch time. It is how you connect a generic driver to your robot’s naming convention without editing the driver. A namespace prefixes every name a node creates, which is how two identical robots share one ROS 2 graph.

Node(
    package='my_robot',
    executable='driver',
    namespace='robot1',
    remappings=[('/scan', 'lidar/scan'), ('/cmd_vel', 'cmd_vel')],
)

Watch the leading slash. An absolute name like /scan ignores the namespace, while a relative name like scan becomes /robot1/scan. Mixing the two is the most common reason a node appears to publish nothing.

Include, conditions and order

Large systems compose launch files. Use IncludeLaunchDescription to pull in a driver or a navigation stack, and IfCondition or UnlessCondition to start parts only when an argument says so. A simulation launch file can include the same robot description as the hardware file, with a different controller.

ROS 2 starts actions in the order they appear, but it does not wait for one node to be ready before starting the next. If a node needs the clock or a transform before it can initialise, add a TimerAction delay or an event handler rather than assuming the order is enough.

XML and YAML launch files

ROS 2 also supports XML and YAML launch descriptions. They are easier for simple compositions and for people who prefer configuration to code. Python remains the most flexible option once you need conditions, computed paths or custom events. Pick one convention per repository and stay with it.

Debugging a launch file

  • ros2 launch my_package bringup.launch.py --show-args lists every declared argument and its default.
  • --print shows the launch description without running it. Useful for checking substitutions.
  • Add output='screen' to every node while debugging; the default log file hides the error you need.
  • Check the node list with ros2 node list and the graph with ros2 topic list after startup.
  • Dump the running parameters with ros2 param dump /node_name to confirm the YAML actually loaded.

Errors you will meet

SymptomCauseFix
Node starts and immediately exitsParameter type mismatch or missing fileRun the node directly to see the error
Two nodes cannot communicateNamespace or remap mismatchEcho the topic and compare fully qualified names
Parameters ignoredYAML node name does not matchMatch the name and namespace exactly
Works in sim, fails on hardwareuse_sim_time left true or falseMake it an argument with the right default
Launch finds no executablePackage not built or sourcedRebuild and source the workspace
Everything starts twiceLaunch file included twiceCheck the include tree

Related: ROS 2 for beginners, Nav2 tutorial, Gazebo ROS 2 tutorial, SLAM for beginners, Raspberry Pi 5 robot guide.

What is a launch file in ROS 2?

A launch file starts multiple nodes with their parameters, remaps and namespaces in one command. It replaces a set of manual terminal commands with a versioned description of the system.

Should I write launch files in Python, XML or YAML?

Python is the most flexible and handles conditions and computed paths well. XML and YAML are simpler for basic compositions. Pick one style per repository.

How do I pass arguments to a ROS 2 launch file?

Declare them with DeclareLaunchArgument, read them with LaunchConfiguration, and override on the command line with name:=value.

Why is my launch file not loading parameters?

Usually the node name or namespace in the YAML does not match the running node. Use ros2 param dump to see what the node actually received.

How do I start a node only in simulation?

Use an IfCondition or UnlessCondition tied to a launch argument, and set that argument differently in the simulation and hardware launch files.

Launch files start nodes; choosing how they talk is the next decision: topics, services and actions in ROS 2.

A launch file usually loads a robot description too; see building a URDF for ROS 2.

Older tutorials use ROS 1 launch XML; ROS 2 vs ROS 1 explains why the file changed.

Launch files drive lifecycle transitions with event handlers, and the lifecycle nodes tutorial shows the sequencing pattern.

Sources and further reading

Pass it onWhatsAppLinkedIn

Keep reading

Continue learning

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.