Making Launch Files

A ROS 2 launch file lets you start multiple nodes, set parameters, and configure remappings from a single command instead of running ros2 run for every node individually. RoboFlock uses launch files to bring up the full sensor and navigation stack in one call.

Official ROS 2 reference: https://docs.ros.org/en/humble/Tutorials/Intermediate/Launch/Launch-Main.html


Why Use Launch Files

Without a launch file you would need a separate terminal for every node:

ros2 run joy joy_node
ros2 run bring_up self_destruct
ros2 run rplidar_ros rplidar_composition
ros2 run nav2_bringup bringup_launch.py
# ... and so on

A launch file starts all of these together, in the right order, with the right parameters.


Basic Structure

ROS 2 launch files are plain Python scripts. The entry point is always a function called generate_launch_description() that returns a LaunchDescription object.

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='joy',
            executable='joy_node',
            name='joy_node',
            output='screen',
        ),
        Node(
            package='bring_up',
            executable='self_destruct',
            name='ps4_teleop',
            output='screen',
        ),
    ])

Save this file as my_launch.py in your package’s launch/ directory, then register it in CMakeLists.txt:

install(DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)

Run it with:

ros2 launch bring_up my_launch.py

Setting Node Parameters

Parameters can be set inline or loaded from a YAML file.

Inline parameters:

Node(
    package='joy',
    executable='joy_node',
    parameters=[{
        'deadzone': 0.05,
        'autorepeat_rate': 20.0,
    }]
)

From a YAML file:

import os
from ament_index_python.packages import get_package_share_directory

params_file = os.path.join(
    get_package_share_directory('bring_up'), 'config', 'teleop_params.yaml'
)

Node(
    package='bring_up',
    executable='self_destruct',
    parameters=[params_file]
)

Topic Remapping

Remapping lets two nodes that publish/subscribe to different topic names talk to each other without changing source code.

Node(
    package='bring_up',
    executable='self_destruct',
    remappings=[
        ('cmd_vel', 'robot/cmd_vel'),   # remap output topic
        ('joy', 'ds4/joy'),             # remap input topic
    ]
)

Including Other Launch Files

Large systems split launch logic across multiple files. Use IncludeLaunchDescription to compose them:

from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os

nav2_launch = IncludeLaunchDescription(
    PythonLaunchDescriptionSource(
        os.path.join(
            get_package_share_directory('nav2_bringup'),
            'launch', 'bringup_launch.py'
        )
    ),
    launch_arguments={'use_sim_time': 'false'}.items()
)

def generate_launch_description():
    return LaunchDescription([
        nav2_launch,
        # ... other nodes
    ])

RoboFlock Launch File

RoboFlock currently uses bring_up.sh (a bash script managed by robot.service) to start nodes sequentially rather than a single unified launch file. The equivalent launch file would look like this:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        # Joystick driver
        Node(package='joy', executable='joy_node', name='joy_node', output='screen'),

        # Teleop node (PS4 controller → cmd_vel)
        Node(package='bring_up', executable='self_destruct', name='ps4_teleop', output='screen'),

        # LiDAR
        Node(
            package='rplidar_ros',
            executable='rplidar_composition',
            name='rplidar_node',
            output='screen',
            parameters=[{'serial_port': '/dev/rplidar', 'frame_id': 'laser'}]
        ),

        # Robot GPS
        Node(package='gps_pkg', executable='gps_node', name='gps_node', output='screen'),

        # Ultrasonic aggregator (reads from Arduino Nano serial)
        Node(package='ultrasonic_pkg', executable='ultrasonic_node', name='ultrasonic_node', output='screen'),
    ])

Note

To convert RoboFlock to use this launch file instead of the bash script, replace the ros2 run lines in bring_up.sh with a single ros2 launch bring_up roboflock_launch.py call and rebuild the package.


Further Reading