Eng_Lab 02 - ROS2 Actions

Lab. 02 - ROS2 Actions

ROS2 Actions visualisation graph

1. What are ROS2 Actions?

Actions are one of the communication types in ROS2 designed for tasks that take longer to complete. They consist of three parts: goal, feedback, and result.

Actions are built on topics and services. Their operation is similar to services, with two differences:

Goal - serves to initiate the action. It has service functionality - to call and respond whether the action was successfully started.

Feedback - data published by the action server during operation. Typically, it informs about the current progress of the action, such as the remaining distance to travel, estimated remaining time, or the percentage of completion.

Result - the action server's final response regarding the action's outcome. It usually contains a binary answer on whether the requested action was successful and the precision with which the action was performed.

2. Environment Setup

Before starting the work, it is necessary to prepare the environment: download the Docker image and create a container based on it.

The image required for today's exercises is based on osrf/ros:humble-desktop-full but includes packages necessary for the tasks. The image can be downloaded from this link or using the command:

wget --content-disposition --no-check-certificate https://chmura.put.poznan.pl/s/qnlGDi1XeWicyFo/download

Load the downloaded image using the following command, providing the appropriate path:

docker load < path/to/file.tar.gz

Then, proceed to create the container using the scripts prepared for those using only CPU or equipped with Nvidia GPU. Download the relevant script using wget, for example.

By default, the container is named ARM_02. NOTE! The scripts, when executed, remove a container with this name before creating a new one.

Using the Container

After each computer restart, remember to invoke:

xhost +local:root

A new terminal can be attached to the container using the command:

docker exec -it ARM_02 bash

3. Course Objective

The goal of the exercises will be to create an action that moves the robot a specified distance from the obstacle in front of it, also providing the time to complete the task and the minimum accuracy required. The feedback will include information about the current distance from the obstacle and the estimated time remaining to complete the action. As a result, the server will inform whether the requirements were met, what the final accuracy and distance from the goal are, and how long the procedure took.

4. Creating Actions

Before we start working on the above task, it's worth familiarizing ourselves with defining actions using a simpler example - calculating the Fibonacci sequence.

Creating a New Package

To create a new package, from the /arm_ws/src level, run the command:

ros2 pkg create --build-type ament_cmake fibonacci_sequence_action

It is also necessary to create an action directory and define the action structure in it:

cd /arm_ws/src/fibonacci_sequence_action
mkdir action && cd action
touch Fibonacci.action

Modify the Fibonacci.action file (e.g., using nano, gedit, or VS code):

int32 order
---
int32[] sequence
---
int32[] partial_sequence

where order is the action's goal, sequence is the result, and partial_sequence is the feedback.

With the action structure defined, add it to the ROS2 internal interface code generation pipeline. To do this, modify the CMakeLists.txt file (before the ament_package() line) to include:

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "action/Fibonacci.action"
)

Also, add the required dependencies to the package.xml file:

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<depend>action_msgs</depend>

<member_of_group>rosidl_interface_packages</member_of_group>

At this stage, it should be possible to build the action definition:

cd /arm_ws
source /opt/ros/humble/setup.bash
colcon build --symlink-install

If the build was successful, you can view the action definition from the ROS2 interface:

source /arm_ws/install/setup.bash
ros2 interface show fibonacci_sequence_action/action/Fibonacci

Preparing the Action Server Script

Knowing that the new package is built and ROS2 provides an action structure, we can start writing the action server script.

Start by creating a scripts folder and a fibonacci_action_server.py file:

cd /arm_ws/src/fibonacci_sequence_action/
mkdir scripts && cd scripts
touch fibonacci_action_server.py

The fibonacci_action_server.py file should be modified as follows:

#!/usr/bin/env python3
import time

import rclpy
from rclpy.action import ActionServer
from rclpy.node import Node

from fibonacci_sequence_action.action import Fibonacci


class FibonacciActionServer(Node):

    def __init__(self):
        super().__init__('fibonacci_action_server')
        self._action_server = ActionServer(
            self,
            Fibonacci,
            'fibonacci',
            self.execute_callback)
        self.get_logger().info('Fibonacci Action Server is running...')

    def execute_callback(self, goal_handle):
        self.get_logger().info('Executing goal...')

        feedback_msg = Fibonacci.Feedback()
        feedback_msg.partial_sequence = [0, 1]

        for i in range(1, goal_handle.request.order):
            feedback_msg.partial_sequence.append(
                feedback_msg.partial_sequence[i] + feedback_msg.partial_sequence[i-1])
            self.get_logger().info('Feedback: {0}'.format(feedback_msg.partial_sequence))
            goal_handle.publish_feedback(feedback_msg)
            time.sleep(1)

        goal_handle.succeed()

        result = Fibonacci.Result()
        result.sequence = feedback_msg.partial_sequence
        return result


def main(args=None):
    rclpy.init(args=args)

    fibonacci_action_server = FibonacciActionServer()

    rclpy.spin(fibonacci_action_server)


if __name__ == '__main__':
    main()

Make sure to mark this file as executable:

chmod +x fibonacci_action_server.py

You will also need to add the path scripts/fibonacci_action_server.py and the packages rclpy and ament_cmake_python to the CMakeLists.txt file:

find_package(ament_cmake_python REQUIRED)
find_package(rclpy REQUIRED)

install(PROGRAMS
  scripts/fibonacci_action_server.py
  DESTINATION lib/${PROJECT_NAME}
)

as well as the corresponding dependencies to the package.xml file:

  <buildtool_depend>ament_cmake_python</buildtool_depend>

  <depend>rclpy</depend>

The work on the action server is complete; now you can build the package and run it:

cd /arm_ws
colcon build --symlink-install
source install/setup.bash
ros2 run fibonacci_sequence_action fibonacci_action_server.py

In a separate terminal, you can check the list of available actions:

ros2 action list

and send an action goal:

ros2 action send_goal --feedback /fibonacci fibonacci_sequence_action/action/Fibonacci "{order: 10}"

If there is an error regarding the lack of an executable file, delete the build, install, and log folders, and then rebuild the workspace:

rm -r /arm_ws/build /arm_ws/install /arm_ws/log
source /opt/ros/humble/setup.bash
colcon build --symlink-insall

5. Independent Task

As part of the task, prepare an action that meets the requirements from Section 3. To do this, use the simulation:

cd /arm_ws
source install/setup.bash
ros2 launch arm02 arm02_world.launch.py

It provides two topics:

The first informs about the distance from the obstacle, while the second should be used for linear motion (in the x-axis).

Support for writing subscribers in ROS2 can be found here.

The action should be defined as follows:

float32 goal_distance
float32 timeout
float32 precision
---
bool succeeded
float32 final_precision
float32 total_time
---
float32 current_distance
float32 estimated_time

NOTE! The action file name must start with a capital letter and contain only letters and numbers.

Hint: How to subscribe to a topic and execute an action simultaneously?

Placing a loop in the action execution method will cause the program to stop reading messages from the /laser_scan topic.

To handle this, you can use the spin_once method, passing the node on which the spin should be executed (self, which is the action node).

An alternative solution is to use a multithreaded executor instead of the one provided by default in rclpy. More information and an example of usage can be found here.

On eKursy, please provide:


Based on: Creating an action and Writing an action server and client (Python) from docs.ros.org.