simple_launch package from simple_launch repo

simple_launch

Package Summary

Tags No category tags.
Version 1.4.1
License MIT
Build type AMENT_PYTHON
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/oKermorgant/simple_launch.git
VCS Type git
VCS Version devel
Last Updated 2022-07-01
Dev Status MAINTAINED
CI status No Continuous Integration
Released RELEASED
Tags No category tags.
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Package Description

Python helper class for the ROS 2 launch system

Additional Links

No additional links.

Maintainers

  • Olivier Kermorgant

Authors

No additional authors.

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 = '', use_sim_time = None)

  • will initialize all nodes relative to the given namespace
  • if use_sim_time is a Boolean, creates a use_sim_time launch argument with this value as the default and forwards it to all nodes, unless explicitely specified when running the node
  • if use_sim_time is 'auto', then SimpleLauncher will set it to True if the /clock topic is advertized (case of an already running simulation). This may have side effects if the /clock topic is advertized but you want to use this launch file with system clock.
  • if use_sim_time is None (default) then no particular value is forwarded to the nodes

Node registration

sl.node(package, executable, **node_args) where

  • package is the node package
  • executable is the name of the executable
  • node_args are any additional Node arguments given as a list

Launch file include

sl.include(package, launch_file, launch_dir = None, launch_arguments=None) where

  • package is the package of the included launch file
  • launch_file is the name of the launch file
  • launch_dir is its directory inside the package share (None to have it found)
  • launch_arguments is a list of arguments to pass to the included launch file

Robust types for parameters

In the launch API, differents types are expected for:

  • node parameters: a list of dictionaries
  • node remappings: a list of (key, value) pairs
  • included launch arguments: a list of (key, value) pairs
  • xacro arguments: a concatenation of key:=value strings

The sl.include, sl.node and xacro_args calls allow using any type (the simplest being a single dictionary) and will convert to the one expected by the API.

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 a condition

  with sl.group(if_condition=True):
    sl.node(package, executable)

  with sl.group(unless_condition=<some expression>):
    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_arg / unless_arg is expected to be the name of a launch argument. These two lines are equivalent:

  with sl.group(if_arg='use_gui'):
  with sl.group(if_condition=sl.arg('use_gui'):

If if_arg / unless_arg is not a string then it is considered as a if_condition / unless_condition.

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')

Use the executable and package parameters if you want to use executors other than rclcpp_components's component_container:

  with sl.container(name='my_container', output='screen', executable='component_container_isolated'):

use_sim_time

The current use_sim_time setting can be retrieved through sl.sim_time that may be:

  • None, if use_sim_time was not set in the SimpleLauncher constructor
  • a raw Boolean, if use_sim_time was set to 'auto', depending on the /clock topic being advertized
  • a Boolean launch argument, if use_sim_time was set to True or False

In all cases, if the use_sim_time parameter is explicitely given to a node, it will be used instead of the SimpleLauncher instance one.

Interaction with Gazebo / Ignition

Note: Ignition being renamed to Gazebo, all tools in this section use Gazebo / gz names

Spawn a model

The sl.spawn_gz_model(name, topic, spawn_args = []) functions allows easily spawing a model from its robot_description:

  • name is the name this model will get in Gazebo
  • topic is the topic to obtain the model from, default is robot_description (relative to the current namespace)
  • spawn_args are any additional spawn arguments, e.g. the initial pose

Declare initial pose

Calling sl.declare_gazebo_axes() will declare all 6 parameters (x,y,z,roll,pitch,yaw) with null default values. If any axis is given (e.g. sl.declare_gazebo_axes(yaw = 3.14) then only this parameter will be declared.

Such parameters can be retrieved through sl.gazebo_axes_args(). As a consequence, it is easy to spawn a model with:

sl.declare_gazebo_axes()
sl.spawn_gz_model(name, spawn_args = sl.gazebo_axes_args())

Gazebo sim

The GazeboBridge class has a few static methods to interact with a running Gazebo. Namely:

  • GazeboBridge.world() returns the current world name
  • GazeboBridge.model_prefix(model) builds the Gazebo topic relative to the given model /world/<world>/model/<model>
  • GazeboBridge.has_model(model) returns True of False depending on the passed model existing in Gazebo already

Gazebo bridge

The GazeboBridge class allows easily creating bridges when using Gazebo. Gazebo has to be already running in order to get information on the simulation scene.

An instance is created with: bridge = GazeboBridge(<gazebo_topic>, <ros_topic>, <ros_message>, direction) where direction is either:

  • GazeboBridge.gz2ros for Gazebo -> ROS
  • GazeboBridge.ros2gz for ROS -> Gazebo
  • GazeboBridge.bidirectional for both

The Gazebo message type is deduced from the ros message type. Remapping will be set to the given ros_topic.

The SimpleLauncher instance can then run all created bridges with: sl.create_gz_bridge([bridges], <node_name>), as illustrated in the examples at this end of this document. If some bridges involve sensor_msgs/Image then SimpleLaunch will spawn a dedicated ros_ign_image bridge.

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)

Find a share file

path = sl.find(package, file_name, file_dir = None) where:

  • package is the name of the package or None
  • file_name is the name of the file to find
  • file_dir is the path inside the package

If file_dir is None then the find function will actually look for the file inside the package share, assuming that package and file_name are raw strings.

Robot state publisher

sl.robot_state_publisher(package, description_file, description_dir=None, xacro_args=None, prefix_gz_plugins=False, **node_args) where

  • description_file is a URDF or xacro file
  • description_dir is the sub-directory of the file. If omitted, let the script search for the file assuming it is a raw string
  • xacro_args are passed to xacro
  • prefix_gz_plugins is used only if a frame_prefix parameter is given to robot_state_publisher. In this case it will forward the frame prefix to Gazebo-published messages that include frame names
  • node_args are any additional arguments for robot_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, x and y are launch arguments that are passed to xacro
  • use_gui tells whether joint_state_publisher should 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()

auto sim time

Here we run Ignition and force all other nodes to use_sim_time:=True, unless this file is included from another one with use_sim_time:=False. This is unlikely as this launch file spawns a simulator.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    # all nodes in this launch file will use_sim_time:=True
    sl = SimpleLauncher(use_sim_time=True)

    # run Gazebo + clock bridge
    sl.include('ros_ign_gazebo','ign_gazebo.launch.py',launch_arguments={'''some sdf world'''}})
    sl.create_gz_clock_bridge()

    # run other nodes with sim time

    return sl.launch_description()


Robot description and conditionnal Gazebo bridge

The file below only runs by default a robot_state_publisher with use_sim_time=False. However, if it is included from another file with use_sim_time:=True then it also spawns the robot into Gazebo and run two bridges for joint states and pose.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    sl = SimpleLauncher(use_sim_time=False)

    # namespace is a launch argument, not a Python string
    sl.declare_arg('robot', default_value = 'robot1')
    robot = sl.arg('robot')

    with sl.group(ns = robot):
        # robot_state_publisher is always run
        sl.robot_state_publisher('my_description', 'my_robot.xacro')

        with sl.group(if_condition = sl.sim_time):
            # only execute this group if use_sim_time was set to True

            # spawn in Ignition at default pose if not already here
            # uses GazeboBridge.has_model(robot) under the hood and calls ros_ign_gazebo::create
            sl.spawn_gz_model(robot)

            # create a bridge for joint states @ /world/<world>/model/<robot>/joint_state
            # note the relative ROS topic 'joint_states' that is actually namespaced
            gz_js_topic = sl.name_join(GazeboBridge.model_prefix(robot), '/joint_state')
            js_bridge = GazeboBridge(gz_js_topic, 'joint_states', 'sensor_msgs/JointState', GazeboBridge.gz2ros)

            # pose publisher bridge @ /model/<robot>
            pose_bridge = GazeboBridge(sl.name_join('/model/', robot, '/pose'),
                                            'pose_gt', 'geometry_msgs/Pose', GazeboBridge.gz2ros)

            # create bridge node with these two topics with default name gz_bridge
            sl.create_gz_bridge([js_bridge, pose_bridge])

    return sl.launch_description()


CHANGELOG

Changelog for package simple_launch

1.4.1 (2022-06-01)

  • more handling of param types
  • Contributors: Olivier Kermorgant

1.4.0 (2022-06-01)

  • use Gazebo instead of Ignition
  • better type comprehension
  • Contributors: Olivier Kermorgant

1.3.1 (2022-05-02)

  • use underscores in setup.cfg
  • typo in included_launch example
  • Merge pull request #4 from yushijinhun/patch-1 Add package & executable parameter to container
  • [readme] add doc for container.package
  • Add package & executable param to container This allows the user to use a component container implementation other than component_container, such as component_container_isolated and component_container_mt.
  • Contributors: Haowei Wen, Olivier Kermorgant

1.3.0 (2022-02-03)

  • more use_sim_time: constructor + warning on \'auto\'
  • auto_sim_time can be forced instead of checking /clock
  • Contributors: Olivier Kermorgant

1.2.1 (2022-01-24)

  • add Ignition support
  • Contributors: Olivier Kermorgant

1.2.0 (2021-11-02)

  • make it clearer for the prefix_gz_plugins param
  • Contributors: Olivier Kermorgant

1.1.0 (2021-09-25)

  • add capacity to prefix and namespace Gazebo-published messages, as an alternative to using Gazebo namespaces that also remap /tf
  • Contributors: Olivier Kermorgant

1.0.6 (2021-09-22)

  • node arguments is a raw list and accepts substitutions
  • Contributors: Olivier Kermorgant

1.0.5 (2021-09-22)

  • automagically convert to expected type for node parameters, remappings, launch arguments
  • Contributors: Olivier Kermorgant

1.0.4 (2021-09-03)

  • some Python2 legacy bugs
  • Contributors: Olivier Kermorgant

1.0.3 (2021-04-17)

  • solve typo in node_args
  • better handling of node arguments in nested lists
  • bug fixes around Substitutions
  • Merge pull request #1 from oKermorgant/master deduce executable from package name if needed
  • Contributors: Olivier Kermorgant

1.0.2 (2020-12-08)

  • composition with existing container
  • Contributors: Olivier Kermorgant

1.0.1 (2020-10-12)

  • PythonExpression shortcut
  • xacro args now take substitution args
  • add LoadComposableNode feature for existing containers
  • add name_join and path_join to combine raw str and Substitution stuff
  • first commit
  • Contributors: Olivier Kermorgant

Wiki Tutorials

See ROS Wiki Tutorials for more details.

Source Tutorials

Not currently indexed.

Package Dependencies

System Dependencies

No direct system dependencies.

Dependant Packages

No known dependants.

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged simple_launch at answers.ros.org

simple_launch package from simple_launch repo

simple_launch

Package Summary

Tags No category tags.
Version 1.4.1
License MIT
Build type AMENT_PYTHON
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/oKermorgant/simple_launch.git
VCS Type git
VCS Version devel
Last Updated 2022-07-01
Dev Status MAINTAINED
CI status No Continuous Integration
Released RELEASED
Tags No category tags.
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Package Description

Python helper class for the ROS 2 launch system

Additional Links

No additional links.

Maintainers

  • Olivier Kermorgant

Authors

No additional authors.

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 = '', use_sim_time = None)

  • will initialize all nodes relative to the given namespace
  • if use_sim_time is a Boolean, creates a use_sim_time launch argument with this value as the default and forwards it to all nodes, unless explicitely specified when running the node
  • if use_sim_time is 'auto', then SimpleLauncher will set it to True if the /clock topic is advertized (case of an already running simulation). This may have side effects if the /clock topic is advertized but you want to use this launch file with system clock.
  • if use_sim_time is None (default) then no particular value is forwarded to the nodes

Node registration

sl.node(package, executable, **node_args) where

  • package is the node package
  • executable is the name of the executable
  • node_args are any additional Node arguments given as a list

Launch file include

sl.include(package, launch_file, launch_dir = None, launch_arguments=None) where

  • package is the package of the included launch file
  • launch_file is the name of the launch file
  • launch_dir is its directory inside the package share (None to have it found)
  • launch_arguments is a list of arguments to pass to the included launch file

Robust types for parameters

In the launch API, differents types are expected for:

  • node parameters: a list of dictionaries
  • node remappings: a list of (key, value) pairs
  • included launch arguments: a list of (key, value) pairs
  • xacro arguments: a concatenation of key:=value strings

The sl.include, sl.node and xacro_args calls allow using any type (the simplest being a single dictionary) and will convert to the one expected by the API.

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 a condition

  with sl.group(if_condition=True):
    sl.node(package, executable)

  with sl.group(unless_condition=<some expression>):
    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_arg / unless_arg is expected to be the name of a launch argument. These two lines are equivalent:

  with sl.group(if_arg='use_gui'):
  with sl.group(if_condition=sl.arg('use_gui'):

If if_arg / unless_arg is not a string then it is considered as a if_condition / unless_condition.

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')

Use the executable and package parameters if you want to use executors other than rclcpp_components's component_container:

  with sl.container(name='my_container', output='screen', executable='component_container_isolated'):

use_sim_time

The current use_sim_time setting can be retrieved through sl.sim_time that may be:

  • None, if use_sim_time was not set in the SimpleLauncher constructor
  • a raw Boolean, if use_sim_time was set to 'auto', depending on the /clock topic being advertized
  • a Boolean launch argument, if use_sim_time was set to True or False

In all cases, if the use_sim_time parameter is explicitely given to a node, it will be used instead of the SimpleLauncher instance one.

Interaction with Gazebo / Ignition

Note: Ignition being renamed to Gazebo, all tools in this section use Gazebo / gz names

Spawn a model

The sl.spawn_gz_model(name, topic, spawn_args = []) functions allows easily spawing a model from its robot_description:

  • name is the name this model will get in Gazebo
  • topic is the topic to obtain the model from, default is robot_description (relative to the current namespace)
  • spawn_args are any additional spawn arguments, e.g. the initial pose

Declare initial pose

Calling sl.declare_gazebo_axes() will declare all 6 parameters (x,y,z,roll,pitch,yaw) with null default values. If any axis is given (e.g. sl.declare_gazebo_axes(yaw = 3.14) then only this parameter will be declared.

Such parameters can be retrieved through sl.gazebo_axes_args(). As a consequence, it is easy to spawn a model with:

sl.declare_gazebo_axes()
sl.spawn_gz_model(name, spawn_args = sl.gazebo_axes_args())

Gazebo sim

The GazeboBridge class has a few static methods to interact with a running Gazebo. Namely:

  • GazeboBridge.world() returns the current world name
  • GazeboBridge.model_prefix(model) builds the Gazebo topic relative to the given model /world/<world>/model/<model>
  • GazeboBridge.has_model(model) returns True of False depending on the passed model existing in Gazebo already

Gazebo bridge

The GazeboBridge class allows easily creating bridges when using Gazebo. Gazebo has to be already running in order to get information on the simulation scene.

An instance is created with: bridge = GazeboBridge(<gazebo_topic>, <ros_topic>, <ros_message>, direction) where direction is either:

  • GazeboBridge.gz2ros for Gazebo -> ROS
  • GazeboBridge.ros2gz for ROS -> Gazebo
  • GazeboBridge.bidirectional for both

The Gazebo message type is deduced from the ros message type. Remapping will be set to the given ros_topic.

The SimpleLauncher instance can then run all created bridges with: sl.create_gz_bridge([bridges], <node_name>), as illustrated in the examples at this end of this document. If some bridges involve sensor_msgs/Image then SimpleLaunch will spawn a dedicated ros_ign_image bridge.

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)

Find a share file

path = sl.find(package, file_name, file_dir = None) where:

  • package is the name of the package or None
  • file_name is the name of the file to find
  • file_dir is the path inside the package

If file_dir is None then the find function will actually look for the file inside the package share, assuming that package and file_name are raw strings.

Robot state publisher

sl.robot_state_publisher(package, description_file, description_dir=None, xacro_args=None, prefix_gz_plugins=False, **node_args) where

  • description_file is a URDF or xacro file
  • description_dir is the sub-directory of the file. If omitted, let the script search for the file assuming it is a raw string
  • xacro_args are passed to xacro
  • prefix_gz_plugins is used only if a frame_prefix parameter is given to robot_state_publisher. In this case it will forward the frame prefix to Gazebo-published messages that include frame names
  • node_args are any additional arguments for robot_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, x and y are launch arguments that are passed to xacro
  • use_gui tells whether joint_state_publisher should 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()

auto sim time

Here we run Ignition and force all other nodes to use_sim_time:=True, unless this file is included from another one with use_sim_time:=False. This is unlikely as this launch file spawns a simulator.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    # all nodes in this launch file will use_sim_time:=True
    sl = SimpleLauncher(use_sim_time=True)

    # run Gazebo + clock bridge
    sl.include('ros_ign_gazebo','ign_gazebo.launch.py',launch_arguments={'''some sdf world'''}})
    sl.create_gz_clock_bridge()

    # run other nodes with sim time

    return sl.launch_description()


Robot description and conditionnal Gazebo bridge

The file below only runs by default a robot_state_publisher with use_sim_time=False. However, if it is included from another file with use_sim_time:=True then it also spawns the robot into Gazebo and run two bridges for joint states and pose.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    sl = SimpleLauncher(use_sim_time=False)

    # namespace is a launch argument, not a Python string
    sl.declare_arg('robot', default_value = 'robot1')
    robot = sl.arg('robot')

    with sl.group(ns = robot):
        # robot_state_publisher is always run
        sl.robot_state_publisher('my_description', 'my_robot.xacro')

        with sl.group(if_condition = sl.sim_time):
            # only execute this group if use_sim_time was set to True

            # spawn in Ignition at default pose if not already here
            # uses GazeboBridge.has_model(robot) under the hood and calls ros_ign_gazebo::create
            sl.spawn_gz_model(robot)

            # create a bridge for joint states @ /world/<world>/model/<robot>/joint_state
            # note the relative ROS topic 'joint_states' that is actually namespaced
            gz_js_topic = sl.name_join(GazeboBridge.model_prefix(robot), '/joint_state')
            js_bridge = GazeboBridge(gz_js_topic, 'joint_states', 'sensor_msgs/JointState', GazeboBridge.gz2ros)

            # pose publisher bridge @ /model/<robot>
            pose_bridge = GazeboBridge(sl.name_join('/model/', robot, '/pose'),
                                            'pose_gt', 'geometry_msgs/Pose', GazeboBridge.gz2ros)

            # create bridge node with these two topics with default name gz_bridge
            sl.create_gz_bridge([js_bridge, pose_bridge])

    return sl.launch_description()


CHANGELOG

Changelog for package simple_launch

1.4.1 (2022-06-01)

  • more handling of param types
  • Contributors: Olivier Kermorgant

1.4.0 (2022-06-01)

  • use Gazebo instead of Ignition
  • better type comprehension
  • Contributors: Olivier Kermorgant

1.3.1 (2022-05-02)

  • use underscores in setup.cfg
  • typo in included_launch example
  • Merge pull request #4 from yushijinhun/patch-1 Add package & executable parameter to container
  • [readme] add doc for container.package
  • Add package & executable param to container This allows the user to use a component container implementation other than component_container, such as component_container_isolated and component_container_mt.
  • Contributors: Haowei Wen, Olivier Kermorgant

1.3.0 (2022-02-03)

  • more use_sim_time: constructor + warning on \'auto\'
  • auto_sim_time can be forced instead of checking /clock
  • Contributors: Olivier Kermorgant

1.2.1 (2022-01-24)

  • add Ignition support
  • Contributors: Olivier Kermorgant

1.2.0 (2021-11-02)

  • make it clearer for the prefix_gz_plugins param
  • Contributors: Olivier Kermorgant

1.1.0 (2021-09-25)

  • add capacity to prefix and namespace Gazebo-published messages, as an alternative to using Gazebo namespaces that also remap /tf
  • Contributors: Olivier Kermorgant

1.0.6 (2021-09-22)

  • node arguments is a raw list and accepts substitutions
  • Contributors: Olivier Kermorgant

1.0.5 (2021-09-22)

  • automagically convert to expected type for node parameters, remappings, launch arguments
  • Contributors: Olivier Kermorgant

1.0.4 (2021-09-03)

  • some Python2 legacy bugs
  • Contributors: Olivier Kermorgant

1.0.3 (2021-04-17)

  • solve typo in node_args
  • better handling of node arguments in nested lists
  • bug fixes around Substitutions
  • Merge pull request #1 from oKermorgant/master deduce executable from package name if needed
  • Contributors: Olivier Kermorgant

1.0.2 (2020-12-08)

  • composition with existing container
  • Contributors: Olivier Kermorgant

1.0.1 (2020-10-12)

  • PythonExpression shortcut
  • xacro args now take substitution args
  • add LoadComposableNode feature for existing containers
  • add name_join and path_join to combine raw str and Substitution stuff
  • first commit
  • Contributors: Olivier Kermorgant

Wiki Tutorials

See ROS Wiki Tutorials for more details.

Source Tutorials

Not currently indexed.

Package Dependencies

System Dependencies

No direct system dependencies.

Dependant Packages

No known dependants.

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged simple_launch at answers.ros.org

simple_launch package from simple_launch repo

simple_launch

Package Summary

Tags No category tags.
Version 1.4.1
License MIT
Build type AMENT_PYTHON
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/oKermorgant/simple_launch.git
VCS Type git
VCS Version devel
Last Updated 2022-07-01
Dev Status MAINTAINED
CI status No Continuous Integration
Released RELEASED
Tags No category tags.
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Package Description

Python helper class for the ROS 2 launch system

Additional Links

No additional links.

Maintainers

  • Olivier Kermorgant

Authors

No additional authors.

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 = '', use_sim_time = None)

  • will initialize all nodes relative to the given namespace
  • if use_sim_time is a Boolean, creates a use_sim_time launch argument with this value as the default and forwards it to all nodes, unless explicitely specified when running the node
  • if use_sim_time is 'auto', then SimpleLauncher will set it to True if the /clock topic is advertized (case of an already running simulation). This may have side effects if the /clock topic is advertized but you want to use this launch file with system clock.
  • if use_sim_time is None (default) then no particular value is forwarded to the nodes

Node registration

sl.node(package, executable, **node_args) where

  • package is the node package
  • executable is the name of the executable
  • node_args are any additional Node arguments given as a list

Launch file include

sl.include(package, launch_file, launch_dir = None, launch_arguments=None) where

  • package is the package of the included launch file
  • launch_file is the name of the launch file
  • launch_dir is its directory inside the package share (None to have it found)
  • launch_arguments is a list of arguments to pass to the included launch file

Robust types for parameters

In the launch API, differents types are expected for:

  • node parameters: a list of dictionaries
  • node remappings: a list of (key, value) pairs
  • included launch arguments: a list of (key, value) pairs
  • xacro arguments: a concatenation of key:=value strings

The sl.include, sl.node and xacro_args calls allow using any type (the simplest being a single dictionary) and will convert to the one expected by the API.

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 a condition

  with sl.group(if_condition=True):
    sl.node(package, executable)

  with sl.group(unless_condition=<some expression>):
    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_arg / unless_arg is expected to be the name of a launch argument. These two lines are equivalent:

  with sl.group(if_arg='use_gui'):
  with sl.group(if_condition=sl.arg('use_gui'):

If if_arg / unless_arg is not a string then it is considered as a if_condition / unless_condition.

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')

Use the executable and package parameters if you want to use executors other than rclcpp_components's component_container:

  with sl.container(name='my_container', output='screen', executable='component_container_isolated'):

use_sim_time

The current use_sim_time setting can be retrieved through sl.sim_time that may be:

  • None, if use_sim_time was not set in the SimpleLauncher constructor
  • a raw Boolean, if use_sim_time was set to 'auto', depending on the /clock topic being advertized
  • a Boolean launch argument, if use_sim_time was set to True or False

In all cases, if the use_sim_time parameter is explicitely given to a node, it will be used instead of the SimpleLauncher instance one.

Interaction with Gazebo / Ignition

Note: Ignition being renamed to Gazebo, all tools in this section use Gazebo / gz names

Spawn a model

The sl.spawn_gz_model(name, topic, spawn_args = []) functions allows easily spawing a model from its robot_description:

  • name is the name this model will get in Gazebo
  • topic is the topic to obtain the model from, default is robot_description (relative to the current namespace)
  • spawn_args are any additional spawn arguments, e.g. the initial pose

Declare initial pose

Calling sl.declare_gazebo_axes() will declare all 6 parameters (x,y,z,roll,pitch,yaw) with null default values. If any axis is given (e.g. sl.declare_gazebo_axes(yaw = 3.14) then only this parameter will be declared.

Such parameters can be retrieved through sl.gazebo_axes_args(). As a consequence, it is easy to spawn a model with:

sl.declare_gazebo_axes()
sl.spawn_gz_model(name, spawn_args = sl.gazebo_axes_args())

Gazebo sim

The GazeboBridge class has a few static methods to interact with a running Gazebo. Namely:

  • GazeboBridge.world() returns the current world name
  • GazeboBridge.model_prefix(model) builds the Gazebo topic relative to the given model /world/<world>/model/<model>
  • GazeboBridge.has_model(model) returns True of False depending on the passed model existing in Gazebo already

Gazebo bridge

The GazeboBridge class allows easily creating bridges when using Gazebo. Gazebo has to be already running in order to get information on the simulation scene.

An instance is created with: bridge = GazeboBridge(<gazebo_topic>, <ros_topic>, <ros_message>, direction) where direction is either:

  • GazeboBridge.gz2ros for Gazebo -> ROS
  • GazeboBridge.ros2gz for ROS -> Gazebo
  • GazeboBridge.bidirectional for both

The Gazebo message type is deduced from the ros message type. Remapping will be set to the given ros_topic.

The SimpleLauncher instance can then run all created bridges with: sl.create_gz_bridge([bridges], <node_name>), as illustrated in the examples at this end of this document. If some bridges involve sensor_msgs/Image then SimpleLaunch will spawn a dedicated ros_ign_image bridge.

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)

Find a share file

path = sl.find(package, file_name, file_dir = None) where:

  • package is the name of the package or None
  • file_name is the name of the file to find
  • file_dir is the path inside the package

If file_dir is None then the find function will actually look for the file inside the package share, assuming that package and file_name are raw strings.

Robot state publisher

sl.robot_state_publisher(package, description_file, description_dir=None, xacro_args=None, prefix_gz_plugins=False, **node_args) where

  • description_file is a URDF or xacro file
  • description_dir is the sub-directory of the file. If omitted, let the script search for the file assuming it is a raw string
  • xacro_args are passed to xacro
  • prefix_gz_plugins is used only if a frame_prefix parameter is given to robot_state_publisher. In this case it will forward the frame prefix to Gazebo-published messages that include frame names
  • node_args are any additional arguments for robot_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, x and y are launch arguments that are passed to xacro
  • use_gui tells whether joint_state_publisher should 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()

auto sim time

Here we run Ignition and force all other nodes to use_sim_time:=True, unless this file is included from another one with use_sim_time:=False. This is unlikely as this launch file spawns a simulator.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    # all nodes in this launch file will use_sim_time:=True
    sl = SimpleLauncher(use_sim_time=True)

    # run Gazebo + clock bridge
    sl.include('ros_ign_gazebo','ign_gazebo.launch.py',launch_arguments={'''some sdf world'''}})
    sl.create_gz_clock_bridge()

    # run other nodes with sim time

    return sl.launch_description()


Robot description and conditionnal Gazebo bridge

The file below only runs by default a robot_state_publisher with use_sim_time=False. However, if it is included from another file with use_sim_time:=True then it also spawns the robot into Gazebo and run two bridges for joint states and pose.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    sl = SimpleLauncher(use_sim_time=False)

    # namespace is a launch argument, not a Python string
    sl.declare_arg('robot', default_value = 'robot1')
    robot = sl.arg('robot')

    with sl.group(ns = robot):
        # robot_state_publisher is always run
        sl.robot_state_publisher('my_description', 'my_robot.xacro')

        with sl.group(if_condition = sl.sim_time):
            # only execute this group if use_sim_time was set to True

            # spawn in Ignition at default pose if not already here
            # uses GazeboBridge.has_model(robot) under the hood and calls ros_ign_gazebo::create
            sl.spawn_gz_model(robot)

            # create a bridge for joint states @ /world/<world>/model/<robot>/joint_state
            # note the relative ROS topic 'joint_states' that is actually namespaced
            gz_js_topic = sl.name_join(GazeboBridge.model_prefix(robot), '/joint_state')
            js_bridge = GazeboBridge(gz_js_topic, 'joint_states', 'sensor_msgs/JointState', GazeboBridge.gz2ros)

            # pose publisher bridge @ /model/<robot>
            pose_bridge = GazeboBridge(sl.name_join('/model/', robot, '/pose'),
                                            'pose_gt', 'geometry_msgs/Pose', GazeboBridge.gz2ros)

            # create bridge node with these two topics with default name gz_bridge
            sl.create_gz_bridge([js_bridge, pose_bridge])

    return sl.launch_description()


CHANGELOG

Changelog for package simple_launch

1.4.1 (2022-06-01)

  • more handling of param types
  • Contributors: Olivier Kermorgant

1.4.0 (2022-06-01)

  • use Gazebo instead of Ignition
  • better type comprehension
  • Contributors: Olivier Kermorgant

1.3.1 (2022-05-02)

  • use underscores in setup.cfg
  • typo in included_launch example
  • Merge pull request #4 from yushijinhun/patch-1 Add package & executable parameter to container
  • [readme] add doc for container.package
  • Add package & executable param to container This allows the user to use a component container implementation other than component_container, such as component_container_isolated and component_container_mt.
  • Contributors: Haowei Wen, Olivier Kermorgant

1.3.0 (2022-02-03)

  • more use_sim_time: constructor + warning on \'auto\'
  • auto_sim_time can be forced instead of checking /clock
  • Contributors: Olivier Kermorgant

1.2.1 (2022-01-24)

  • add Ignition support
  • Contributors: Olivier Kermorgant

1.2.0 (2021-11-02)

  • make it clearer for the prefix_gz_plugins param
  • Contributors: Olivier Kermorgant

1.1.0 (2021-09-25)

  • add capacity to prefix and namespace Gazebo-published messages, as an alternative to using Gazebo namespaces that also remap /tf
  • Contributors: Olivier Kermorgant

1.0.6 (2021-09-22)

  • node arguments is a raw list and accepts substitutions
  • Contributors: Olivier Kermorgant

1.0.5 (2021-09-22)

  • automagically convert to expected type for node parameters, remappings, launch arguments
  • Contributors: Olivier Kermorgant

1.0.4 (2021-09-03)

  • some Python2 legacy bugs
  • Contributors: Olivier Kermorgant

1.0.3 (2021-04-17)

  • solve typo in node_args
  • better handling of node arguments in nested lists
  • bug fixes around Substitutions
  • Merge pull request #1 from oKermorgant/master deduce executable from package name if needed
  • Contributors: Olivier Kermorgant

1.0.2 (2020-12-08)

  • composition with existing container
  • Contributors: Olivier Kermorgant

1.0.1 (2020-10-12)

  • PythonExpression shortcut
  • xacro args now take substitution args
  • add LoadComposableNode feature for existing containers
  • add name_join and path_join to combine raw str and Substitution stuff
  • first commit
  • Contributors: Olivier Kermorgant

Wiki Tutorials

See ROS Wiki Tutorials for more details.

Source Tutorials

Not currently indexed.

Package Dependencies

System Dependencies

No direct system dependencies.

Dependant Packages

No known dependants.

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged simple_launch at answers.ros.org

simple_launch package from simple_launch repo

simple_launch

Package Summary

Tags No category tags.
Version 1.4.1
License MIT
Build type AMENT_PYTHON
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/oKermorgant/simple_launch.git
VCS Type git
VCS Version devel
Last Updated 2022-07-01
Dev Status MAINTAINED
CI status No Continuous Integration
Released RELEASED
Tags No category tags.
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Package Description

Python helper class for the ROS 2 launch system

Additional Links

No additional links.

Maintainers

  • Olivier Kermorgant

Authors

No additional authors.

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 = '', use_sim_time = None)

  • will initialize all nodes relative to the given namespace
  • if use_sim_time is a Boolean, creates a use_sim_time launch argument with this value as the default and forwards it to all nodes, unless explicitely specified when running the node
  • if use_sim_time is 'auto', then SimpleLauncher will set it to True if the /clock topic is advertized (case of an already running simulation). This may have side effects if the /clock topic is advertized but you want to use this launch file with system clock.
  • if use_sim_time is None (default) then no particular value is forwarded to the nodes

Node registration

sl.node(package, executable, **node_args) where

  • package is the node package
  • executable is the name of the executable
  • node_args are any additional Node arguments given as a list

Launch file include

sl.include(package, launch_file, launch_dir = None, launch_arguments=None) where

  • package is the package of the included launch file
  • launch_file is the name of the launch file
  • launch_dir is its directory inside the package share (None to have it found)
  • launch_arguments is a list of arguments to pass to the included launch file

Robust types for parameters

In the launch API, differents types are expected for:

  • node parameters: a list of dictionaries
  • node remappings: a list of (key, value) pairs
  • included launch arguments: a list of (key, value) pairs
  • xacro arguments: a concatenation of key:=value strings

The sl.include, sl.node and xacro_args calls allow using any type (the simplest being a single dictionary) and will convert to the one expected by the API.

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 a condition

  with sl.group(if_condition=True):
    sl.node(package, executable)

  with sl.group(unless_condition=<some expression>):
    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_arg / unless_arg is expected to be the name of a launch argument. These two lines are equivalent:

  with sl.group(if_arg='use_gui'):
  with sl.group(if_condition=sl.arg('use_gui'):

If if_arg / unless_arg is not a string then it is considered as a if_condition / unless_condition.

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')

Use the executable and package parameters if you want to use executors other than rclcpp_components's component_container:

  with sl.container(name='my_container', output='screen', executable='component_container_isolated'):

use_sim_time

The current use_sim_time setting can be retrieved through sl.sim_time that may be:

  • None, if use_sim_time was not set in the SimpleLauncher constructor
  • a raw Boolean, if use_sim_time was set to 'auto', depending on the /clock topic being advertized
  • a Boolean launch argument, if use_sim_time was set to True or False

In all cases, if the use_sim_time parameter is explicitely given to a node, it will be used instead of the SimpleLauncher instance one.

Interaction with Gazebo / Ignition

Note: Ignition being renamed to Gazebo, all tools in this section use Gazebo / gz names

Spawn a model

The sl.spawn_gz_model(name, topic, spawn_args = []) functions allows easily spawing a model from its robot_description:

  • name is the name this model will get in Gazebo
  • topic is the topic to obtain the model from, default is robot_description (relative to the current namespace)
  • spawn_args are any additional spawn arguments, e.g. the initial pose

Declare initial pose

Calling sl.declare_gazebo_axes() will declare all 6 parameters (x,y,z,roll,pitch,yaw) with null default values. If any axis is given (e.g. sl.declare_gazebo_axes(yaw = 3.14) then only this parameter will be declared.

Such parameters can be retrieved through sl.gazebo_axes_args(). As a consequence, it is easy to spawn a model with:

sl.declare_gazebo_axes()
sl.spawn_gz_model(name, spawn_args = sl.gazebo_axes_args())

Gazebo sim

The GazeboBridge class has a few static methods to interact with a running Gazebo. Namely:

  • GazeboBridge.world() returns the current world name
  • GazeboBridge.model_prefix(model) builds the Gazebo topic relative to the given model /world/<world>/model/<model>
  • GazeboBridge.has_model(model) returns True of False depending on the passed model existing in Gazebo already

Gazebo bridge

The GazeboBridge class allows easily creating bridges when using Gazebo. Gazebo has to be already running in order to get information on the simulation scene.

An instance is created with: bridge = GazeboBridge(<gazebo_topic>, <ros_topic>, <ros_message>, direction) where direction is either:

  • GazeboBridge.gz2ros for Gazebo -> ROS
  • GazeboBridge.ros2gz for ROS -> Gazebo
  • GazeboBridge.bidirectional for both

The Gazebo message type is deduced from the ros message type. Remapping will be set to the given ros_topic.

The SimpleLauncher instance can then run all created bridges with: sl.create_gz_bridge([bridges], <node_name>), as illustrated in the examples at this end of this document. If some bridges involve sensor_msgs/Image then SimpleLaunch will spawn a dedicated ros_ign_image bridge.

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)

Find a share file

path = sl.find(package, file_name, file_dir = None) where:

  • package is the name of the package or None
  • file_name is the name of the file to find
  • file_dir is the path inside the package

If file_dir is None then the find function will actually look for the file inside the package share, assuming that package and file_name are raw strings.

Robot state publisher

sl.robot_state_publisher(package, description_file, description_dir=None, xacro_args=None, prefix_gz_plugins=False, **node_args) where

  • description_file is a URDF or xacro file
  • description_dir is the sub-directory of the file. If omitted, let the script search for the file assuming it is a raw string
  • xacro_args are passed to xacro
  • prefix_gz_plugins is used only if a frame_prefix parameter is given to robot_state_publisher. In this case it will forward the frame prefix to Gazebo-published messages that include frame names
  • node_args are any additional arguments for robot_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, x and y are launch arguments that are passed to xacro
  • use_gui tells whether joint_state_publisher should 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()

auto sim time

Here we run Ignition and force all other nodes to use_sim_time:=True, unless this file is included from another one with use_sim_time:=False. This is unlikely as this launch file spawns a simulator.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    # all nodes in this launch file will use_sim_time:=True
    sl = SimpleLauncher(use_sim_time=True)

    # run Gazebo + clock bridge
    sl.include('ros_ign_gazebo','ign_gazebo.launch.py',launch_arguments={'''some sdf world'''}})
    sl.create_gz_clock_bridge()

    # run other nodes with sim time

    return sl.launch_description()


Robot description and conditionnal Gazebo bridge

The file below only runs by default a robot_state_publisher with use_sim_time=False. However, if it is included from another file with use_sim_time:=True then it also spawns the robot into Gazebo and run two bridges for joint states and pose.

from simple_launch import SimpleLauncher, GazeboBridge

def generate_launch_description():

    sl = SimpleLauncher(use_sim_time=False)

    # namespace is a launch argument, not a Python string
    sl.declare_arg('robot', default_value = 'robot1')
    robot = sl.arg('robot')

    with sl.group(ns = robot):
        # robot_state_publisher is always run
        sl.robot_state_publisher('my_description', 'my_robot.xacro')

        with sl.group(if_condition = sl.sim_time):
            # only execute this group if use_sim_time was set to True

            # spawn in Ignition at default pose if not already here
            # uses GazeboBridge.has_model(robot) under the hood and calls ros_ign_gazebo::create
            sl.spawn_gz_model(robot)

            # create a bridge for joint states @ /world/<world>/model/<robot>/joint_state
            # note the relative ROS topic 'joint_states' that is actually namespaced
            gz_js_topic = sl.name_join(GazeboBridge.model_prefix(robot), '/joint_state')
            js_bridge = GazeboBridge(gz_js_topic, 'joint_states', 'sensor_msgs/JointState', GazeboBridge.gz2ros)

            # pose publisher bridge @ /model/<robot>
            pose_bridge = GazeboBridge(sl.name_join('/model/', robot, '/pose'),
                                            'pose_gt', 'geometry_msgs/Pose', GazeboBridge.gz2ros)

            # create bridge node with these two topics with default name gz_bridge
            sl.create_gz_bridge([js_bridge, pose_bridge])

    return sl.launch_description()


CHANGELOG

Changelog for package simple_launch

1.4.1 (2022-06-01)

  • more handling of param types
  • Contributors: Olivier Kermorgant

1.4.0 (2022-06-01)

  • use Gazebo instead of Ignition
  • better type comprehension
  • Contributors: Olivier Kermorgant

1.3.1 (2022-05-02)

  • use underscores in setup.cfg
  • typo in included_launch example
  • Merge pull request #4 from yushijinhun/patch-1 Add package & executable parameter to container
  • [readme] add doc for container.package
  • Add package & executable param to container This allows the user to use a component container implementation other than component_container, such as component_container_isolated and component_container_mt.
  • Contributors: Haowei Wen, Olivier Kermorgant

1.3.0 (2022-02-03)

  • more use_sim_time: constructor + warning on \'auto\'
  • auto_sim_time can be forced instead of checking /clock
  • Contributors: Olivier Kermorgant

1.2.1 (2022-01-24)

  • add Ignition support
  • Contributors: Olivier Kermorgant

1.2.0 (2021-11-02)

  • make it clearer for the prefix_gz_plugins param
  • Contributors: Olivier Kermorgant

1.1.0 (2021-09-25)

  • add capacity to prefix and namespace Gazebo-published messages, as an alternative to using Gazebo namespaces that also remap /tf
  • Contributors: Olivier Kermorgant

1.0.6 (2021-09-22)

  • node arguments is a raw list and accepts substitutions
  • Contributors: Olivier Kermorgant

1.0.5 (2021-09-22)

  • automagically convert to expected type for node parameters, remappings, launch arguments
  • Contributors: Olivier Kermorgant

1.0.4 (2021-09-03)

  • some Python2 legacy bugs
  • Contributors: Olivier Kermorgant

1.0.3 (2021-04-17)

  • solve typo in node_args
  • better handling of node arguments in nested lists
  • bug fixes around Substitutions
  • Merge pull request #1 from oKermorgant/master deduce executable from package name if needed
  • Contributors: Olivier Kermorgant

1.0.2 (2020-12-08)

  • composition with existing container
  • Contributors: Olivier Kermorgant

1.0.1 (2020-10-12)

  • PythonExpression shortcut
  • xacro args now take substitution args
  • add LoadComposableNode feature for existing containers
  • add name_join and path_join to combine raw str and Substitution stuff
  • first commit
  • Contributors: Olivier Kermorgant

Wiki Tutorials

See ROS Wiki Tutorials for more details.

Source Tutorials

Not currently indexed.

Package Dependencies

System Dependencies

No direct system dependencies.

Dependant Packages

No known dependants.

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged simple_launch at answers.ros.org

No version for distro noetic. Known supported distros are highlighted in the buttons above.
No version for distro melodic. Known supported distros are highlighted in the buttons above.
No version for distro ardent. Known supported distros are highlighted in the buttons above.
No version for distro bouncy. Known supported distros are highlighted in the buttons above.
No version for distro crystal. Known supported distros are highlighted in the buttons above.
No version for distro eloquent. Known supported distros are highlighted in the buttons above.
No version for distro dashing. Known supported distros are highlighted in the buttons above.
No version for distro lunar. Known supported distros are highlighted in the buttons above.
No version for distro jade. Known supported distros are highlighted in the buttons above.
No version for distro indigo. Known supported distros are highlighted in the buttons above.
No version for distro hydro. Known supported distros are highlighted in the buttons above.
No version for distro kinetic. Known supported distros are highlighted in the buttons above.