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: Instrukcja przygotowania środowiska do zajęć.

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

ROS_DOMAIN_ID

W przypadku pracy na wielu komputerach w tej samej sieci lokalnej (eduroam też może tego wymagać), konieczne może być ustawienie zmiennej środowiskowej ROS_DOMAIN_ID na różnych wartościach. Należy wybrać dowolną liczbę całkowitą z zakresu 0-100. Wartość tę należy ustawić w pliku ~/.bashrc:

export ROS_DOMAIN_ID=10  # przykładowa wartość

lub bezpośrednio w terminalu (wtedy będzie obowiązywać tylko w tym terminalu):

export ROS_DOMAIN_ID=10  # przykładowa wartość

Sugerowane jest użycie dwóch ostatnich cyfr numeru IP komputera jako wartości ROS_DOMAIN_ID, aby uniknąć konfliktów z innymi użytkownikami w sieci. Aby sprawdzić swój adres IP, można użyć polecenia:

ip addr show

adres IP będzie widoczny przy interfejsie sieciowym, np. wlan0 lub eth0.

Wygodne może być dodanie tego exportu do bashrc:

echo 'export ROS_DOMAIN_ID=<wybrana_wartość>' >> ~/.bashrc 

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-install

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.