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!
0 Comments