Launch Files and Parameter Management
Learning Objectives
By the end of this section, you will be able to:
- Create and use launch files to manage multiple nodes
- Configure parameters using YAML files and launch files
- Implement conditional node launching
- Use launch arguments for flexible configurations
Launch System Overview
The ROS 2 launch system provides a Python-based framework for starting multiple nodes and configuring their parameters. It replaces the XML-based launch system from ROS 1 with a more flexible and powerful Python-based approach.
Why Use Launch Files?
- Convenience: Start multiple nodes with a single command
- Configuration: Set parameters and configurations for all nodes
- Flexibility: Use arguments to change behavior without modifying files
- Organization: Group related nodes together logically
- Testing: Create specific configurations for different scenarios
Basic Launch File Structure
Simple Launch File
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='demo_nodes_cpp',
executable='talker',
name='my_talker'
),
Node(
package='demo_nodes_cpp',
executable='listener',
name='my_listener'
)
])
Launch File with Parameters
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_robot_package',
executable='robot_controller',
name='robot_controller',
parameters=[
{'wheel_radius': 0.05},
{'base_frame': 'base_link'},
{'odom_frame': 'odom'},
]
)
])
Launch Arguments
Launch arguments allow you to customize launch files without modifying them:
Adding Arguments to Launch Files
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 = DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation time'
)
robot_name = DeclareLaunchArgument(
'robot_name',
default_value='turtlebot4',
description='Name of the robot'
)
return LaunchDescription([
# Add argument declarations
use_sim_time,
robot_name,
# Use arguments in nodes
Node(
package='my_robot_package',
executable='robot_controller',
name=[LaunchConfiguration('robot_name'), '_controller'],
parameters=[
{'use_sim_time': LaunchConfiguration('use_sim_time')},
{'robot_name': LaunchConfiguration('robot_name')},
]
)
])
Using Arguments When Launching
# Use default values
ros2 launch my_robot_package my_launch_file.py
# Override specific arguments
ros2 launch my_robot_package my_launch_file.py use_sim_time:=true robot_name:=my_robot
Advanced Launch Features
Conditional Launch
Launch nodes 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():
use_rviz = DeclareLaunchArgument(
'use_rviz',
default_value='true',
description='Whether to launch RViz'
)
rviz_node = Node(
condition=IfCondition(LaunchConfiguration('use_rviz')),
package='rviz2',
executable='rviz2',
name='rviz2'
)
return LaunchDescription([
use_rviz,
rviz_node,
# Other nodes...
])
Launch Included Files
Include other launch files in your launch file:
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
# Include another launch file
other_launch_file = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
PathJoinSubstitution([
get_package_share_directory('other_package'),
'launch',
'other_launch_file.py'
])
])
)
return LaunchDescription([
other_launch_file,
# Additional nodes...
])
YAML Parameter Files
Creating Parameter Files
Create parameter files in a config directory:
# config/robot_params.yaml
robot_controller:
ros__parameters:
# Basic configuration
robot_name: 'turtlebot4'
use_sim_time: false
# Hardware parameters
wheel_radius: 0.05
wheel_separation: 0.3
encoder_resolution: 4096
# Control parameters
max_linear_velocity: 0.5
max_angular_velocity: 1.0
linear_acceleration: 1.0
angular_acceleration: 2.0
# Sensor parameters
laser_scan_topic: '/scan'
camera_topic: '/camera/image_raw'
# Navigation parameters
planner_frequency: 5.0
controller_frequency: 20.0
recovery_enabled: true
Loading Parameters from YAML
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
# Declare parameter file argument
params_file = DeclareLaunchArgument(
'params_file',
default_value=PathJoinSubstitution([
get_package_share_directory('my_robot_package'),
'config',
'robot_params.yaml'
]),
description='Full path to the ROS2 parameters file'
)
return LaunchDescription([
params_file,
Node(
package='my_robot_package',
executable='robot_controller',
name='robot_controller',
parameters=[LaunchConfiguration('params_file')],
output='screen'
)
])
Complex Parameter Management
Multiple Parameter Sources
You can combine multiple parameter sources:
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
# Define configuration
config_file = PathJoinSubstitution([
get_package_share_directory('my_robot_package'),
'config',
'robot_params.yaml'
])
return LaunchDescription([
Node(
package='my_robot_package',
executable='robot_controller',
name='robot_controller',
parameters=[
config_file, # From YAML file
{'robot_name': 'custom_robot'}, # Override specific parameter
{'use_sim_time': True}, # Additional parameter
],
output='screen'
)
])
Parameter Remapping
Remap topics, services, and parameters:
Node(
package='my_robot_package',
executable='robot_controller',
name='robot_controller',
parameters=[
{'robot_name': 'turtlebot4'},
],
remappings=[
('/original_topic', '/remapped_topic'),
('/cmd_vel', '/navigation/cmd_vel'),
('/scan', '/laser_scan'),
]
)
Launch Actions
Execute Processes
Run external processes alongside ROS nodes:
from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
# Start a background process
ExecuteProcess(
cmd=['ros2', 'bag', 'record', '-a'],
output='screen'
),
# Start ROS nodes
Node(
package='demo_nodes_cpp',
executable='talker',
name='talker'
)
])
Timer Actions
Delay node startup or run actions after a delay:
from launch import LaunchDescription
from launch.actions import TimerAction
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='demo_nodes_cpp',
executable='talker',
name='talker'
),
# Start listener 5 seconds after talker
TimerAction(
period=5.0,
actions=[
Node(
package='demo_nodes_cpp',
executable='listener',
name='listener'
)
]
)
])
Best Practices for Launch Files
Organize by Functionality
Group related functionality in separate launch files:
launch/
├── robot.launch.py # Core robot nodes
├── navigation.launch.py # Navigation stack
├── perception.launch.py # Perception stack
├── visualization.launch.py # RViz and other visualization
└── bringup.launch.py # Includes all of the above
Use Descriptive Names
# Good: Descriptive names
Node(
package='my_robot_driver',
executable='wheel_odometry_node',
name='wheel_odometry'
)
# Less good: Generic names
Node(
package='my_robot_driver',
executable='node',
name='n1'
)
Modular Design
Create reusable launch file components:
# launch/robot_description.py
from launch import LaunchDescription
from launch_ros.actions import Node
def get_robot_description_nodes():
return [
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[
{'robot_description': Command(['xacro ', FindFile('my_robot_description', 'urdf/my_robot.urdf.xacro')])}
]
)
]
# launch/bringup.py
from launch import LaunchDescription
from launch_ros.actions import Node
from .robot_description import get_robot_description_nodes
def generate_launch_description():
nodes = get_robot_description_nodes()
nodes.extend([
Node(
package='my_robot_driver',
executable='driver_node',
name='driver'
)
])
return LaunchDescription(nodes)
Common Launch Patterns
Sensor Processing Pipeline
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 arguments
use_sim_time = DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation time'
)
return LaunchDescription([
use_sim_time,
# Point cloud processing
Node(
package='my_robot_perception',
executable='point_cloud_filter',
name='point_cloud_filter',
parameters=[
{'use_sim_time': LaunchConfiguration('use_sim_time')},
],
remappings=[
('input_cloud', '/velodyne_points'),
('output_cloud', '/filtered_points'),
]
),
# Object detection
Node(
package='my_robot_perception',
executable='object_detector',
name='object_detector',
parameters=[
{'use_sim_time': LaunchConfiguration('use_sim_time')},
],
remappings=[
('input_cloud', '/filtered_points'),
('detections', '/object_detections'),
]
)
])
Multi-Robot Launch
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration, TextSubstitution
from launch_ros.actions import Node
def generate_launch_description():
# Robot-specific arguments
robot1_namespace = DeclareLaunchArgument(
'robot1_namespace',
default_value='robot1',
description='Namespace for robot 1'
)
robot2_namespace = DeclareLaunchArgument(
'robot2_namespace',
default_value='robot2',
description='Namespace for robot 2'
)
return LaunchDescription([
robot1_namespace,
robot2_namespace,
# Robot 1 nodes
Node(
package='my_robot_package',
executable='robot_controller',
name='controller',
namespace=LaunchConfiguration('robot1_namespace'),
parameters=[
{'robot_name': [LaunchConfiguration('robot1_namespace'), TextSubstitution(text='_robot')]},
]
),
# Robot 2 nodes
Node(
package='my_robot_package',
executable='robot_controller',
name='controller',
namespace=LaunchConfiguration('robot2_namespace'),
parameters=[
{'robot_name': [LaunchConfiguration('robot2_namespace'), TextSubstitution(text='_robot')]},
]
)
])
Interactive Elements
Launch System Assessment
What is the primary purpose of launch arguments in ROS 2 launch files?
Summary
Launch files and parameter management are essential tools for organizing and configuring ROS 2 systems. They allow you to start multiple nodes with a single command, manage complex parameter configurations, and create flexible, reusable system configurations. Proper use of launch files improves the maintainability and usability of robotic systems.
In the next week, we'll explore robot simulation with Gazebo, where we'll see how launch files are used to start simulation environments alongside robot controllers.