Launch: Automate Initial Executions

1. Motivation

Until so far, we have relied on terminals and CLIs to perform the below operations:

As you might guess, it is not feasible to perform the initial operations every time by opening terminals and memorizing all the CLIs. Instead, we programmatically describe the initial executions in launch (opens in a new tab) files based on Python language. On this page, we learn how to automate all the above operations by running a single launch file.

Scenario

In this chapter, we run two turtlesim_node executables in different namespaces: turtle_original and turtle_mimic, along with a new executable called mimic_controller. As the names suggest, the turtle in turtle_mimic will mimic the turtle in turtle_original. This can be done as mimic_controller subscribes pose of the original and controls the mimic turtle.

2. Running Node with Configurations

Running Node Under Namespace

In the directory you want, create a python file called turtle.launch.py, and start to write the description of actions (in the context of launch, actions refer to operations). First of all, we describe a node to be run:

/<path/to/parameter>/turtle.launch.py
 
from launch import LaunchDescription
from launch_ros.actions import Node
 
 
def generate_launch_description():
    ld = LaunchDescription() # Initialize the list of actions
    turtle_original = Node(package='turtlesim',
                        executable='turtlesim_node',
                        namespace='turtle_original')
    ld.add_action(turtle_original) # run turtlesim_node under namespace
    return ld

Try launching the file:

Launch launch file
ros2 launch turtle.launch.py

This will run a node turtle_original/turtlesim under the namespace turtle_original as shown in the below:

rosmarry@rosmarry:~$ ros2 node list
/turtle_original/turtlesim

Loading Parameters

Now, we want to set the background color of the turtlesim node. Previously, we created a yaml file and loaded it. Same here, create a parameter.yaml in a directory /<path/to/parameter>/ and change the top namespace:

/<path/to/parameter>/parameter.yaml
turtle_original: # Note the namespace
  turtlesim:
    ros__parameters: # parameters
      background_r: 200
      background_g: 200

Then, we will add parameter argument into Node class. Note that you should use your directory in /<path/to/parameter>/parameter.yaml.

/<path/to/parameter>/parameter.yaml
...
def generate_launch_description():
    ld = LaunchDescription()
    turtle_original = Node(package='turtlesim',
                           executable='turtlesim_node',
                           namespace='turtle_original',
                           parameters=['/<path/to/parameter>/parameter.yaml'])
 
    ld.add_action(turtle_original)
    return ld
 

3. Running Multiple Nodes

Add another action & Topic Remapping

In the above code, we only added a single node ld.add_action(turtle_original). Now, we add another node in the launch description:

/<path/to/parameter>/turtle.launch.py
...
def generate_launch_description():
    ld = LaunchDescription()
    turtle_original = Node(package='turtlesim',
                           executable='turtlesim_node',
                           namespace='turtle_original',
                           parameters=['/<path/to/parameter>/parameter.yaml'])
 
    turtle_mimic = Node(package='turtlesim',
                    executable='turtlesim_node',
                    remappings=[("turtle1/cmd_vel", "cmd_vel_mimic")])
 
    ld.add_action(turtle_original)
    ld.add_action(turtle_mimic)
 
    return ld
 

For demonstration, we have a twist: topic remapping from /turtlesim/turtle1/cmd_vel into /turtlesim/cmd_vel_mimic. In this way, we can easily remap when we run a node by the launch file.

Launching the file will open two turtlesims.

Launch launch file
ros2 launch turtle.launch.py

will lead to the below:

rosmarry@rosmarry:~/ros2_ws/src$ ros2 node list
/turtle_original/turtlesim
/turtlesim

Receiving Arguments for Launch

As other programs do, we can pass arguments to the launch.

⚠️

We differentiate argument and ROS parameter. The former is not managed by ROS and takes effect only when starting launch. Parameter is managed under ROS and we can perform the operations mentioned in the previous chapter.

The argument from users can substitute the values in the python file:

/<path/to/parameter>/turtle.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
 
ARGUMENTS = [
    DeclareLaunchArgument('mimic_namespace', default_value='turtle_mimic',
                          description='Namespace of mimic turtle')]
 
 
def generate_launch_description():
    ld = LaunchDescription(ARGUMENTS)
 
    mimic_namespace = LaunchConfiguration("mimic_namespace")
 
    turtle_original = Node(package='turtlesim',
                           executable='turtlesim_node',
                           namespace='turtle_original',
                           parameters=['/<path/to/parameter>/parameter.yaml'])
 
    turtle_mimic = Node(package='turtlesim',
                        executable='turtlesim_node',
                        namespace=mimic_namespace,
                        remappings=[("turtle1/cmd_vel", "cmd_vel_mimic")])
 
    ld.add_action(turtle_original)
    ld.add_action(turtle_mimic)
 
    return ld
 
 

In the above launch file, we declare launch arguments in L6. By doing this, the users can look up which arguments they can provide by:

rosmarry@rosmarry:~$ ros2 launch turtle.launch.py --show-args
Arguments (pass arguments as '<name>:=<value>'):
 
    'mimic_namespace':
        Namespace of mimic turtle
        (default: 'turtle_mimic')

As the output suggests, users will put arguments by

ros2 launch turtle.launch.py mimic_namespace:="my_mimic"

In the launch file, mimic_namespace will substitute the namespace of turtle_mimic with my_mimic, as can be seen below after launch the file:

rosmarry@rosmarry:~$ ros2 node list
/my_mimic/turtlesim
/turtle_original/turtlesim

4. Executing Process

Using launch file, we can also run any terminal command. As an example, we will try to publish a topic when we run the launch file. Add another action as the below:

/<path/to/parameter>/turtle.launch.py
...
from launch.actions import DeclareLaunchArgument, ExecuteProcess
...
def generate_launch_description():
  ...
  velocity_publish = ExecuteProcess(
      cmd=[
          'ros2 topic pub /turtle_original/turtle1/cmd_vel -r 10 geometry_msgs/msg/Twist',
          '"{linear: {x: 1.0, y: 0, z: 0}, angular: {x: 0, y: 0, z: 1}}" --print 0'],
      shell=True
  )
  ...
 
  ld.add_action(turtle_original)
  ld.add_action(turtle_mimic)
  ld.add_action(velocity_publish)
 
  return ld
 

Launching this file will make the turtle in turtle_original move along a circle.

Final Launch File

Until here, our launch description opened two turtlesim nodes and published a topic to move the original turtle. Now, we introduce another node to move the other turtle to mimic the original.

/<path/to/parameter>/turtle.launch.py
 
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument, ExecuteProcess
from launch.substitutions import LaunchConfiguration
 
ARGUMENTS = [
    DeclareLaunchArgument('mimic_namespace', default_value='turtle_mimic',
                          description='Namespace of mimic turtle')]
 
 
def generate_launch_description():
    ld = LaunchDescription(ARGUMENTS)
 
    mimic_namespace = LaunchConfiguration("mimic_namespace")
 
    turtle_original = Node(package='turtlesim',
                           executable='turtlesim_node',
                           namespace='turtle_original',
                           parameters=['/home/rosmarry/ros2_ws/src/bcr_bot/launch/parameter.yaml'])
 
    turtle_mimic = Node(package='turtlesim',
                        executable='turtlesim_node',
                        namespace=mimic_namespace,
                        remappings=[("turtle1/cmd_vel", "cmd_vel_mimic")])
 
    velocity_publish = ExecuteProcess(
        cmd=[
            'ros2 topic pub /turtle_original/turtle1/cmd_vel -r 10 geometry_msgs/msg/Twist',
            '"{linear: {x: 1.0, y: 0, z: 0}, angular: {x: 0, y: 0, z: 1}}" --print 0'],
        shell=True
    )
 
    mimic_controller = Node(package='turtlesim',
                            executable='mimic',
                            name='mimic',
                            remappings=[
                                ('/input/pose', '/turtle_original/turtle1/pose'),
                                ('/output/cmd_vel',
                                 [mimic_namespace, '/cmd_vel_mimic']),
                            ])
 
    ld.add_action(turtle_original)
    ld.add_action(turtle_mimic)
    ld.add_action(velocity_publish)
    ld.add_action(mimic_controller)
 
    return ld
 

Adding the action mimic_controller will move the turtle in the mimic namespace so that it can follow the pose of the original turtle. In the below figure, the right window is the original, and the left is the mimic turtle.

5. Summary

On this page, we learned how to use the launch file to automate various actions. In most cases, running launch files will be the first command that a user will try for a large enough project. There are lots of lines not covered in this chapter. For further investigation, visit the below links: