Skip to main content

ROS 2 Launch Files

Overview​

ROS 2 launch files provide a powerful way to start multiple nodes with specific configurations simultaneously. They allow you to define complex robot systems with a single command, manage parameters, handle dependencies, and orchestrate the startup sequence of your robot's software stack. This lesson covers the structure, syntax, and best practices for creating and using ROS 2 launch files.

What are Launch Files?​

Launch files in ROS 2 are scripts that define and start multiple nodes with specific configurations. They serve several important purposes:

  • Convenience: Start multiple nodes with a single command
  • Configuration: Set parameters and configurations for nodes
  • Orchestration: Control the startup order and dependencies
  • Reusability: Define common system configurations that can be reused
  • Modularity: Break down complex systems into manageable components

Launch File Formats​

ROS 2 supports multiple launch file formats, but Python is the most commonly used and recommended approach.

Python launch files offer the most flexibility and are the recommended approach:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
executable='turtle_teleop_key',
name='teleop'
)
])

XML Launch Files​

XML launch files provide a declarative approach:

<launch>
<node pkg="turtlesim" exec="turtlesim_node" name="sim"/>
<node pkg="turtlesim" exec="turtle_teleop_key" name="teleop"/>
</launch>

YAML Launch Files​

YAML launch files offer another declarative option:

launch:
- node:
pkg: "turtlesim"
exec: "turtlesim_node"
name: "sim"
- node:
pkg: "turtlesim"
exec: "turtle_teleop_key"
name: "teleop"

Basic Python Launch File Structure​

Required Imports​

Every Python launch file needs these basic imports:

from launch import LaunchDescription
from launch_ros.actions import Node

The generate_launch_description Function​

This is the entry point for the launch file:

def generate_launch_description():
return LaunchDescription([
# List of launch actions here
])

Basic Node Launch Action​

The most common launch action is starting a node:

Node(
package='package_name',
executable='executable_name',
name='node_name', # Optional, defaults to executable name
namespace='namespace', # Optional
parameters=[{'param1': 'value1'}], # Optional
remappings=[('original_topic', 'new_topic')], # Optional
arguments=['arg1', 'arg2'], # Optional
output='screen' # Optional: 'log', 'screen', or 'both'
)

Advanced Launch Features​

Parameters​

You can set parameters for nodes in several ways:

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
return LaunchDescription([
Node(
package='navigation2',
executable='nav2_amcl',
name='amcl',
parameters=[
# Direct parameter specification
{'use_sim_time': True},
# Load from YAML file
PathJoinSubstitution([
FindPackageShare('my_robot_bringup'),
'config',
'amcl_config.yaml'
])
]
)
])

Remappings​

Remap topics between nodes:

Node(
package='image_proc',
executable='rectify',
name='image_rect',
remappings=[
('image', '/camera/image_raw'),
('camera_info', '/camera/camera_info'),
('image_rect', '/camera/image_rect')
]
)

Arguments and Substitutions​

Launch files can accept arguments and use substitutions:

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

def generate_launch_description():
# Declare launch arguments
use_sim_time = LaunchConfiguration('use_sim_time')

declare_use_sim_time_cmd = DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation (Gazebo) clock if true'
)

return LaunchDescription([
declare_use_sim_time_cmd,
Node(
package='my_robot_control',
executable='controller',
name='controller',
parameters=[{'use_sim_time': use_sim_time}]
)
])

Conditional Launch Actions​

Execute actions based on conditions:

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

def generate_launch_description():
# Declare argument
use_rviz = LaunchConfiguration('use_rviz')

declare_use_rviz_cmd = DeclareLaunchArgument(
'use_rviz',
default_value='true',
description='Whether to start RViz'
)

return LaunchDescription([
declare_use_rviz_cmd,
# Start RViz only if use_rviz is true
Node(
package='rviz2',
executable='rviz2',
name='rviz',
output='screen',
condition=IfCondition(use_rviz)
)
])

Common Launch Actions​

Including Other Launch Files​

Include other launch files to build modular systems:

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():
# Include another launch file
turtlebot3_bringup_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
PathJoinSubstitution([
FindPackageShare('turtlebot3_bringup'),
'launch',
'robot.launch.py'
])
])
)

return LaunchDescription([
turtlebot3_bringup_launch,
# Additional nodes can be added here
])

Setting Environment Variables​

Set environment variables for launched nodes:

from launch.actions import SetEnvironmentVariable

def generate_launch_description():
return LaunchDescription([
# Set environment variable
SetEnvironmentVariable(name='RCUTILS_LOGGING_SEVERITY_THRESHOLD', value='INFO'),
Node(
package='my_package',
executable='my_node',
name='my_node'
)
])

Executing Shell Commands​

Execute shell commands as part of the launch process:

from launch.actions import ExecuteProcess

def generate_launch_description():
return LaunchDescription([
# Execute a shell command
ExecuteProcess(
cmd=['ros2', 'param', 'set', '/my_node', 'param_name', 'param_value'],
output='screen'
),
Node(
package='my_package',
executable='my_node',
name='my_node'
)
])

Complex Launch File Example​

Here's a comprehensive example showing multiple advanced features:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, TimerAction
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
# Declare launch arguments
use_sim_time = LaunchConfiguration('use_sim_time')
use_rviz = LaunchConfiguration('use_rviz')
slam = LaunchConfiguration('slam')

declare_use_sim_time_cmd = DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation (Gazebo) clock if true'
)

declare_use_rviz_cmd = DeclareLaunchArgument(
'use_rviz',
default_value='true',
description='Whether to start RViz'
)

declare_slam_cmd = DeclareLaunchArgument(
'slam',
default_value='False',
description='Whether to run SLAM'
)

# Nodes
robot_state_publisher_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'use_sim_time': use_sim_time}],
remappings=[
('/joint_states', 'demo_joint_states'),
]
)

joint_state_publisher_node = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
name='joint_state_publisher',
parameters=[{'use_sim_time': use_sim_time}],
remappings=[
('/joint_states', 'demo_joint_states'),
]
)

# Create launch description
ld = LaunchDescription()

# Add launch arguments
ld.add_action(declare_use_sim_time_cmd)
ld.add_action(declare_use_rviz_cmd)
ld.add_action(declare_slam_cmd)

# Add nodes
ld.add_action(robot_state_publisher_node)
ld.add_action(joint_state_publisher_node)

# Add conditional nodes
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
arguments=['-d', PathJoinSubstitution([
FindPackageShare('my_robot_description'),
'rviz',
'view_robot.rviz'
])],
parameters=[{'use_sim_time': use_sim_time}],
condition=IfCondition(use_rviz)
)
ld.add_action(rviz_node)

# Add SLAM node if enabled
slam_node = Node(
condition=IfCondition(slam),
package='slam_toolbox',
executable='async_slam_toolbox_node',
name='slam_toolbox',
parameters=[
PathJoinSubstitution([
FindPackageShare('my_robot_bringup'),
'config',
'slam.yaml'
]),
{'use_sim_time': use_sim_time}
]
)
ld.add_action(slam_node)

return ld

Launch File Organization​

Package Structure​

A well-organized package structure for launch files:

my_robot_bringup/
├── launch/
│ ├── robot.launch.py
│ ├── navigation.launch.py
│ └── simulation.launch.py
├── config/
│ ├── robot.yaml
│ └── navigation.yaml
├── rviz/
│ └── view_robot.rviz
└── package.xml

Installation​

To make launch files available in your package, add to setup.py:

import os
from glob import glob
from setuptools import setup

package_name = 'my_robot_bringup'

setup(
# ... other setup parameters ...
data_files=[
# ... other data files ...
(os.path.join('share', package_name, 'launch'), glob('launch/*')),
(os.path.join('share', package_name, 'config'), glob('config/*')),
(os.path.join('share', package_name, 'rviz'), glob('rviz/*')),
],
)

Or in CMakeLists.txt:

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

Running Launch Files​

From Command Line​

Launch files can be run from the command line:

# Run a launch file from a package
ros2 launch my_package my_launch_file.py

# With arguments
ros2 launch my_package my_launch_file.py use_sim_time:=true

# With multiple arguments
ros2 launch my_package my_launch_file.py use_sim_time:=true use_rviz:=false

Launch File Arguments​

Arguments can be passed to launch files:

# Boolean argument
ros2 launch my_package launch_file.py enable_viz:=true

# Numeric argument
ros2 launch my_package launch_file.py rate:=10.0

# String argument
ros2 launch my_package launch_file.py robot_name:=turtlebot3

Launch System Best Practices​

1. Modular Design​

  • Break complex systems into smaller, reusable launch files
  • Use IncludeLaunchDescription to compose systems
  • Keep launch files focused on specific functionality

2. Parameter Management​

  • Use YAML files for complex parameter configurations
  • Group related parameters logically
  • Use launch arguments for configurable options

3. Naming Conventions​

  • Use descriptive names for nodes and parameters
  • Follow consistent naming patterns
  • Use snake_case for launch file names

4. Error Handling​

  • Provide clear error messages
  • Use conditions to handle optional components
  • Validate input arguments when possible

5. Documentation​

  • Comment complex launch files
  • Document launch arguments and their purposes
  • Include usage examples

Launch File Debugging​

Verbose Output​

Get detailed information about launch execution:

ros2 launch -v my_package my_launch_file.py

Check Launch File Syntax​

Verify launch file syntax without executing:

python3 path/to/launch_file.py

Common Issues and Solutions​

Issue: Launch file not found​

Solution: Check package installation and launch file path.

Issue: Node fails to start​

Solution: Check package dependencies and node executable permissions.

Issue: Parameters not loaded​

Solution: Verify parameter file paths and YAML syntax.

Issue: Nodes can't communicate​

Solution: Check namespaces, remappings, and network configuration.

Advanced Launch Concepts​

Launch Substitutions​

Substitutions allow dynamic value insertion:

from launch.substitutions import TextSubstitution, EnvironmentVariable
from launch.actions import LogInfo

def generate_launch_description():
return LaunchDescription([
LogInfo(
msg=["Launch time: ", TextSubstitution(text=str(time.time()))]
),
LogInfo(
msg=["Home directory: ", EnvironmentVariable(name='HOME')]
)
])

Event Handling​

Handle events during launch execution:

from launch import LaunchDescription
from launch.actions import RegisterEventHandler
from launch.event_handlers import OnProcessStart, OnProcessExit
from launch_ros.actions import Node

def generate_launch_description():
node1 = Node(
package='my_package',
executable='node1',
name='node1'
)

node2 = Node(
package='my_package',
executable='node2',
name='node2'
)

# Start node2 only after node1 starts
delayed_node2 = RegisterEventHandler(
OnProcessStart(
target_action=node1,
on_start=[node2],
)
)

return LaunchDescription([
node1,
delayed_node2
])

Learning Objectives​

By the end of this lesson, you should be able to:

  • Create and structure ROS 2 launch files using Python
  • Configure nodes with parameters, remappings, and arguments
  • Use advanced launch features like conditions and substitutions
  • Organize launch files in a modular, reusable way
  • Debug common launch file issues
  • Apply best practices for launch file development
  • Understand the launch system architecture and event handling
Physical AI assistant
Hello! I am your Physical AI assistant. Ask me anything about the course!