ZERO Notificatons

NO Feedback yet!!

okay

xparo
X.P.A.R.O



project - ROS 2 Package Generator



Once a robot has more than two nodes, starting them one terminal at a time stops being practical. Launch files start everything with one command and set each node's parameters, topic names and namespace. This guide builds a Python launch file step by step, including a parameter gotcha that catches almost everyone who uses namespaces. Everything was tested on ROS 2 Jazzy.

The smallest useful launch file

We start from the talker and listener from our publisher and subscriber tutorial. Save this as launch/demo.launch.py in the package:

from launch import LaunchDescription
from launch_ros.actions import Node


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

ROS looks for a function named generate_launch_description that returns a LaunchDescription. Each Node action starts one executable; output='screen' shows its log in your terminal.

Install the launch file

Launch files run from the install space, so the package must install them. In an ament_python package, add a data_files entry in setup.py (with from glob import glob at the top):

('share/' + package_name + '/launch', glob('launch/*.launch.py')),

In an ament_cmake package, use install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}). Also add <exec_depend>launch_ros</exec_depend> to package.xml. Rebuild, source, and run:

colcon build --symlink-install --packages-select robot_demo
source install/setup.bash
ros2 launch robot_demo demo.launch.py

Arguments, namespaces and remapping

A real launch file takes arguments, gives the robot its own namespace and renames topics to fit the rest of the system:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
    robot_name = LaunchConfiguration('robot_name')
    params_file = PathJoinSubstitution([FindPackageShare('robot_demo'), 'config', 'talker.yaml'])

    return LaunchDescription([
        DeclareLaunchArgument('robot_name', default_value='robot1',
                              description='Namespace for this robot'),
        Node(
            package='robot_demo',
            executable='talker',
            namespace=robot_name,
            parameters=[params_file],
            remappings=[('chatter', 'status')],
            output='screen',
        ),
        Node(
            package='robot_demo',
            executable='listener',
            namespace=robot_name,
            remappings=[('chatter', 'status')],
            output='screen',
        ),
    ])
  • DeclareLaunchArgument declares an argument with a default and a description; LaunchConfiguration reads its value. Pass a value with ros2 launch robot_demo demo.launch.py robot_name:=rover_a, and list a file's arguments with --show-args.
  • namespace prefixes the node's name and its relative topic names, so the nodes become /rover_a/talker and /rover_a/listener.
  • remappings rename topics without touching code: here chatter becomes status, so the robot publishes on /rover_a/status.
  • FindPackageShare finds the package's installed share directory, so the launch file works wherever the workspace lives.

Parameters from a YAML file, and the namespace gotcha

Put parameters in config/talker.yaml (and install the config folder the same way as launch). The obvious way to write it is:

talker:
  ros__parameters:
    rate_hz: 5.0

On our test robot this file was silently ignored: the talker kept publishing at its default 2 Hz. The reason is the namespace. The top-level key in a ROS 2 parameter file is matched against the node's full name, and with a namespace the node is /rover_a/talker, not talker. Use a wildcard so the file works with any namespace:

/**/talker:
  ros__parameters:
    rate_hz: 5.0

With that change the talker published at 5 Hz, and ros2 param get /rover_a/talker rate_hz returned 5.0. To apply parameters to every node in a file, use /**: as the key. Inline parameters in the launch file, such as parameters=[{'rate_hz': 5.0}], apply to that node whatever its name.

When a parameter "does not work", check it on the running node with ros2 param get before debugging your code. It tells you immediately whether the value arrived.

Reusing launch files: two robots from one file

Launch files can include other launch files and pass them arguments. This starts the same demo twice, for two robots:

from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
    demo = PathJoinSubstitution([FindPackageShare('robot_demo'), 'launch', 'demo.launch.py'])
    return LaunchDescription([
        IncludeLaunchDescription(PythonLaunchDescriptionSource(demo),
                                 launch_arguments={'robot_name': 'rover_a'}.items()),
        IncludeLaunchDescription(PythonLaunchDescriptionSource(demo),
                                 launch_arguments={'robot_name': 'rover_b'}.items()),
    ])

Each robot's listener hears only its own talker, because the namespaces keep their topics apart. This is the same pattern used to run several simulated robots, or a fleet of real ones, from shared launch files.

Troubleshooting

  • "file 'demo.launch.py' was not found in the share directory of package": the file is not installed. Check data_files or install(DIRECTORY …), rebuild and source.
  • Parameters ignored: check the YAML key against the node's full name, as above.
  • Topics not connecting: list them with ros2 topic list. A leading slash (/chatter) makes a topic name absolute, so it ignores the namespace.
  • Edits to the launch file have no effect: without --symlink-install, the installed copy is used until you rebuild.

The ROS 2 Package Generator creates a package with a working launch file and the install rules already in place. For build problems, see common colcon errors.

More guides

Oct. 4, 2026, 10:20 a.m.
Common colcon build Errors in ROS 2 and How to Fix Them
Read more..
Oct. 4, 2026, 10:22 a.m.
Write Your First ROS 2 Publisher and Subscriber in Python (rclpy)
Read more..
Oct. 4, 2026, 10:23 a.m.
Anatomy of a ROS 2 Package: package.xml, setup.py, CMakeLists.txt Explained
Read more..
Oct. 4, 2026, 10:24 a.m.
ament_python vs ament_cmake: Which ROS 2 Build Type Should You Use?
Read more..

If you have any query or problem
feel free to contact us
email: [email protected]