README
simple_launch
This package provides a Python class to help writing ROS 2 launch files.
The motivation behind this package is that the ROS 2 launch Python syntax may not be suited for many simple cases such as running basic nodes, spawning a robot_state_publisher, and grouping nodes in namespaces or components..
Thanks: this package is inspired by ros2_launch_util.
The entry point is the SimpleLauncher class, which has several capabilities.
Basic syntax
Namespace and argument parser initialization
sl = SimpleLauncher(namespace)
Node registration
sl.node(package, executable, **node_args) where
packageis the node packageexecutableis the name of the executablenode_argsare any additionalNodeargumentsin particular,
argumentscan be a single string that will be split into a list
Launch file include
sl.include(package, launch_file, launch_dir = None, launch_arguments=None) where
packageis the package of the included launch filelaunch_fileis the name of the launch filelaunch_diris its directory inside the package share (Noneto have it found)launch_argumentsis a(key, value)list of arguments to pass to the included launch file
Launch arguments
The helper class allows declaring launch arguments and getting them in return:
Declare a launch argument
sl.declare_arg(name, default_value, description = None)
Retrieve a launch argument
sl.arg(name): returns the argument name
Retrieve several arguments as a dictionary
sl.arg_map(('robot', 'x', 'y')): returns {'robot': <robot arg value>, 'x': <x arg value>, 'y': <y arg value>}
Node groups
Groups are created through the sl.with() syntax and accepts both a namespace and/or an if/unless condition:
By namespace
with sl.group(ns=sub_namespace):
sl.node(package, executable)
From conditional arguments
with sl.group(if_arg='use_gui'):
sl.node(package, executable)
with sl.group(unless_arg='dont_do'):
sl.node(package, executable)
If if_arg / unless_arg is a string then the corresponding launch argument is used. Otherwise the raw value is passed as a condition (Boolean / Substitution).
Creating containers
This syntax adds the composition/composition::Talker as a ComposableNode
with sl.container(name='my_container', output='screen'):
sl.node(package='composition', plugin='Talker', name='talker')
Other shortcuts
String / substitution concatenation
The following syntax builds <robot name>.xacro:
file_name = sl.name_join(sl.arg('robot'), '.xacro')
Path concatenation
The following syntax builds <my_package_path>/urdf/<robot name>.xacro:
file_name = sl.name_join(sl.arg('robot'), '.xacro')
urdf_file = sl.path_join(get_package_share_directory(package), 'urdf', file_name)
Robot state publisher
sl.robot_state_publisher(package, description_file, description_dir=None,xacro_args=None, tf_prefix = None, **node_args) where
description_fileis a URDF or xacro filedescription_dir, if omitted, let the script search for the filexacro_argsare passed to xacrotf_prefixis passed torobot_state_publisher(not used as of Foxy)node_argsare any additional arguments forrobot_state_publisher(typically remapping)
Joint state publisher
sl.joint_state_publisher(use_gui, **node_args): fires up a joint_state_publisher, with or without the gui.
Fallback to low-level syntax
If any unavailable functionality is needed, the sl.entity(entity) function adds any passed Entity at the current namespace / conditional / composition level.
Examples
Examples can be found in the corresponding folder.
Launch with arguments
Here is a file spawning a robot_state_publisher and its joint_state_publisher, where:
prefix,xandyare launch arguments that are passed to xacrouse_guitells whetherjoint_state_publishershould run the gui
from simple_launch import SimpleLauncher
def generate_launch_description():
'''
Launch description for a single robot - runs the two nodes in their own namespace
'''
sl = SimpleLauncher()
sl.declare_arg('prefix', default_value = '', description='name of the robot (+ tf prefix)')
sl.declare_arg('x', default_value = 0, description='x-offset of the robot')
sl.declare_arg('y', default_value = 0, description='y-offset of the robot')
sl.declare_arg('use_gui', default_value = True, description='Use JSP gui')
xacro_args = sl.arg_map(('prefix', 'x', 'y'))
xacro_args['prefix'] = [xacro_args['prefix'], ':']
with sl.group(ns=sl.arg('prefix')):
sl.robot_state_publisher('simple_launch', 'turret.xacro', xacro_args = xacro_args)
sl.joint_state_publisher(sources_list = ['source_joints'], use_gui = sl.arg('use_gui'))
return sl.launch_description()
Conditional blocks and includes
The file below fires up either robot1 or robot2 (or both) and also has a boolean argument to spawn RViz2:
from simple_launch import SimpleLauncher
def generate_launch_description():
sl = SimpleLauncher()
# conditional args
sl.declare_arg('robot1', default_value=False, description='use robot 1')
sl.declare_arg('robot2', default_value=True, description='use robot 2')
sl.declare_arg('no_robot2', default_value=False, description='cancel use of robot 2')
sl.declare_arg('rviz', default_value=False, description='Bringup RViz2')
# numeric args
sl.declare_arg('robot2_x', default_value=1, description='x-offset of robot 2')
sl.declare_arg('robot2_y', default_value=1, description='y-offset of robot 2')
with sl.group(if_arg='robot1'):
sl.include('simple_launch', 'included_launch.py', launch_arguments = [('prefix', 'robot1')])
with sl.group(if_arg='robot2'):
with sl.group(unless_arg='no_robot2'):
args = [('prefix', 'robot2'), ('x', sl.arg('robot2_x')), ('y', sl.arg('robot2_y'))]
sl.include('simple_launch', 'included_launch.py', launch_arguments=args)
with sl.group(if_arg='rviz'):
rviz_config = sl.find('simple_launch', 'turret.rviz')
sl.node('rviz2', 'rviz2', arguments = ['-d', rviz_config])
return sl.launch_description()
Composition
The file below is another way to write the composition launch example:
from simple_launch import SimpleLauncher
def generate_launch_description():
sl = SimpleLauncher()
with sl.container(name='my_container', output='screen'):
sl.node(package='composition', plugin='Talker', name='talker')
sl.node(package='composition', plugin='Listener', name='listener')
return sl.launch_description()