Tutorial 4: The launch utility

Objective: This tutorial session is devoted to learn the ROS mechanism for starting the master and many nodes all at once, using a file called a launch file (an XML document).

Contents

After this session you will be able to:

  • Understand the structure of a launch file, write and use basic launch files.

  • Understand the graph resource naming convention and use namespaces and remapping in launch files.

  • Understand the ROS parameter server and use and set parameters from the launch files.

Provided packages:

This tutorial will use the following packages:

Package agitr_chapter6
Package agitr_chapter3
Package demoparameters

Prepare the catkin workspace to work with them:

  1. If you have not yet done so, follow the steps in Exercise 0 to:

  • clone in your ~/git-projects folder the meta-repository containing all the required packages,

  • create the catkin workspace folders (use catkin_ws4 for the current tutorial).

  1. Follow the procedure shown in Procedure to prepare the exercises to import in the intro2ros/2023/teamXX/EXERCISE2 subgroup of your GitLab account the packages:

  1. In your git-projects folder clone the agitr_chapter6, agitr_chapter3 and solution_exercise2 projects of your GitLab account inside an exercise2 subfolder (change XX by your team number). You can use https:

$ git clone https://gitlab.com/intro2ros/2023/teamXX/exercise2/agitr_chapter6.git exercise2/agitr_chapter6
$ git clone https://gitlab.com/intro2ros/2023/teamXX/exercise2/agitr_chapter3.git exercise2/agitr_chapter3
$ git clone https://gitlab.com/intro2ros/2023/teamXX/exercise2/solution_exercise2.git exercise2/solution_exercise2

or ssh:

$ git clone git@gitlab.com:intro2ros/2023/teamXX/exercise2/agitr_chapter6.git exercise2/agitr_chapter6
$ git clone git@gitlab.com:intro2ros/2023/teamXX/exercise2/agitr_chapter3.git exercise2/agitr_chapter3
$ git clone git@gitlab.com:intro2ros/2023/teamXX/exercise2/solution_exercise2.git exercise2/solution_exercise2
  1. In your catkin_ws4/src folder create a symbolic link to the packages to be used:

$ ln -s ~/git-projects/exercise2/agitr_chapter6
$ ln -s ~/git-projects/exercise2/agitr_chapter3
$ ln -s ~/git-projects/all_introduction_to_ros_packages/demoparameters
  1. Edit the build.sh file of the solution_exercise2 package to add your credencials (token_user and token_code) and push the change.

References:

This tutorial is partially based on chapter 6 of A gentle introduction ROS by Jason M. O’Kane.

Complementary resources:

Using launch files

What follows is an example of a launch file that starts: a turtlesim simulator, along with the teleoperation node:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<launch>
 <node
   pkg="turtlesim"
   type="turtlesim_node"
   name="turtlesim"
   respawn="true"
 />
 <node
   pkg="turtlesim"
   type="turtle_teleop_key"
   name="teleop_key"
   required="true"
   launch-prefix="xterm -e"
 />
</launch>

Execute the launch file as follows:

$ roslaunch agitr_chapter6 teleopturtlesim.launch

Basic features:

  • Before starting any nodes, roslaunch will determine whether roscore is already running and, if not, start it automatically.

  • All of the nodes in a launch file are started at roughly the same time. As a result, you cannot be sure about the order in which the nodes will initialize themselves. Well-written ROS nodes don’t care about the order in which they and their siblings start up.

  • By default the standard output from launched nodes is not the terminal but a log file (∼/.ros/log/run_id/node_name-number-stdout.log). You can check your run_id folder and browse the rosout.log file as we did here.

  • To terminate an active roslaunch, use Ctrl-C . This signal will attempt to gracefully shut down each active node from the launch.

Understanding launch files

Naming and placing conventions

  • Each launch file should be associated with a particular package.

  • The usual naming scheme is to give launch files names ending with .launch.

  • They are stored either directly in the package directory, or in a subdirectory usually called launch.

File structure

1. The root element: <launch> … </launch>

Like every XML document, launch files have exactly one root element. It is called launch. All of the other elements of each launch file should be enclosed between these tags.

2. The node element: <node> … </node>

The launch files contain a collection of node elements corresponding to the nodes to be launched.

The attributes of this element are:

  • pkg (required): The name of the package the node belongs to.

  • type (required): The name of the executable file that starts the node.

  • name (required): The name of the node. This overrides any name that the node would normally assign to itself in its call to ros::init. It should be a relative name (without mention of any namespaces), not a global name. See Section Graph resource names.

  • respawn (optional): If set to “true” roslaunch restarts the node when it terminates.

  • required (optional): If a node is labeled as required then when it is terminated roslaunch terminates all other nodes.

  • launch-prefix (optional): Used to insert the given prefix at the start of the command line that runs the node. The prefix “xterm-e” starts a new terminal window.

  • output (optional): If set to “screen” (for only a single node) allows the standard output of the node be the terminal and not a log file.

  • ns (optional): To set a namespace. See Section Launching nodes inside a namespace.

Exercise

Close the turtlesim window and check that a new one is created, because the turtlesim_node has the respawn attribute to true.

Exercise

Terminate the teleop_key node and check that all other nodes also terminate, because the teleop_key has the required attribute to true.

3. The include element: <include> … </include>

This element allows you to include the contents of another launch file, including all of its nodes and parameters.

<include file=”path-to-launch-file” />

A find substitution to search for a package is usually used in order not to explicitly specify the path:

<include file=”$(find package-name)/launch-file-name” />

As an example, consider the following launch file to launch the teleoperation of the turtlesim plus the monitoring of the turtle pose (using the subscriber node implemented in the publisher-subscriber_tutorial - package agitr_chapter3-):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<launch>
 <include
   file="$(find agitr_chapter6)/launch/teleopturtlesim.launch"
 />
 <node
   pkg="agitr_chapter3"
   type="subpose"
   name="pose_subscriber"
   output="screen"
 />
</launch>

Execute the launch file:

$ roslaunch agitr_chapter6 teleopturtlesim2.launch

4. The arg element: <arg> … </arg>

This element is used to help make launch files configurable. It is defined as:

<arg name=”arg-name” />

And used with an arg substitution:

$(arg arg-name)

The value of the arguments:

  • Is set at the command line:

    roslaunch package-name launch-file-name arg-name:=arg-value

  • Is defaulted with a value that can be overriden at the command line:

    <arg name=”arg-name” default=”arg-value”/>

  • Is defaulted with a value that cannot be overriden at the command line:

    <arg name=”arg-name” value=”arg-value”/>

As an example take a look at the launch file teleopturtlesim3.launch. It introduces an argument to run or not the node that monitors the pose of the turtle, with a default value of 0.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<launch>
 <arg
   name="monitorpose"
   default="0"
 />
 <include
   file="$(find agitr_chapter6)/launch/teleopturtlesim.launch"
 />
 <node if="$(arg monitorpose)"
   pkg="agitr_chapter3"
   type="subpose"
   name="pose_subscriber"
   output="screen"
 />
</launch>

Execute it using different values of the argument monitorpose:

$ roslaunch agitr_chapter6 teleopturtlesim3.launch
$ roslaunch agitr_chapter6 teleopturtlesim3.launch monitorpose:=1
$ roslaunch agitr_chapter6 teleopturtlesim3.launch monitorpose:=0

EXERCISE 2

To solve Exercise 2 you must modify the agitr_chapter6 package that you have cloned in your git-projects folder.

Recall to keep commiting the changes when coding the solution (do small commits with good commit messages!) and write a README.md file in the solution_exercise2 package explaining your solution (Markdown Cheat Sheet).

Exercise 2a

Modify the teleopturtlesim.launch file to substitute the teleop_key node by the pubvel node of package agitr_chapter3 that commands random velocities to the /turtle1/cmd_vel topic (call it randvelturtlesim.launch).

Exercise 2b

Modify the teleopturtlesim2.launch file in order to use the randvelturtlesim.launch file created before, instead of teleopturtlesim.launch (call it randvelturtlesim2.launch).

Exercise 2c

Create a launch file to move the turtle in the turtlesim_node by either commanding the velocities through the keyword using the teleop_key node, or by using the random velocities generated by the pubvel node of package agitr_chapter3. Call it teleopturtlesim4.launch.

  • Use an argument called teleop such that if set to one the teleoperation is activated. Use a default value of 1.

  • Hint: use unless=$(arg teleop) or if=”$(eval teleop==0)” to activate the pubvel node.

SOLUTION EXERCISE 2

Download the zipped solution.

Graph resource names

ROS has a flexible naming system that accepts several different kinds of names.

  • Nodes, topics, services, and parameters are collectively referred to as graph resources.

  • Every graph resource is identified by a short string called a graph resource name.

Global names

Global names have a global scope, i.e. they make sense anywhere they’re used with clear, unambiguous meanings. No additional context information is needed to decide which resource the name refers to.

Some global names seen so far:

  • /teleop_turtle

  • /turtlesim

  • /turtle1/cmd_vel

  • /turtle1/pose

There are several parts to a global name:

  • A leading slash /, which identifies the name as a global name.

  • A sequence of zero or more namespaces, separated by slashes. Namespaces are used to group related graph resources together, such as turtle1 in the above examples.

  • A base name that describes the resource itself, such as cmd_vel.

Relative names

Relative names allow ROS to supply a default namespace.

The characteristic feature of a relative name is that it lacks a leading slash (/), like:

  • teleop_turtle

  • turtlesim

  • cmd_vel

  • turtle1/pose

Relative names cannot be matched to specific graph resources unless we know the default namespace that ROS is using to resolve them.

To resolve a relative name to a global name, ROS attaches the name of the current default namespace to the front of the relative name, e.g.:

  • Default namespace = /turtle1

  • Relative name = cmd_vel

  • Global name = /turtle1/cmd_vel

The default namespace has to be set for each node (defaulted to the global namespace (/)). This is usually done with the ns attribute in the launch files.

When a node uses relative names, it is essentially giving its users the ability to easily push that node and the topics it uses down into a namespace that the node’s original designers did not necessarily anticipate. This kind of flexibility can make the organization of a system more clear and, more importantly, can prevent name collisions when groups of nodes from different sources are combined.

Private names

Private names use the name of their node as a namespace. They begin with a tilde (∼) character, e.g.:

  • Node global name = /sim1/pubvel

  • Relative name = ∼max_vel

  • Global name = /sim1/pubvel/max_vel

Private names are often used for parameters and services that govern the operation of a node (for things that are related only to that node, and are not interesting to anyone else).

Graph resources referred to by private names remain accessible, via their global names, to any node that knows their name.

Anonymous names

Anonymous names are specifically used to name nodes.

To request an anonymous name, a node should pass ros::init_options::AnonymousName as a fourth parameter to ros::init:

ros::init(argc, argv, base_name, ros::init_options::AnonymousName)

They are used because by requesting an anonymous name, we are free to run as many simultaneous copies of a program as we like, knowing that each will be assigned a unique name when it starts.

Managing names in launch files

Launching nodes inside a namespace

By setting the ns attribute to a node element of the launch file, we can specify the namespace.

As an example the teleopturtlesim_double.launch file runs two instances of the turtlesim_node, each one with its own namespace, thus avoiding collisions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<launch>
 <node
   pkg="turtlesim"
   type="turtlesim_node"
   name="turtlesim"
   respawn="true"
   ns="sim1"
 />
 <node
   pkg="turtlesim"
   type="turtle_teleop_key"
   name="teleop_key"
   required="true"
   launch-prefix="xterm -e"
   ns="sim1"
 />
 <node
   pkg="turtlesim"
   type="turtlesim_node"
   name="turtlesim"
   respawn="true"
   ns="sim2"
 />
 <node
   pkg="turtlesim"
   type="turtle_teleop_key"
   name="teleop_key"
   required="true"
   launch-prefix="xterm -e"
   ns="sim2"
 />
</launch>

In this way, the turtlesim topic names (which in the code for turtlesim_node are defined as relative names, like turtle1/pose) become global names by using the namespaces, i.e.:

  • The turtle1/cmd_vel relative topic name is resolved to the global names /sim1/turtle1/cmd_vel and /sim2/turtle1/cmd_vel.

  • The turtle1/color_sensor is resolved to /sim1/turtle1/color_sensor and /sim2/turtle1/color_sensor.

  • The turtle1/pose is resolved to /sim1/turtle1/pose and /sim2/turtle1/pose.

Likewise, the node names in the launch file are relative names. Then:

  • turtlesim is resolved to /sim1/turtlesim and /sim2/turtlesim.

  • teleop_key is resolved to /sim1/teleop_key and /sim2/teleop_key.

Note that the namespaces specified by the ns attributes are themselves relative names! Here the default namespace is the global namespace and therefore the default namespaces have been resolved to /sim1 and /sim2.

Execute the launch file as follows:

$ roslaunch agitr_chapter6 teleopturtlesim_double.launch

In this example we pushed each simulator node into its own namespace. The resulting changes to the topic names make the two simulators truly independent, enabling us to publish different velocity commands to each one.

Exercise

  • Verify the names of the topics and the names of the nodes.

  • Visualize the ROS graph wiht rqt_graph.

Remapping

Remappings provides a finer level of control for modifying the names used by our nodes.

Each remapping provides an original name and a new name. Each time a node uses any of its remappings’ original names, the ROS client library silently replaces it with the new name from that remapping.

Remapping can be done:

  1. By using original-name:=new-name somewhere in the command line:

    rosrun turtlesim turtlesim_node turtle1/pose:=tim

This line launches the turtlesim_node that publishes on the tim topic instead of publishing to the turtle1/pose.

  1. By using the remap element in the launch file, e.g.:

    <node pkg=”turtlesim” type=”turtlesim_node” name=”turtlesim” >
    <remap from=”turtle1/pose” to=”tim” />
    </node>

Note that all names, including the original and new names in the remapping itself, are resolved to global names, before ROS applies any remappings.

Consider as an example the case where we want to teleoperate the turtle using the keys but reversing the directions. The reverse_cmd_vel.cpp file shown below subscribes to turtle1/cmd_vel topic and modifies it to publish a topic named turtle1/cmd_vel_reversed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 // This program subscribes to turtle1/cmd_vel and
 // republishes on turtle1/cmd_vel_reversed,
 // with the signs inverted.
 #include <ros/ros.h>
 #include <geometry_msgs/Twist.h>

 ros::Publisher *pubPtr;

 void commandVelocityReceived(
  const geometry_msgs::Twist& msgIn
 ) {
  geometry_msgs::Twist msgOut;
  msgOut.linear.x = -msgIn.linear.x;
  msgOut.angular.z = -msgIn.angular.z;
  pubPtr->publish(msgOut);
 }

 int main(int argc, char **argv) {
  ros::init(argc, argv, "reverse_velocity");
  ros::NodeHandle nh;

  pubPtr = new ros::Publisher(
    nh.advertise<geometry_msgs::Twist>(
      "turtle1/cmd_vel_reversed",
      1000));

  ros::Subscriber sub = nh.subscribe(
    "turtle1/cmd_vel", 1000,
    &commandVelocityReceived);

  ros::spin();

  delete pubPtr;
}

In order to make the turtlesim_node respond to this new topic instead to the usual turtle1/cmd_vel, a remapping is done in the launch file as shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<launch>
  <node
    pkg="turtlesim"
    type="turtlesim_node"
    name="turtlesim"
    respawn="true"
  >
    <remap
      from="turtle1/cmd_vel"
      to="turtle1/cmd_vel_reversed"
    />
  </node>
  <node
    pkg="turtlesim"
    type="turtle_teleop_key"
    name="teleop_key"
    required="true"
    launch-prefix="xterm -e"
  />
  <node
    pkg="agitr_chapter6"
    type="reverse_cmd_vel"
    name="reverse_velocity"
  />
</launch>

Execute the launch file to verify the new behavior:

$ roslaunch agitr_chapter6 teleopturtlesim_reverse.launch

Exercise

Use rqt_graph to visualize the turtlesim_node being subscribed to the topic advertised by the reverse_velocity node.

ROS parameters

This section is based on chapter 7 of A gentle introduction ROS by Jason M. O’Kane.

ROS has a centralized parameter server that keeps track of a collection of values, to be queried by the nodes, that basically store configuration information that does not change (much) over time.

Accessing and setting parameters from the command line

There are three command lines to deal with the parameters:

  • To list all existing parameters:

$ rosparam list
  • To query the value of the parameter or all the values within a namespace:

$ rosparam get parameter_name
$ rosparam get namespace
  • To set the value of a parameter:

$ rosparam set parameter_name parameter_value

It is important to note that the changed values are not “pushed” to nodes, i.e. nodes that care about changes on some parameters, must explicitly query the parameter server for these values.

  • To store all the parameters of a namespace into a file:

$ rosparam dump filename namespace
  • To load all the parameters from a file:

$ rosparam load filename namespace

In the last two commands, if the namespace is the global namespace (/), the argument is optional.

Accessing and setting parameters from C++

The C++ interface to ROS parameters is straightforward:

  • void ros::param::set(parameter_name, input_value);

  • bool ros::param::get(parameter_name, output_value);

The following example, from the demoparameters package, illustrates the getting of a parameter. It is a variant of the agitr_chapter3/pubvel program where the publishing rate has been set as a ROS parameter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// This program publishes randomly-generated velocity
// messages for turtlesim.
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>  // For geometry_msgs::Twist
#include <stdlib.h> // For rand() and RAND_MAX

int main(int argc, char **argv) {
 // Initialize the ROS system and become a node.
 ros::init(argc, argv, "publish_velocity");
 ros::NodeHandle nh;
 double frequency;

 // Create a publisher object.
 ros::Publisher pub = nh.advertise<geometry_msgs::Twist>(
   "turtle1/cmd_vel", 1000);

 // Seed the random number generator.
 srand(time(0));

 // Loop at a frequency set by the parameter *publish_cmd_vel_rate*, until the node is shut down.
 bool ok = ros::param::get("publish_cmd_vel_rate", frequency);
 if(!ok)
 {
     ROS_FATAL_STREAM("Could not get parameter publish_cmd_vel_rate. Please set it from the command line");
     exit(1);
 }

 ros::Rate rate(frequency);
 while(ros::ok()) {
   // Create and fill in the message.  The other four
   // fields, which are ignored by turtlesim, default to 0.
   geometry_msgs::Twist msg;
   msg.linear.x = double(rand())/double(RAND_MAX);
   msg.angular.z = 2*double(rand())/double(RAND_MAX) - 1;

   // Publish the message.
   pub.publish(msg);

   // Send a message to rosout with the details.
   ROS_INFO_STREAM("Sending random velocity command:"
     << " linear=" << msg.linear.x
     << " angular=" << msg.angular.z);

   // Wait until it's time for another iteration.
   rate.sleep();
 }
}

Build and run the node pubvel_param:

$ catkin build demoparameters
$ source devel/setup.bash
$ rosrun demoparameters pubvel_param

An error will occur since the publish_cmd_vel_rate has not yet been set. Do it from the command line, rerun and verify the published frequency:

$ rosparam set publish_cmd_vel_rate 5
$ rosrun demoparameters pubvel_param
$ rostopic hz /turtle1/cmd_vel

Accessing and setting parameters from launch files

Parameters can also be set from launch files using the param element:

<param name=”param-name” value=”param-value” />

As an example try to launch the demoparameters.launch file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<launch>
<node
   pkg="turtlesim"
   type="turtlesim_node"
   name="turtlesim"
   respawn="true"
 />
 <param name="publish_cmd_vel_rate" value="5.0" />
 <node
   pkg="demoparameters"
   type="pubvel_param"
   name="pubvel_param"
   required="true"
 />
</launch>
$ roslaunch demoparameters demoparameters.launch

Check the publishing rate:

$ rostopic hz /turtle1/cmd_vel