Definizione e nesting di messaggi personalizzati – ROS (Noetic) Italian Tutorial

Written by Alessandro Pozzoni

16/04/2024

This tutorial is created by Robotics Ambassador Alessandro

Robotics Ambassador Program https://www.theconstruct.ai/robotics-ambassador/

In questo tutorial andremo a vedere come definire messaggi personalizzati da utilizzare all’interno dei nostri nodi e come comporli per ottenere messaggi più elaborati. Inoltre vedremo come poter accedere ai singoli campi dei messaggi in modo più intuitivo all’interno del codice.
Il progetto descritto da questo tutorial è disponibile sul sito di TheConstruct al seguente link: Definizione e nesting di messaggi personalizzati [ROS Noetic] [Python]

Definire la struttura dei messaggi

All’interno del package in cui vogliamo andare a definire i nostri messaggi personalizzati (in questo caso custom_messages_nesting) andiamo a creare una nuova cartella, identificata dal nome “msg”. Questa sarà la cartella che durante la compilazione sarà utilizzata per verificare la presenza dei file di definizione dei messaggi personalizzati (che indicheremo successivamente).
Quindi possiamo creare un file con estensione .msg per ogni tipo di messaggio personalizzato che vogliamo definire.
Nel caso di questo tutorial assumiamo di voler avere un messaggio che contiene delle letture provenienti da sensori di un robot mobile con una componente di edge computing visiva, e avremo quindi:

  • NestedCustomMessage.msg che conterrà un’istanza dei seguenti messaggi e vi aggiungerà un ID
  • LidarGradient.msg rappresentante i valori del gradiente computato su una serie di letture da parte di un LiDAR
  • BoundingBoxLabeled.msg contenente le coordinate di una bounding box e la relativa label
  • EncodersReadings.msg con i valori letti dagli encoder delle ruote del nostro robot

Di seguito riportiamo le definizioni dei messaggi, in modo da avere un’idea chiara e immediata della loro struttura. In particolare possiamo notare come messaggi personalizzati possano essere annidati per andare a comporne altri più complessi, nel caso di NestedCustomMessage.

NestedCustomMessage.msg
int32 id
LidarGradient gradient
BoundingBoxLabeled bb_labeled
EncodersReadings enc_readings
LidarGradient.msg
float32[] measures
BoundingBoxLabeled.msg
string label
float32 x_left
float32 x_right
float32 y_top
float32 y_bottom
EncodersReadings.msg
float32 wheel1
float32 wheel2
float32 wheel3

Garantire la compilazione e generazione dei messaggi

Per poter usufruire dei messaggi che abbiamo definito è necessario dare indicazione a ROS di dove si trovino e delle dependencies necessarie alla loro compilazione e generazione. Per questo motivo apporteremo delle modifiche al file CMakeLists.txt e al Package.xml del nostro package.

Package.xml

All’interno di questo file dobbiamo aggiungere le due seguenti righe:

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

CMakeLists.txt

Qui partiamo dal modificare le required components in questo modo:

find_package(catkin REQUIRED COMPONENTS
   rospy
   std_msgs
   message_generation
)

Indichiamo poi i file in cui abbiamo definito i messaggi (i file verranno cercati all’interno della cartella “msg” del nostro package):

add_message_files(
   FILES
   LidarGradient.msg
   BoundingBoxLabeled.msg
   EncodersReadings.msg
   NestedCustomMessage.msg
)

E includiamo le dependencies necessarie per la generazione dei nostri messaggi. In questo caso i nostri messaggi dipendono solo da std_msgs (abbiamo usato solo stringhe e float), per cui sarà sufficiente includere:

generate_messages(
   DEPENDENCIES
   std_msgs
)

Generazione dei messaggi

Possiamo a questo punto verificare che tutto sia stato definito in modo corretto e che ROS sia in grado di trovare, identificare e generare i nostri messaggi personalizzati.
Ci spostiamo all’interno di un terminale, a livello del nostro workspace di catkin, e ricompiliamo.

catkin_make

Importare e accedere ai singoli campi dei messaggi

La situazione di partenza di questo tutorial è un semplice publisher (custom_messages_nesting/script/custom_user_node.py) che pubblica un Float32. Andremo ora a modificare il codice per poter includere i nostri messaggi.

#! /usr/bin/env python3

import rospy
from std_msgs.msg import Float32

def start_chatter():
   rospy.init_node('custom_chatter')
   pub = rospy.Publisher('custom_chatter_topic', Float32, queue_size = 10)
   rate = rospy.Rate(1)
   id = 0
   while not rospy.is_shutdown():
      id += 1
      pub.publish(id)
      rate.sleep()

if __name__ == "__main__":
   try:
      start_chatter()
   except rospy.ROSInterruptException:
      pass

Vogliamo per prima cosa importare i messaggi personalizzati. Aggiungiamo quindi (o rimpiazziamo l’import già presente con):

from custom_messages_nesting.msg import LidarGradient.msg, BoundingBoxLabeled.msg, EncodersReadings.msg, NestedCustomMessage.msg

Possiamo poi quindi ridefinire il publisher in modo che pubblichi il nostro NestedCustomMessage:

pub = rospy.Publisher('custom_chatter_topic', NestedCustomMessage, queue_size = 10)

Per poter accedere ai singoli campi della struttura del NestedCustomMessage (nel caso della pubblicazione) sarà sufficiente istanziare i sottomessaggi di cui è composto e assegnare ai rispettivi campi i valori letti dai nostri ipotetici sensori.
Istanziamo quindi tutti i messaggi necessari:

nc = NestedCustomMessage()
gradient_msg = LidarGradient()
bb_labeled_msg = BoundingBoxLabeled()
enc_readings_msg = EncodersReadings()

E procediamo ad assegnare loro dei valori, accedendo ai campi del messaggio con il nome che abbiamo indicato all’interno della rispettiva definizione. In particolare per questo esempio abbiamo selezionato i seguenti valori:

gradient_msg.measures = range(36)
bb_labeled_msg.label = 'Persona'
bb_labeled_msg.x_left = bb_labeled_msg.y_bottom = 1.0
bb_labeled_msg.x_right = bb_labeled_msg.y_top = 2.0
enc_readings_msg.wheel1 = enc_readings_msg.wheel2 = enc_readings_msg.wheel3 = 0.035

Avendo assegnato valori a tutte le componenti di NestedCustomMessage, queste possono poi essere unite per andare a comporre il nostro messaggio finale in questo modo:

nc.id = id
nc.gradient = gradient_msg
nc.bb_labeled = bb_labeled_msg
nc.enc_readings = enc_readings_msg

Test del publisher

Per verificare che tutto funzioni correttamente sarà sufficiente eseguire il nodo e osservare il relativo topic.
In una prima shell:

roscore

In una seconda shell:

rosrun custom_messages_nesting custom_user_node.py

E infine in una terza shell:

rostopic echo /custom_chatter_topic

Il risulato che ci aspettiamo è di osservare che il nostro messaggio viene pubblicato una volta al secondo, con ID crescente e con gli altri campi in accordo con quello che abbiamo indicato nel codice del nodo.

E con questo abbiamo accesso a infinite possibilità, componendo i messaggi come più conviene e utilizzandoli all’interno della nostra implementazione!

Video Tutorial

Topics: ROS
Masterclass 2023 batch2 blog banner

Check Out These Related Posts

0 Comments

Pin It on Pinterest

Share This