************ Launch Files ************ A robot system in ROS or ROS2 consist of several interconnected nodes, which are responsible for different tasks, e.g.: - localization, - locomotion, - path planning, - mapping, - reading out sensor data, - visualization and so on. Instead of invoking every node on its own, use a *launch* file! One launch file can start multiple nodes. It is a perfect tool for managing the processes of a more complex ROS2 application. On top, a launch file can include other launch files, which makes it even easier to structure the complex starting process of a robot system. For better organization launch files should be placed in a folder named *launch* inside the ROS2 package. Create a launch file named *turtle.launch.py*: .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage/ $ mkdir launch $ cd launch $ touch turtle.launch.py Instead, you can also create the launch folder and the launch file using the graphical user interface or inside your IDE. Multiple Nodes in one launch file --------------------------------- Include the nodes **turtlesim_node** and **draw_square** of the package **turtlesim** in the *turtle.launch.py* file. The example given below shows the basic syntax of a launch file. .. literalinclude:: /_resources/code/tutorials/launch/simple_launch_file.launch.py :caption: A simple launch.py file to start two nodes :language: python :lines: 1- For a Node you can define parameters in a launch file, e.g.: - package → name of the package that holds the executable - node_executable → name of the executable - node_name → (free to choose) name for the running process in ROS2 - output → location of the output (screen or log, as desired) Every launch file needs the *generate_launch_description* function and the return value *LaunchDescription* to be able to startup the dedicated Nodes. Before we can start our launch file, it is necessary to once add launch files to our ``setup.py`` file in our package. Open the ``setup.py`` file and add the following two imports at the beginning of the file: .. code-block:: python import os from glob import glob Afterwards add the following code to the *data_files* parameter in line 15: .. literalinclude:: /_resources/code/tutorials/scripts/launch_setup.py :diff: /_resources/code/tutorials/scripts/node_setup.py We only need to do that once. Using this line all future launch files will be included automatically after building the package with *colcon*. So build the package again: .. code-block:: bash $ cd ~/robot_ws $ colcon build Start the launch file: .. code-block:: bash $ ros2 launch myfirstpackage turtle.launch.py # ros2 launch The **ros2 launch** command executes a launch file. Start one Node multiple times ----------------------------- It is also possible to run the same Node multiple times, using *namespaces*. In the following example we add another turtlesim instance to our *turtle.launch.py* file and just add the parameter *namespace* with the value *new_turtle* to run the same *node* just with a different name: .. literalinclude:: /_resources/code/tutorials/launch/simple_launch_file_multi.launch.py :caption: A simple launch.py file to start one node multiple times using namespaces :language: python :lines: 1- If you build the package and start the launch file again, you should see two *turtlesim* instances now. This new one is quite boring, because it does not move yet. → Please add another *draw_circle* Node to the launch file using the same *namespace new_turtle*. → Create another launch file *turtle2.launch.py* and move the 2nd *turtlesim* and the second *draw_circle* **including** the *namespace* parameter to that new *turtle2.launch.py* file. Don't forget to delete them from your initial *turtle.launch.py*. Include a launch file in a launch file -------------------------------------- We should now have organized our two turtles in two seperate *launch* files. If we now want to start both of them, we can create another *launch* file starting both turtles. Create another launch file and name it *multi_turtle.launch.py*. It should include the previous launch files *turtle.launch.py* and *turtle2.launch.py*. Use the given example below. .. literalinclude:: /_resources/code/tutorials/launch/simple_launch_include_launch_file.launch.py :caption: A simple launch.py file to start multiple launch files :language: python :lines: 1- → Start the *multi_turtle.launch.py* launch file. → Include your node **myfirstnode** into the *multi_turtle.launch.py* file, build and restart it and check all active nodes of your system. *For further information:* `https://index.ros.org/doc/ros2/Tutorials/Launch-system/ `__ Parameters ================== A parameter is a configuration value of a node. You can think of parameters as node settings. A node can store parameters as integers, floats, booleans, strings and lists. In ROS 2, each node maintains its own parameters. All parameters are dynamically reconfigurable. Parameters are accessible via: - command line, - \*.yaml file or - launch file. Access Parameters via command line ----------------------------------------------- Start the ROS2 nodes **turtlesim_node** and **turtle_teleop_key** of the package **turtlesim** and list all available parameters: .. code-block:: bash $ ros2 param list You will see the node namespaces, ``/teleop_turtle`` and ``/turtlesim``, followed by the dedicated parameters of the nodes. Every node has the parameter ``use_sim_time``; it’s not unique to turtlesim. To read out the current value of a parameter, e.g. the ``background_g`` parameter of the node **turtlesim**, use: .. code-block:: bash $ ros2 param get /turtlesim background_g # ros2 param get You should receive the output: .. code-block:: bash Integer value is: 86 This is the green part of the RGB color code from the background of the turtlesim window. Now that we know that it is an integer value, we can change it to a different integer value: .. code-block:: bash $ ros2 param set /turtlesim background_g 177 # ros2 param set .. hint:: If you additionally set the red value to 0 and the blue value to 172, you will get a nice FH Aachen mint green background. You can save the current parameters of the Node for a later use: .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage $ mkdir config $ cd config $ ros2 param dump /turtlesim # ros2 param dump This command will save the parameter setup in a \*.yaml file in the current directory where the command is executed. For now in ROS2 we need to slightly modify this file so that it will also work with namespaces, e.g. in launch files. Open the **turtlesim.yaml** now and replace the node name ``turtlesim`` by double asterisk ``/**``: .. literalinclude:: /_resources/code/tutorials/config/turtlesim_asterisk.yaml :diff: /_resources/code/tutorials/config/turtlesim_dump.yaml Access parameters via \*.yaml files ----------------------------------------------- Go to the directory, where you stored your **turtlesim.yaml** file that you have created. Create a copy for further steps: $ cd ~/robot_ws/src/myfirstpackage/config $ cp turtlesim.yaml turtlesim_fh.yaml Open the file now. It should have the following content: .. literalinclude:: /_resources/code/tutorials/config/turtlesim_fh.yaml :language: xml :lines: 1- :caption: Example 4. config file for turtlesim with parameters. We can now start *turtlesim* with this configuration using the ``--ros-args`` and ``--param-file`` flag: .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage/config $ ros2 run turtlesim turtlesim_node --ros-args --params-file ./turtlesim_fh.yaml # ros2 run --ros-args --params-file This method does only work, if we pass the complete path to the \*.yaml file. A more smart way is to use a launch file instead. Access parameters via launch file ----------------------------------------------- We can include paramater files in launch files. This is very useful to start ROS2 Nodes with a predifened setup. This could for example be a camera driver Node with parameters for resolution and frame rate. So it would be possible to create different configurations for resolution and frame rate and start the dedicated launch file - always using the same Node, but with a different configuration. Let's modify our *turtle.launch.py* file for this purpose: .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage/launch In line 12 we add: .. code-block:: python parameters=[os.path.join(my_first_pkg, 'turtlesim_fh.yaml')], Of course we need to define the **my_first_pkg** variable and import the necessary libraries as shown in the following: .. literalinclude:: /_resources/code/tutorials/launch/simple_params_launch_file.launch.py :diff: /_resources/code/tutorials/launch/simple_launch_file.launch.py Before we can start our modified launch file with parameters, it is necessary to once add \*.yaml files from our config folder to our ``setup.py``, like we did for *launch* files. .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage Open the ``setup.py`` file and add the the following code to the *data_files* parameter in line 16: .. literalinclude:: /_resources/code/tutorials/scripts/params_setup.py :diff: /_resources/code/tutorials/scripts/launch_setup.py Finally build the package and start the modified *turtle.launch.py*: .. code-block:: bash $ cd ~/robot_ws $ colcon build --packages-select myfirstpackage # colcon build --packages-select $ ros2 launch myfirstpackage turtle.launch.py .. hint:: You can use the flag ``--packages-select`` with **colcon build** to only build one specific package in the workspace. You should now see the FH Aachen mint green turtlesim background like in the following figure: .. figure:: /_resources/image/turtlesim_fh_mint.png :scale: 100% :name: fig_turtlesim_fh_mint :align: center Turtlesim with FH Aachen mint green background. Now create a copy of the *turtlesim_fh.yaml* file and name it *turtlesim_random.yaml* .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage/config $ cp turtlesim_fh.yaml turtlesim_random.yaml → Open the new file and change the RGB values to any integer number that you like (only values from 0 to 255 are valid for color codes). → Modify the *turtle2.launch.py* in the *launch* folder of your package **myfirstpackage** and include the new *turtlesim_random.yaml* as a paramater for your second turtlesim instance. → Now start the *multi_turtle.launch.py* launch file from **myfirstpackage**. Don't forget to build your package first to install new *\*.yaml* parameter file and the modified *launch* file. *For further information:* `http://design.ros2.org/articles/ros_parameters.html `__ Create your own parameters in a Node ----------------------------------------------- In ROS2 it is quite simple to create parameters in your Node. For that purpose create a copy of **myfirstnode.py**: .. code-block:: bash $ cd ~/robot_ws/src/myfirstpackage/myfirstpackage $ cp myfirstnode.py param_node.py Don't forget to add an entry point for this node in the **setup.py**: .. literalinclude:: /_resources/code/tutorials/scripts/multiple_nodes_setup.py :diff: /_resources/code/tutorials/scripts/params_setup.py In some special cases you do not want your executable to do ROS related stuff only. In that cases and only in such cases, it makes sense to replace the ``rclpy.spin()`` by a ``rclpy.spin_once()``. Remember: the **spin()** function loops our Node and waits for work for our Node to do. **spin_once()** instead will only ask the ROS2 system once, if there is work for the Node to do and not continously in a loop. That allows us to do something after the **spin_once()** request within the executable. Again: This is only useful in *special cases* and not recommended as a general approach. We will anyway do this now for our parameter example. Open your **param_node.py** Node and replace the ```rclpy.spin``` by ```rclpy.spin_once``` and rename the Node meanwhile: .. literalinclude:: /_resources/code/tutorials/scripts/simple_python_node2_with_spin_once.py :diff: /_resources/code/tutorials/scripts/simple_python_node2_with_shutdown.py We should set the **timeout_sec** to ``0.0`` in case there is some blocking function. To create our own parameter, we will add the following code in line 8 after creating the Node: .. code-block:: python myfirstnode.declare_parameter('my_param', 13) #.declare_parameter('', ) That's already it. → To see that it is working, run the Node and check the parameter value from command line. To see that our parameter is actively affected by changing its value, we can also ask the current parameter value in our Node using: .. code-block:: python myfirstnode.get_parameter('my_param').value #nodename.get_parameter('').value We can print the parameter value in a while loop so that we see the current value within the Node. Using rclpy we can loop around our *spin_once* unless it is ok: .. code-block:: python while rclpy.ok(): To not spam in our terminal, it is also useful to use a delay in the loop, let's say of 1 second. For that purpose we can use the Python **sleep()** function from the time library: .. code-block:: python from time import sleep # add this line in the beginning of your Python script sleep(1.0) # blocks the code for 1 second inside the loop Your final Python Node should now look like this: .. literalinclude:: /_resources/code/tutorials/scripts/simple_python_node2_with_params.py :caption: A simple Node to continously receive a parameter. :language: python :lines: 1- **Congratulations! You are now familiar with the basics of the ROS2 Filesystem.**