ROS2 Python Launch File Example – How to Start All Your Nodes at Once

In this tutorial I’ll show you a ROS2 Python launch file example and we’ll build it step by step.

When you start to have a few nodes in your ROS2 application, it quickly becomes not-so-practical to start each node – with its own configuration – manually in a different terminal. With a launch file you can write all the nodes with a complete configuration (remapping, parameters, etc.) in a single file, that you can launch with only one command line.

First we’ll start with a simple launch file to start 2 nodes. You’ll see the entire process to create, write, install, and start the launch file. And after that I’ll show you how you can add more levels of customization to your nodes.

>> Watch this video as an additional resource to this article:

After watching the video, subscribe to the Robotics Back-End Youtube channel so you don’t miss the next tutorials!


You want to learn ROS2 efficiently?

Check out ROS2 For Beginners and learn ROS2 step by step, in 1 week.


Where to create your launch files?

Well, technically you could create a launch file anywhere, in any package you want. However we’ll stick to a few rules here.

If you want to create a launch file in an existing package (whether a Python package or Cpp package), then create a launch/ folder at the root of this package. Put all launch files in this new folder.

This first technique makes sense when you release a package along with a launch file specific to that package, for example. But, if you want to create launch files for your entire application (with multiple packages), then a best practice is to create a new ROS2 package dedicated to launching your app, and which will contain launch files, config files, etc.

That’s what we’ll do here. Go to your ROS2 workspace and create a new package named “my_robot_bringup”, with no build type (default will be ament_cmake) and no dependency. Of course replace the “my_robot” part with the name of your actual robot/hardware component. If you look as some existing ROS2 stacks from other robots (on GitHub for example), you’ll see that doing this has become quite a convention in the ROS community.

In this new package, remove the src/ and include/ folders. Create a new launch/ folder, and create your first launch file inside. Let’s name it “demo.launch.py”.

cd ~/ros2_ws/src
ros2 pkg create my_robot_bringup
cd my_robot_bringup/
rm -rf include/
rm -rf src/
mkdir launch
touch launch/demo.launch.py

Write your first ROS2 Python launch file

As you can see the launch file we created (demo.launch.py) is a Python file. You can use XML (or even YAML) instead if you want to.

Here we’ll simply start the talker/listener example from the official ROS2 demos. The talker will publish on the /chatter topic, and the listener will subscribe to that topic.

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    ld = LaunchDescription()

    talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
    )

    listener_node = Node(
        package="demo_nodes_py",
        executable="listener"
    )

    ld.add_action(talker_node)
    ld.add_action(listener_node)

    return ld

Let’s break this down line by line.

from launch import LaunchDescription
from launch_ros.actions import Node

First you import what you need for this launch file, from the “launch” and “launch_ros” modules.

def generate_launch_description():
    ld = LaunchDescription()

Your launch file must contain this function: generate_launch_description(), and must return a LaunchDescription object.

No main function here. You won’t run this file directly, you’ll first install it. And when you install the launch file, colcon will expect to find generate_launch_description().

    talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
    )
    listener_node = Node(
        package="demo_nodes_py",
        executable="listener"
    )

Inside the function you can now create your nodes. This is the bare minimum you have to write for a node: you specify the package name and executable name. We’ll see later on this tutorial how you can add more customization to your nodes.

As you can see here, we’re using nodes from 2 different packages.

    ld.add_action(talker_node)
    ld.add_action(listener_node)

Once you’ve create launch_ros.actions.Node object for each node you want to start, don’t forget to add them to the LaunchDescription with the add_action() function. Or else they won’t be started.

    return ld

Finally, and this will be the last line of your launch file, return the LaunchDescription object, which now contains all nodes to launch.

Install the ROS2 Python launch file

Add dependencies

In this example, we are starting nodes from demo_nodes_cpp and demo_nodes_py packages. As those are different packages than the one we’re using for the launch file, we need to add some dependencies.

Nothing complicated here, just go to the package.xml of the package where you’ve written your launch file, and add an “exec_depend” tag for each dependency.

...
  <exec_depend>demo_nodes_cpp</exec_depend>
  <exec_depend>demo_nodes_py</exec_depend>
...

If, later on, you add new nodes from other packages in your launch file, don’t forget to add the dependencies as well.

Install from a Cpp package

So, if you create your launch files into an existing Cpp package, or into a dedicated package with default build type (here my_robot_bringup), the way to install launch files will be the same.

Go into the CMakeLists.txt of your package, and after find_package(ament_cmake REQUIRED), add:

...

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

...

This will install all launch files from under the launch/ folder of your package. Once it’s done once, you don’t need to do anything else for other launch files you add.

Now, go back into your ROS2 workspace and build the package with colcon build.

Install from a Python package

In the case where you’ve created your launch file inside a Python package, well, things are a little bit different. You don’t have a CMakeLists.txt file, instead you have setup.py.

Open setup.py and add this:

from setuptools import setup
import os
from glob import glob

...

    data_files=[
        ...
        (os.path.join('share', package_name), glob('launch/*.launch.py'))
    ],

...

This will do the same thing as for the previous instruction with a Cpp package: install the launch files from the launch/ folder. Here we’ve added one more condition: only files ending with “.launch.py” will be installed.

Save, and go back to your ROS2 workspace to build with colcon build.

Run the ROS2 Python launch file

Now that you have written and installed your launch file, it’s now ready to be launched!

And to do that, you’ll use the ros2 launch command line tool. This command will take 2 arguments: name of the package + name of the launch file.

First, go into another terminal and source your ROS2 workspace. Then:

$ ros2 launch ros2_tutorials_py demo.launch.py 
[INFO] [launch]: All log files can be found below /home/ed/.ros/log/2020-07-03-08-51-07-071225-ed-vm-10382
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started with pid [10384]
[INFO] [listener-2]: process started with pid [10386]
[talker-1] [INFO] [1593759068.181805900] [talker]: Publishing: 'Hello World: 1'
[listener-2] [INFO] [1593759068.198144751] [listener]: I heard: [Hello World: 1]
[talker-1] [INFO] [1593759069.181186103] [talker]: Publishing: 'Hello World: 2'
[listener-2] [INFO] [1593759069.185819615] [listener]: I heard: [Hello World: 2]

Each executable will get a unique name (“talker-1” for node “talker”, “listener-2” for node “listener”), and you can also see their pid.

Then, all the logs from all nodes will be printed in this terminal. If you have many nodes this may result in a total log mess, so you may have to optimize which log to print or not to make your app easier to debug.

And, well, that’s it: all the nodes you’ve added in your launch file are running, until you press CTRL+C.

Now, let’s see how we can customize those nodes inside the launch file.

Customize your nodes in ROS2 Python launch files

There are many things you can do, here I’ll focus on some of the most common ones.

Rename node

You may want to rename your node, for example if you want to start 2 different nodes from the same executable.

...
    talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
        name="my_talker"
    )
...

Topic/Service remapping

To remap a topic/service inside a node, add a remappings[] array inside the Node object, and add a tuple: first value is the current name, second value is the new name.

...
    talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
        name="my_talker",
        remappings=[
            ("chatter", "my_chatter")
        ]
    )

    listener_node = Node(
        package="demo_nodes_py",
        executable="listener",
        remappings=[
            ("chatter", "my_chatter")
        ]
    )
...

In this case, as we want the talker and listener nodes to be able to communicate, of course we have to rename the topic on both sides.

To add more remappings, simply add other tuples inside the remappings[] array.

Parameters

Here we’ll launch the turtlesim node from the Turtlesim package (if you don’t have it: sudo apt install ros-<distro>-turtlesim)

What we want is simply to start the turtlesim node, but with a different background color (light grey instead of blue). So we’ll need to set some parameters values.

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    ld = LaunchDescription()

    turtlesim_node = Node(
        package="turtlesim",
        executable="turtlesim_node",
        parameters=[
            {"background_b": 200},
            {"background_g": 200},
            {"background_r": 200}
        ]
    )

    ld.add_action(turtlesim_node)

    return ld

To add parameters, create a parameters[] array inside the node. For each parameter you want to set, add a new dictionary: key is the parameter’s name, value is the parameter’s value.

Note: with this launch file you’ll have to add a new line to your package.xml: <exec_depend>turtlesim</exec_depend>.

To set parameters in a launch file you can either set them directly like we did here, or load them from a YAML config file, which may be more scalable when you start to have many parameters.

Conclusion

In this tutorial you’ve seen how to create, write, install, and start a ROS2 Python launch file. Each node you start from a launch file can be fully customized.

Launch files will make your application much more scalable. You will be able to write each node as a module, and then combine everything together, with any configuration you want, inside only one file.

Want to learn how to program with ROS2?

Don't miss this opportunity:

ROS2 For Beginners - Step by Step Course


[NEW] ROS 2 Book


Or, learn with a video course