6. Launch Files#
6.1. Introduction#
Launch files are an important tool in ROS2. You can think of them as fulfilling the role of a conductor in an orchestra. Each musician (or instrument section) represents an individual ROS2 node, such as a sensor node, a motor controller, or a navigation module. Instead of having each musician start playing randomly, the conductor (launch file) ensures a few things:
Everyone starts together. The launch file starts multiple ROS nodes at the same time, just like a conductor cues musicians to begin playing together.
Each musician follows a sheet of instructions. The launch file defines parameters, like volume (sensor sensitivity) or tempo (publish rate), so each node knows how to behave.
Sections communicate with each other. Just like strings, brass, and percussion need to coordinate, ROS nodes need to publish and subscribe to correct topics.
A rehearsal plan is followed. The launch file can include other launch files, similar to how an orchestra follows a structured program with multiple movements.
The conductor can adjust the performance dynamically. Using arguments and parameters, the launch file allows changes (tempo adjustments) without rewriting the entire script.
From the analogy, we see that launch files allow users to start multiple nodes simultaneously, configure parameters, and pass arguments dynamically. Essentially, they provide a structured way to manage complex robotic applications. Therefore, just as an orchestra would not function as well without a conductor, a complex ROS system would not efficiently start and manage its nodes without a launch file.
6.2. Creating a Basic Launch File#
A launch file in ROS2 allows us to start multiple nodes, configure parameters, and manage execution in an organized way. In this section, we will break down the Python launch file provided below and explain its components.
import launch # Brings in the core launch module, which allows defining a launch file in Python
from launch_ros.actions import Node # Imports the Node action, used to start ROS2 nodes within the launch file
# Every launch file in ROS2 requires a function called generate_launch_description()
# This function returns a LaunchDescription object that defines what will be executed when the launch file is run
def generate_launch_description():
return launch.LaunchDescription([
Node(
package='demo_nodes_cpp', # Defines package containing node
executable='talker', # Defines executable name of node to launch
name='my_talker', # Assigns a custom name to the node
output='screen' # Prints the node los and output to the terminal screen
),
])
Once the launch file is created, it can be executed using: ros2 launch name_of_package name_of_launch_file.py
For example, if the file is named talker_launch.py, you would run: ros2 launch demo_nodes_cpp talker_launch.py
This will start the “talker” node, which continuously publishes messages on the /chatter topic.
6.3. Expanding the Complexity of the Launch File#
In the example above, we saw a launch file that launches one node, “talker”. Now we will expand the functionality of the launch file by having it launch multiple nodes simultaneously. A simple addition will allow the launch file to execute both “talker” and “listener” nodes at once.
import launch
from launch_ros.actions import Node
def generate_launch_description():
return launch.LaunchDescription([
Node( # Talker node
package='demo_nodes_cpp',
executable='talker',
name='my_talker',
output='screen'
),
Node( # Listener node
package='demo_nodes_cpp',
executable='listener',
name='my_listener',
output='screen'
),
])
The example above shows how launch files can orchestrate multiple ROS nodes at the same time.
6.4. Using Parameters in Launch Files#
In the introduction section to this chapter, we mentioned how nodes, like musicians, can follow a “sheet” of instructions during their execution. ROS2 nodes often use parameters to configure their behavior, such as changing publishing rate, enabling debugging, etc. We will now modify the “talker” node to accept parameters.
import launch
from launch_ros.actions import Node
from launch_ros.descriptions import ParameterValue # Import to use parameters
def generate_launch_description():
return launch.LaunchDescription([
Node(
package='demo_nodes_cpp',
executable='talker',
name='my_talker',
output='screen',
parameters=[{'publish_rate': ParameterValue(5.0)}] # Set publish rate to 5 Hz
),
])
As you can see, we modified the “talker” node to accept parameters, and configured its behavior by changing its publishing rate.
6.5. Using Arguments in Launch Files#
Arguments allow for dynamic configurations without modifying the launch file. In our analogy, just as an orchestra might interpret a piece of music differently depending on the venue (different audience, acoustics, etc.), launch arguments allow us to adjust node behavior dynamically without changing the launch file itself. In this case, the conductor (launch file), can instruct the musicians (nodes) to play in different styles based on the sheet music interpretation (launch arguments).
import launch
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')
return LaunchDescription([
# Declare an argument for 'use_sim_time' (default: False)
DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulated time'
),
# The musician (node) plays the piece (executes with parameters)
Node(
package='demo_nodes_cpp',
executable='talker',
name='my_talker',
output='screen',
parameters=[{'use_sim_time': use_sim_time}]
),
])
To execute with simulated time, we can run the command:
ros2 launch my_package my_launch_file.py use_sim_time:=true
6.6. Conditional Execution#
In an orchestra, not every instrument plays in every piece. Some musicians wait for the conductor’s cue. Similarly, we can enable or disable nodes based on conditions. Just like in general programming, IfCondition and UnlessCondition can be used to determine if a node should be launched.
from launch.conditions import IfCondition, UnlessCondition
def generate_launch_description():
enable_listener = LaunchConfiguration('enable_listener')
return LaunchDescription([
DeclareLaunchArgument(
'enable_listener',
default_value='false',
description='Whether to launch the listener node'
),
# The listener joins only if 'enable_listener' is set to true
Node(
package='demo_nodes_cpp',
executable='listener',
name='my_listener',
output='screen',
condition=IfCondition(enable_listener),
),
])
To enable the listener node when launching:
ros2 launch my_package my_launch_file.py enable_listener:=true
6.7. Handling Events#
This next functionality is quite basic. If a node crashes, we can restart it. However, just like a conductor might signal a musician to rejoin, using event handlers, we can ensure that a node automatically restarts if it exists.
from launch.event_handlers import OnProcessExit
from launch.actions import RegisterEventHandler, LogInfo
def generate_launch_description():
listener_node = Node(
package='demo_nodes_cpp',
executable='listener',
name='my_listener',
output='screen',
)
restart_listener = RegisterEventHandler(
event_handler=OnProcessExit(
target_action=listener_node,
on_exit=[LogInfo(msg='Listener node exited, restarting...'), listener_node],
)
)
return LaunchDescription([
listener_node,
restart_listener, # Automatically restart listener if it stops
])