Launch file

Creating a launch file

The launch system in ROS 2 is responsible for helping the user describe the configuration of their system and then execute it as described. The configuration of the system includes what programs to run, where to run them, what arguments to pass them, and ROS-specific conventions which make it easy to reuse components throughout the system by giving them each a different configuration. It is also responsible for monitoring the state of the processes launched, and reporting and/or reacting to changes in the state of those processes.

Launch files written in Python, XML, or YAML can start and stop different nodes as well as trigger and act on various events. See Using Python, XML, and YAML for ROS 2 Launch Files for a description of the different formats. The package providing this framework is launch_ros, which uses the non-ROS-specific launch framework underneath.

Setup

Create a new directory to store your launch files:

mkdir launch

Write the launch file

Python

Copy and paste the complete code into the launch/turtlesim_mimic_launch.py file:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='turtlesim',
            namespace='turtlesim1',
            executable='turtlesim_node',
            name='sim'
        ),
        Node(
            package='turtlesim',
            namespace='turtlesim2',
            executable='turtlesim_node',
            name='sim'
        ),
        Node(
            package='turtlesim',
            executable='mimic',
            name='mimic',
            remappings=[
                ('/input/pose', '/turtlesim1/turtle1/pose'),
                ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
            ]
        )
    ])
XML

Copy and paste the complete code into the launch/turtlesim_mimic_launch.xml file:

<launch>
  <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim1"/>
  <node pkg="turtlesim" exec="turtlesim_node" name="sim" namespace="turtlesim2"/>
  <node pkg="turtlesim" exec="mimic" name="mimic">
    <remap from="/input/pose" to="/turtlesim1/turtle1/pose"/>
    <remap from="/output/cmd_vel" to="/turtlesim2/turtle1/cmd_vel"/>
  </node>
</launch>
YAML

Copy and paste the complete code into the launch/turtlesim_mimic_launch.yaml file:

launch:

- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "sim"
    namespace: "turtlesim1"

- node:
    pkg: "turtlesim"
    exec: "turtlesim_node"
    name: "sim"
    namespace: "turtlesim2"

- node:
    pkg: "turtlesim"
    exec: "mimic"
    name: "mimic"
    remap:
    -
        from: "/input/pose"
        to: "/turtlesim1/turtle1/pose"
    -
        from: "/output/cmd_vel"
        to: "/turtlesim2/turtle1/cmd_vel"

Examine the launch file

All of the launch files above are launching a system of three nodes, all from the turtlesim package. The goal of the system is to launch two turtlesim windows, and have one turtle mimic the movements of the other.

When launching the two turtlesim nodes, the only difference between them is their namespace values. Unique namespaces allow the system to start two nodes without node name or topic name conflicts. Both turtles in this system receive commands over the same topic and publish their pose over the same topic. With unique namespaces, messages meant for different turtles can be distinguished.

The final node is also from the turtlesim package, but a different executable: mimic. This node has added configuration details in the form of remappings. mimic’s /input/pose topic is remapped to /turtlesim1/turtle1/pose and it’s /output/cmd_vel topic to /turtlesim2/turtle1/cmd_vel. This means mimic will subscribe to /turtlesim1/sim’s pose topic and republish it for /turtlesim2/sim’s velocity command topic to subscribe to. In other words, turtlesim2 will mimic turtlesim1’s movements.

Python

These import statements pull in some Python launch modules.

from launch import LaunchDescription
from launch_ros.actions import Node

Next, the launch description itself begins:

def generate_launch_description():
   return LaunchDescription([

   ])

The first two actions in the launch description launch the two turtlesim windows:

Node(
    package='turtlesim',
    namespace='turtlesim1',
    executable='turtlesim_node',
    name='sim'
),
Node(
    package='turtlesim',
    namespace='turtlesim2',
    executable='turtlesim_node',
    name='sim'
),

The final action launches the mimic node with the remaps:

Node(
    package='turtlesim',
    executable='mimic',
    name='mimic',
    remappings=[
      ('/input/pose', '/turtlesim1/turtle1/pose'),
      ('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
    ]
)

ros2 launch

To run the launch file created above, enter into the directory you created earlier and run the following command:

cd launch
ros2 launch turtlesim_mimic_launch.py

Integrating launch files into ROS 2 packages

Create a package

mkdir -p launch_ws/src
cd launch_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 py_launch_example

Creating the structure to hold launch files

By convention, all launch files for a package are stored in the launch directory inside of the package. Make sure to create a launch directory at the top-level of the package you created above.

For Python packages, the directory containing your package should look like this:

src/
  py_launch_example/
    launch/
    package.xml
    py_launch_example/
    resource/
    setup.cfg
    setup.py
    test/

To enable colcon to locate and utilize our launch files, we need to inform Python’s setup tools of their presence. To achieve this, open the setup.py file, add the necessary import statements at the top, and include the launch files into the data_files parameter of setup

import os
from glob import glob
# Other imports ...

package_name = 'py_launch_example'

setup(
    # Other parameters ...
    data_files=[
        # ... Other data files
        # Include all launch files.
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*')))
    ]
)

Writing the launch file

Inside your launch directory, create a new launch file called my_script_launch.py. _launch.py is recommended, but not required, as the file suffix for Python launch files. However, the launch file name needs to end with launch.py to be recognized and autocompleted by ros2 launch.

Your launch file should define the generate_launch_description() function which returns a launch.LaunchDescription() to be used by the ros2 launch verb.

import launch
import launch_ros.actions

def generate_launch_description():
    return launch.LaunchDescription([
        launch_ros.actions.Node(
            package='demo_nodes_cpp',
            executable='talker',
            name='talker'),
  ])

Building and running the launch file

colcon build

After the colcon build has been successful and you’ve sourced the workspace, you should be able to run the launch file as follows:

ros2 launch py_launch_example my_script_launch.py

Using substitutions

Create and setup the package

Create a new package of build_type ament_python called launch_tutorial:

ros2 pkg create --build-type ament_python --license Apache-2.0 launch_tutorial

Inside of that package, create a directory called launch:

mkdir launch_tutorial/launch

Finally, make sure to add in changes to the setup.py of the package so that the launch files will be installed:

import os
from glob import glob
from setuptools import setup

package_name = 'launch_tutorial'

setup(
    # Other parameters ...
    data_files=[
        # ... Other data files
        # Include all launch files.
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*')))
    ]
)

Parent launch file

Let’s create a launch file that will call and pass arguments to another launch file. To do this, create an example_main.launch.py file in the launch folder of the launch_tutorial package.

from launch_ros.substitutions import FindPackageShare

from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution, TextSubstitution


def generate_launch_description():
    colors = {
        'background_r': '200'
    }

    return LaunchDescription([
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource([
                PathJoinSubstitution([
                    FindPackageShare('launch_tutorial'),
                    'launch',
                    'example_substitutions.launch.py'
                ])
            ]),
            launch_arguments={
                'turtlesim_ns': 'turtlesim2',
                'use_provided_red': 'True',
                'new_background_r': TextSubstitution(text=str(colors['background_r']))
            }.items()
        )
    ])

In the example_main.launch.py file, the FindPackageShare substitution is used to find the path to the launch_tutorial package. The PathJoinSubstitution substitution is then used to join the path to that package path with the example_substitutions.launch.py file name.

PathJoinSubstitution([
    FindPackageShare('launch_tutorial'),
    'launch',
    'example_substitutions.launch.py'
])

The launch_arguments dictionary with turtlesim_ns and use_provided_red arguments is passed to the IncludeLaunchDescription action. The TextSubstitution substitution is used to define the new_background_r argument with the value of the background_r key in the colors dictionary.

launch_arguments={
    'turtlesim_ns': 'turtlesim2',
    'use_provided_red': 'True',
    'new_background_r': TextSubstitution(text=str(colors['background_r']))
}.items()

Substitutions example launch file

Now create an example_substitutions.launch.py file in the same folder.

from launch_ros.actions import Node

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, ExecuteProcess, TimerAction
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, PythonExpression


def generate_launch_description():
    turtlesim_ns = LaunchConfiguration('turtlesim_ns')
    use_provided_red = LaunchConfiguration('use_provided_red')
    new_background_r = LaunchConfiguration('new_background_r')

    turtlesim_ns_launch_arg = DeclareLaunchArgument(
        'turtlesim_ns',
        default_value='turtlesim1'
    )
    use_provided_red_launch_arg = DeclareLaunchArgument(
        'use_provided_red',
        default_value='False'
    )
    new_background_r_launch_arg = DeclareLaunchArgument(
        'new_background_r',
        default_value='200'
    )

    turtlesim_node = Node(
        package='turtlesim',
        namespace=turtlesim_ns,
        executable='turtlesim_node',
        name='sim'
    )
    spawn_turtle = ExecuteProcess(
        cmd=[[
            'ros2 service call ',
            turtlesim_ns,
            '/spawn ',
            'turtlesim/srv/Spawn ',
            '"{x: 2, y: 2, theta: 0.2}"'
        ]],
        shell=True
    )
    change_background_r = ExecuteProcess(
        cmd=[[
            'ros2 param set ',
            turtlesim_ns,
            '/sim background_r ',
            '120'
        ]],
        shell=True
    )
    change_background_r_conditioned = ExecuteProcess(
        condition=IfCondition(
            PythonExpression([
                new_background_r,
                ' == 200',
                ' and ',
                use_provided_red
            ])
        ),
        cmd=[[
            'ros2 param set ',
            turtlesim_ns,
            '/sim background_r ',
            new_background_r
        ]],
        shell=True
    )

    return LaunchDescription([
        turtlesim_ns_launch_arg,
        use_provided_red_launch_arg,
        new_background_r_launch_arg,
        turtlesim_node,
        spawn_turtle,
        change_background_r,
        TimerAction(
            period=2.0,
            actions=[change_background_r_conditioned],
        )
    ])

n the example_substitutions.launch.py file, turtlesim_ns, use_provided_red, and new_background_r launch configurations are defined. They are used to store values of launch arguments in the above variables and to pass them to required actions. These LaunchConfiguration substitutions allow us to acquire the value of the launch argument in any part of the launch description.

DeclareLaunchArgument is used to define the launch argument that can be passed from the above launch file or from the console.

turtlesim_ns = LaunchConfiguration('turtlesim_ns')
use_provided_red = LaunchConfiguration('use_provided_red')
new_background_r = LaunchConfiguration('new_background_r')

turtlesim_ns_launch_arg = DeclareLaunchArgument(
    'turtlesim_ns',
    default_value='turtlesim1'
)
use_provided_red_launch_arg = DeclareLaunchArgument(
    'use_provided_red',
    default_value='False'
)
new_background_r_launch_arg = DeclareLaunchArgument(
    'new_background_r',
    default_value='200'
)

The turtlesim_node node with the namespace set to turtlesim_ns LaunchConfiguration substitution is defined.

turtlesim_node = Node(
    package='turtlesim',
    namespace=turtlesim_ns,
    executable='turtlesim_node',
    name='sim'
)

Afterwards, the ExecuteProcess action called spawn_turtle is defined with the corresponding cmd argument. This command makes a call to the spawn service of the turtlesim node.

Additionally, the LaunchConfiguration substitution is used to get the value of the turtlesim_ns launch argument to construct a command string.

spawn_turtle = ExecuteProcess(
    cmd=[[
        'ros2 service call ',
        turtlesim_ns,
        '/spawn ',
        'turtlesim/srv/Spawn ',
        '"{x: 2, y: 2, theta: 0.2}"'
    ]],
    shell=True
)

The same approach is used for the change_background_r and change_background_r_conditioned actions that change the turtlesim background’s red color parameter. The difference is that the change_background_r_conditioned action is only executed if the provided new_background_r argument equals 200 and the use_provided_red launch argument is set to True. The evaluation inside the IfCondition is done using the PythonExpression substitution.

change_background_r = ExecuteProcess(
    cmd=[[
        'ros2 param set ',
        turtlesim_ns,
        '/sim background_r ',
        '120'
    ]],
    shell=True
)
change_background_r_conditioned = ExecuteProcess(
    condition=IfCondition(
        PythonExpression([
            new_background_r,
            ' == 200',
            ' and ',
            use_provided_red
        ])
    ),
    cmd=[[
        'ros2 param set ',
        turtlesim_ns,
        '/sim background_r ',
        new_background_r
    ]],
    shell=True
)

Build the package

colcon build

Launching example

Now you can launch the example_main.launch.py file using the ros2 launch command.

ros2 launch launch_tutorial example_main.launch.py

This will do the following:

  1. Start a turtlesim node with a blue background

  2. Spawn the second turtle

  3. Change the color to purple

  4. Change the color to pink after two seconds if the provided background_r argument is 200 and use_provided_red argument is True

Modifying launch argument

If you want to change the provided launch arguments, you can either update them in launch_arguments dictionary in the example_main.launch.py or launch the example_substitutions.launch.py with preferred arguments. To see arguments that may be given to the launch file, run the following command:

ros2 launch launch_tutorial example_substitutions.launch.py --show-args

This will show the arguments that may be given to the launch file and their default values.

Arguments (pass arguments as '<name>:=<value>'):

    'turtlesim_ns':
        no description given
        (default: 'turtlesim1')

    'use_provided_red':
        no description given
        (default: 'False')

    'new_background_r':
        no description given
        (default: '200')

Now you can pass the desired arguments to the launch file as follows:

ros2 launch launch_tutorial example_substitutions.launch.py turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200

Last updated