Lab 02 - ROS2 Actions

Lab. 02 - ROS2 Actions

ROS2 Actions visualisation graph

1. Czym są Akcje w ROS2?

Akcje są jednym z typów komunikacji w ROS2, przeznaczonym do zadań, które trwają dłużej. Składają się z trzech części: celu (goal), informacji zwrotnej (feedback) i wyniku (result).

Akcje zbudowane są na topicach i serwisach. Ich sposób działania przypomina serwisy, z dwoma różnicami:

Cel (goal) - służy wywołaniu akcji. Ma funkcjonalność serwisu- wywołanie i odpowiedź, czy udało się uruchomić akcję.

Informacja zwrotna (feedback) - są to dane publikowane przez serwer akcji w czasie pracy. Zazwyczaj informują o aktualnym postępie działania, np. jaka odległość jeszcze została do przejechania, jaki jest szacowany pozostały czas albo w ilu procentach działanie zostało zakończone.

Wynik (result) - ostateczna odpowiedź serwera akcji na temat wyniku działania. Zazwyczaj zawiera binarną odpowiedź czy udało się wykonać żądane działanie lub z jaką dokładnością akcja została przeprowadzona.

2. Przygotowanie środowiska

Przed przystąpieniem do pracy należy przygotować środowisko: pobrać obraz dockera i utworzyć na jego podstawie kontener.

Obraz konieczny do wykonywania dzisiejszych zajęć opiera się o osrf/ros:humble-desktop-full, ale zawiera paczki, które są niezbędne do prawidłowego wykonania zadań. Obraz można pobrać z tego linku, lub korzystając z polecenia:

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

Pobrany obraz należy wczytać, korzystając z polecenia, podając odpowiednią ścieżkę:

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

Następnie można przejść do tworzenia kontenera korzystając ze skryptów przygotowanych dla osób korzystających wyłącznie z CPU lub wyposażonych w GPU Nvidia. Można pobrać odpowiedni skrypt korzystając np. z wget.

Domyślnie kontener nosi nazwę ARM_02. UWAGA! Skrypty po uruchomieniu usuwają kontener o takiej nazwie przed utworzeniem nowego.

Korzystanie z kontenera

Po każdym ponownym uruchomieniu komputera, proszę pamiętać o wywoływaniu:

xhost +local:root

Nowy terminal można dołączyć do kontenera korzystając z polecenia:

docker exec -it ARM_02 bash

3. Cel zajęć

Docelowo na zajęciach zadaniem będzie utworzenie akcji która porusza robota na zadaną odległość od przeszkody, która się przed nim znajduje, przekazując również czas na wykonanie zadania i minimalna dokładność, jaka jest wymagana. W feedbacku będzie publikowana informacja o aktualnej odległości od przeszkody i przewidywanym czasie pozostałym do zakończenia wykonywania akcji. Jako result serwer będzie informował czy udało się spełnić założenia, jaka jest ostateczna dokładność i odległość od celu oraz ile czasu trwała procedura.

4. Tworzenie akcji

Zanim rozpoczniemy prace nad powyższym zadaniem, warto zapoznać się z definiowaniem akcji na prostszym przykładzie - obliczaniu ciągu Fibonacciego.

Tworzenie nowej paczki

Aby utworzyć nową paczkę, należy, z poziomu /arm_ws/src wywołać polecenie:

ros2 pkg create --build-type ament_cmake fibonacci_sequence_action

Konieczne będzie też utworzenie katalogu action i zdefiniowanie w nim struktury akcji:

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

Należy zmodyfikować plik Fibonacci.action (np. korzystając z nano, gedit lub VS code):

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

gdzie order to cel akcji, sequence to wynik, a partial_sequence to feedback.

Mając zdefiniowaną strukturę akcji możemy ją dodać do pipelineu generowania kodu interfejsu wewnętrznego ROS2 rosidl. W tym celu konieczne jest zmodyfikowanie pliku CMakeLists.txt (przed linijką ament_package()) tak, aby zawierał:

find_package(rosidl_default_generators REQUIRED)

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

Należy również dodać wymagane zależności do pliku package.xml:

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<depend>action_msgs</depend>

<member_of_group>rosidl_interface_packages</member_of_group>

Na tym etapie powinno być możliwe zbudowanie definicji akcji:

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

Jeżeli budowanie zakończyło się pomyślnie, możliwe jest podejrzenie definicji akcji z poziomu interfejsu ROS2:

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

Przygotowanie skryptu serwera akcji

Wiedząc, że nowa paczka się buduje, a ROS2 udostpęnia strukturę akcji, możemy przystąpić do pisania skryptu serwera akcji.

Należy zacząć od utworzenia folderu scripts i pliku fibonacci_action_server.py:

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

Plik fibonacci_action_server.py powinien zostać zmodyfikowany następująco:

#!/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()

Należałoby także oznaczyć ten plik jako wykonywalny:

chmod +x fibonacci_action_server.py

Niezbędne będzie również dodanie ścieżki scripts/fibonacci_action_server.py i paczek rclpy oraz ament_cmake_python do pliku CMakeLists.txt:

find_package(ament_cmake_python REQUIRED)
find_package(rclpy REQUIRED)

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

oraz odpowiednich zależności do pliku package.xml:

  <buildtool_depend>ament_cmake_python</buildtool_depend>

  <depend>rclpy</depend>

Prace nad serwerem akcji zostały zakończone, możliwe jest teraz zbudowanie paczki i uruchomienie:

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

W osobnym terminalu możemy sprawdzić listę dostępnych akcji:

ros2 action list

oraz wywołać wykonywanie akcji:

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

W przypadku błędu o braku pliku wykonywalnego należy usunąć foldery build, install i log, oraz ponownie przebudować workspace:

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

5. Zadanie do samodzielnej realizacji

W ramach zadania należy przygotować akcję spełniającą założenia z punktu 3. W tym celu należy wykorzystać symulację:

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

Udostępnia ona dwa topic'i:

Pierwszy informuje o odległości od przeszkody, drugi natomiast powinien być wykorzystany do ruchu (liniowo, w osi x).

Wsparcie w zakresie pisania subscriberów w ROS2 można znaleźć tutaj.

Akcja powinna być zdefiniowana tak, jak poniżej:

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

UWAGA! Nazwa pliku action musi zaczynać się z wielkiej litery i zawierać wyłącznie litery oraz cyfry.

Podpowiedź: jak jednocześnie subskrybować topic i wykonywać akcję?

Umieszczenie pętli w metodzie wykonującej akcję spowoduje, że program przestanie odczytywać wiadomość z topicu /laser_scan.

Żeby sobie z tym poradzić, można użyć metody spin_once, przekazując node, na którym spin ma zostać wykonany (self, czyli node akcji).

Alternatywnym rozwiązaniem jest wykorzystanie wielowątkowego executora, zamiast tego domyślnie udostępnionego w ramach rclpy. Więcej informacji i przykład wykorzystania można znaleźć tutaj.

Na eKursy proszę udostępnić:


Autor: Kamil Młodzikowski
Na podstawie: Creating an action oraz Writing an action server and client (Python) ze strony docs.ros.org.