In questo tutorial vedremo come poter impostare la visualizzazione grafica di messaggi pubblicati sui differenti topic da una simulazione tramite l’uso di RViz e come poter salvare una configurazione della visualizzazione per poterla riutilizzare in futuro.
Visualizzazione dei messaggi dei topic
Per iniziare sarà necessario che siano attivi dei nodi che pubblicano dei messaggi adeguati, che siano nodi che abbiamo scritto noi, o preesistenti all’interno di una simulazione o di un robot con ROS installato.
Per questo tutorial utilizzeremo la simulazione di un drone disponibile sul corso di TheConstruct “Programming Drones with ROS“.
La simulazione sarà avviata nel momento in cui apriremo il corso e il relativo Rosject sarà avviato quando accederemo all’unità 3.
Possiamo verificare l’attività dei publisher relativi ai dati dei sensori a bordo del drone: rostopic list
Otterremo come output una serie di topic che pubblicano informazioni relative a sensori di varia natura (LiDAR, Depth Cameras ecc.) la cui visualizzazione potrebbe risultare interessante per lo scopo di un nostro progetto.
RViz
Per poter lavorare con RViz sarà necessario lanciarlo dal nostro terminale.
Procediamo quindi con il seguente comando: rosrun rviz rviz
Una volta di fronte alla schermata di RViz possiamo quindi iniziare a impostare un reference frame (in questo caso “base_link”).
Andiamo in alto a sinistra, in “Global options”, e impostiamo il “Fixed Frame” con valore “base_link”.
Siamo quindi pronti per poter aggiungere e visualizzare tutti i topic a cui siamo interessati.
Un ottimo punto di partenza sarebbe quello di visualizzare il modello fisico del robot che viene pubblicato dalla simulazione. In basso a sinistra premiamo quindi su “Add” e scorriamo fino a trovare “RobotModel”. Clicchiamo su “RobotModel” e poi su “Ok”.
Il modello fisico del robot sarà quindi rappresentato al centro della nostra simulazione.
All’interno di questa simulazione abbiamo per esempio anche la pubblicazione di messaggi da parte di sensori legati al robot, come LaserScan, ottenuti attraverso uno scan da parte di un LiDAR dell’ambiente circostante. In basso a sinistra premiamo quindi su “Add” e ci spostiamo nella tab “By topic”, dove potremo vedere quali topic stanno pubblicando messaggi, il loro nome e il tipo di messaggio che possiamo andare a visualizzare.
Aggiungiamo per esempio lo scan relativo al LiDAR /scan/LaserScan
le immagini raccolte dalla telecamera rgb frontale on-board /camera/rgb/image_raw/Image
e il PointCloud ritornato dalla DepthCamera /camera/depth/points/PointCloud2
Salvare la configurazione
Poter visualizzare tutti questi topic porta un grosso vantaggio nel momento in cui andremo a testare i nostri progetti o semplicemente a raccogliere informazioni su ciò che il robot effettivamente è in grado di percepire. Chiaramente, più topic dovranno essere tracciati più sarà lungo il processo di configurazione ogni volta che andremo ad aprire RViz.
Per evitare questa faticosa operazione è possibile salvare la configurazione di RViz in modo da poterla riutilizzare in una seconda apertura, recuperandola direttamente dal comando di avvio di RViz.
Questo permetterà non solo di non dover ripetere il processo di configurazione, ma anche di poter salvare configurazioni differenti in base a ciò di a cui siamo interessati e di potersi muovere in modo semplice tra di esse.
Procediamo dunque con creare uno spazio all’interno del quale salvare le nostre configurazioni.
Ci spostiamo sull’editor di codice e creiamo una nuova cartella “rviz” all’interno di catkin_ws/src/my_rtab_package.
Qui andremo a salvare la configurazione e a recuperarla quando andremo a lanciare RViz.
Torniamo quindi su RViz (ripetiamo la configurazione nel caso l’avessimo o si dovesse essere chiuso) e poi su “File” > “Save Config As” e scegliamo il percorso in cui salvare il nostro file di configurazione, in questo caso salviamo il file in /home/user/catkin_ws/src/my_rtab_package/rviz/sensor_view.rviz.
Avviare RViz con configurazione salvata
Quello che ci manca a questo punto sarà solo un modo per avviare RViz indicando la configurazione che vogliamo che sia utilizzata. Utilizziamo dunque il comando: rosrun rviz rviz -d `rospack find my_rtab_package`/rviz/sensor_view.rviz
IMPORTANTE: l’uso del backtick (`) è essenziale perché il comando funzioni, con il layout italiano è possibile ottenerlo con [AltGr + ‘]
In questo modo RViz viene avviato e configurato a seconda del file che indichiamo all’interno del comando, risparmiandoci la fatica di dover ripetere tutto il procedimento dei punti precedenti e garantendo una maggiore flessibilità nell’osservare diverse visualizzazioni dei nostri messaggi.
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
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:
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:
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:
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:
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:
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!
In questo tutorial vedremo come sia possibile configurare ROS per funzionare su più macchine connesse attraverso la rete, espandendo le possibilità per i tuoi progetti.
La configurazione iniziale parte da un nodo publisher minimale chatter.py appartenente al pacchetto chatter, con un singolo publisher di stringhe.
roscore
Consideriamo che un roscore per poter essere eseguito necessita di una configurazione iniziale di due variabili principali all’interno del nostro terminale (che sono inizializzate con un valore di default).
ROS_MASTER_URI Definisce l’indirizzo della macchina che avrà il ruolo di “master” per la nostra rete di ROS. La sua struttura è definita come
http://< indirizzo_della_macchina>:11311
ROS_HOSTNAME Definisce l’indirizzo della macchine ospite (quella che vogliamo che sia in grado di comunicare con il roscore), tipicamente l’ip della macchina che stiamo configurando.
Possiamo leggere il contenuto di queste due variabili con le seguenti istruzioni:
Come puoi vedere il nome dell’ospite e del master corrente è settato con un nome simbolico a causa dell’implementazione dei Rosject, nella tua macchina potresti vedere “localhost” o un indirizzo specifico se hai già modificato le variabili.
Ora, possiamo provare a runnare un roscore in un primo terminal, e a osservare il suo output.
roscore
# Output: process[master]: started with pid [4295]
# ROS_MASTER_URI=http://4_xterm:11311/
Possiamo notare quindi, come previsto, il ROS_MASTER_URI settato sulla macchina corrente.
Settare il roscore per connettere altre macchine
All’interno della macchina su cui vogliamo far girare il roscore e ospitare altre macchine (tipicamente la nostra macchina) dovremo cambiare i valori all’interno delle due variabili già menzionate.
All’interno della macchina che vogliamo utilizzare come ospite (tipicamente il nostro robot, o i nostri robot) dovremmo fare lo stesso, ma con valori differenti.
Vediamo quindi nel dettaglio come impostare questi valori.
Macchina ospitante
ROS_MASTER_URI dovrà essere impostato con l’ip della macchina all’interno della nostra rete. Per trovare questo ip si possono utilizzare le impostazioni di rete, del router, o anche comandi da terminale come ifconfig o ip addr. Poiché lavoreremo su un solo Rosject, utilizzeremo l’indirizzo corrispondente al localhost.
Assumiamo quindi che la nostra macchina abbia come indirizzo 127.0.0.1. In una prima shell eseguiamo:
exportROS_MASTER_URI=http://127.0.0.1:11311
exportROS_HOSTNAME=127.0.0.1
ROS_HOSTNAME sarà l’ip stesso della macchina.
Faremo lo stesso in una seconda shell, per scopo di testing.
Macchina ospite (e.g. robot)
ROS_MASTER_URI dovrà essere impostato con l’ip della macchina ospitante all’interno della nostra rete. Perciò utilizzeremo lo stesso comando della configurazione sulla macchina ospitante. In una terza shell, eseguiamo:
export ROS_MASTER_URI=http://127.0.0.1:11311
Per quanto riguarda ROS_HOSTNAME dovremo usare invece l’ip della macchina ospite, recuperabile con gli stessi metodi già menzionati. Assumiamo quindi che il nostro robot abbia come indirizzo ip 127.0.0.42.
export ROS_HOSTNAME=127.0.0.42
A questo punto le due macchine saranno in grado di comunicare e di avere accesso a tutti i messaggi pubblicati all’interno della rete e di pubblicare a loro volta.
Considerazioni importanti per questo rosject
Quando si cambiano i valori di queste variabili, la modifica sarà limitata alla shell su cui viene invocato il comando. Di fatto, aprendo una nuova shell, essa avrà nuovamente le variabili settate con i valori di default.
Sicuramente è possibile inserire i due comandi nel file ~/.bashrc, ma in questo caso non lo faremo per verificare l’effetto della modifica.
Test del nostro nodo
Nella terza shell (che sarebbe eseguita sul robot), possiamo quindi lanciare l’esecuzione del nostro nodo chatter (che possiamo interpretare come un nodo nel nostro robot che pubblica lo stato di un sensore).
rosrun chatter chatter.py
Grazie alla stampa ad output possiamo notare che il nodo inizia a inviare messaggi.
Andando quindi nella seconda shell (che si troverebbe sulla macchina del master) possiamo andare a stampare la lista dei topic pubblicati.
rostopic list
Ecco qui, anche se ci troviamo su una macchina differente, riceviamo i messaggi mandati dal nostro robot.
Osservazione
Se proviamo ad aprire una quarta shell, che quindi sarà configurata con i valori di default (a meno che le istruzioni di export non siano state messe nel file ~/.bashrc), questa shell non sarà in grado di interagire con il roscore, nemmeno se aperta sulla stessa macchina del master.
La configurazione dovrà essere ripetuta per ogni shell aperta, in accordo con la macchina su cui viene aperta (vedi differenze tra macchina ospitante e macchina ospite), oppure per comodità si potrà utilizzare il file ~/.bashrc.
Nel caso non si volesse installare ROS Noetic sul proprio sistema e fare il set up dell’ambiente di sviluppo, è possibile utilizzare i Rosject offerti da The Construct per poter testare i contenuti del tutorial o per seguirlo passo passo.
Per fare ciò sarà sufficiente seguire il link indicato nelle risorse e cliccare su “Run”. In questo modo si avrà accesso a una copia del Rosject utilizzato all’interno del tutorial.
Aprire terminal e IDE
Per aprire un nuovo terminale e avere accesso all’IDE forniti dal Rosject, sarà possibile utilizzare la console in basso a destra
Creare un nuovo package
Per poter creare un nuovo package sarà sufficiente spostarsi all’interno del workspace di catkin, e creare il package con l’aiuto di catkin_create_pkg. In questo caso aggiungendo come dependencies rospy (poiché scriveremo in Python) e std_msgs. cd ~/catkin_ws
catkin_create_pkg custom_srv_pkg rospy std_msgs
Creare un nuovo file per la definizione del Service
Quello che bisogna fare ora è creare una cartella che possa contenere i file di definizione dei Service e il file per il nostro Service personalizzato.
Definizione della struttura del Service personalizzato
I servizi sono identificati da due sezioni, divise da un triplo dash (- – -):
Request: inviata da un client a un server, con i parametri necessari all’esecuzione del servizio
Response: ritornata dal server al client, con eventuali risultati dell’esecuzione del servizio
⚠ Request e response possono essere anche vuoti
All’interno delle due sezioni possiamo usare per esempio std_msgs, ma anche altri tipi di messaggio come geometry_msgs.
Andiamo quindi a definire il nostro servizio con i seguenti parametri: # Request
string da_stampare
geometry_msgs/Point punto
---
# Response
float64 somma
bool successo
In questo caso abbiamo una stringa da stampare, un punto tridimensionale, una variabile somma rappresentante la somma delle coordinate del punto e un booleano che indica il successo dell’esecuzione del servizio.
CMakeLists e package.xml
Per permettere la compilazione del servizio e la generazione dei relativi messaggi, bisogna apportare qualche modifica al file CMakeLists e al package.xml.
CMakeLists
Qui sarà necessario includere le seguenti linee:
cmake_minimum_required(VERSION 3.0.2)
project(custom_srv_pkg)
find_package(catkin REQUIRED COMPONENT
rospy
std_msgs
message_generation # per la generazione dei messaggi relativi al servizio
geometry_msgs # poiché abbiamo utilizzato un messaggio di questo tipo
)
add_service_files(
FILES
CustomSrvMsg.srv # il nostro file dove abbiamo definito il servizio
)
generate_messages(
DEPENDENCIES
std_msgs
geometry_msgs # per generare messaggi contenenti geometry_msgs
)
catkin_package(
CATKIN_DEPENDS rospy std_msgs
)
package.xml
All’interno del package.xml dovremo aggiungere le dependencies per la generazione e l’utlizzo dei messaggi relativi al servizio
cd ~/catkin_ws
catkin_make –only-pkg-with-deps custom_srv_pkg
source devel/setup.bash # poiché sono stati generati nuovi messaggi e servizi
Verificare la generazione dei messaggi del Service personalizzato
Per poter verificare la presenza dei messaggi relativi al nostro nuovo messaggio e il loro contenuto possiamo utilizzare i seguenti comandi: rossrv list | grep custom # grep custom è usato per filtrare l’output
# l’output sarà del tipo:
# custom_srv_pkg/CustomSrvMsg
rossrv show custom_srv_pkg/CustomSrvMsg
# l’output sarà del tipo:
# string da_stampare
# geometry_msgs/Point punto
# float64 x
# float64 y
# float64 z
# ---
# float64 somma
# bool successo
Importare i messaggi generati per il servizio
All’interno dello script server.py possiamo importare:
Nel caso di un server:
[NomeDelMessaggioDelServizio]
[NomeDelMessaggioDelServizio]Response
Nel caso di un client:
[NomeDelMessaggioDelServizio]
[NomeDelMessaggioDelServizio]Request
Nel tutorial scriveremo un esempio di un server di Servizi, quindi importeremo i messaggi in questo modo:
from custom_srv_pkg.srv import CustomSrvMsg,CustomSrvMsgResponse
# from custom_srv_pkg.srv import CustomSrvMsg,CustomSrvMsgRequest # nel caso client
Accesso ai parametri di request e response
In una possibile implementazione del nostro server accediamo a request, ricevuto dal client tramite una callback invocata ogni volta che viene richiesto il servizio, e ai relativi parametri.
Possiamo poi istanziare una response (di tipo CustomSrvMsgResponse) e riempirne i campi con le informazioni che ci interessa ritornare al client.
#! /usr/bin/env python
import rospy
from custom_srv_pkg.srv import CustomSrvMsg, CustomSrvMsgResponse
# CustomServiceRequest in caso di un client
def callback(request):
response = CustomSrvMsgResponse()
print(request.da_stampare)
response.somma = request.punto.x + request.punto.y + request.punto.z
response.successo = True
return response
rospy.init_node("service_server")
service = rospy.Service("my_service", CustomSrvMsg, callback)
rospy.spin()
Conclusione
Se siete riusciti a seguire fino a qui sarà possibile compilare il package e eseguire il nodo relativo al Service server. In questo modo sarà reso disponibile il servizio che potrà essere chiamato da linea di comando o da un client.
# terminale 1
roscore
# terminale 2
rosrun custom_srv_pkg server.py # esecuzione del nodo server.py
# terminale 3
rosservice list | grep my # mostra tutti i servizi disponibili contenenti "my"
rosservice call /my_service [TAB] [TAB] # chiama il servizio e precompone la request
In questo tutorial verrà mostrata un’implementazione di un nodo subscriber in grado di leggere messaggi di diverso tipo e provenienti da molteplici topic, gestiti attraverso una singola funzione di callback.
Nel caso non si volesse installare ROS Noetic sul proprio sistema e fare il set up dell’ambiente di sviluppo, è possibile utilizzare i Rosject offerti da The Construct per poter testare i contenuti del tutorial o per seguirlo passo passo.
Per fare ciò sarà sufficiente seguire il link indicato nelle risorse e cliccare su “Run”. In questo modo si avrà accesso a una copia del
Rosject utilizzato all’interno del tutorial.
Aprire terminal e IDE
Per aprire un nuovo terminale e avere accesso all’IDE forniti dal Rosject, sarà possibile utilizzare la console in basso a destra.
Prerequisiti
Per poter seguire il tutorial passo passo sarò necessario avere il nodo publisher, che pubblica su diversi topic, attivo e creare un package dove poter inserire il codice per il subscriber.
Per iniziare eseguiamo il nodo publisher. Sarà sufficiente aprire il terminale dalla console (vedi Risorse) e inserire i seguenti comandi nelle rispettive schede del terminale:
Terminale 1: roscore Terminale 2: cd catkin_ws
rosrun publisher publisher.py
Quindi è possibile verificare il corretto funzionamento del nodo aprendo una seconda finestra al’interno del terminale e osservando l’output del comando rostopic list.
L’output previsto è il seguente:
Terminale 3: rostopic list
/chatter0
/chatter1
/chatter2
/rosout
/rosout_agg
Procediamo quindi a creare il package per il subscriber:
Terminale 3: cd src
catkin_create_pkg multiple_subscriber rospy std_msgs
mkdir multiple_subscriber/script
Situazione di partenza
Al momento abbiamo eseguito il nodo publisher che pubblica su 3 topic messaggi di vario tipo. In particolare:
/chatter0 pubblica messaggi di tipo String
/chatter1 pubblica messaggi di tipo Float64
/chatter2 pubblica messaggi di tipo Bool
Vediamo quindi come possiamo gestire tutti e tre i topic, creando un subscriber in grado di leggere i messaggi dai chatter e di gestirli con una singola funzione di callback, differenziando il risultato di quest’ultima sfruttando l’uso di parametri.
Creazione del file multiple_sub.py
Proseguiamo creando il file necessario a definire il nostro subscriber e tutte le sue funzioni.
Terminale 3: touch multiple_sub/script/multiple_subscriber.py # Crea il file
chmod +x multiple_sub/script/multiple_subscriber.py # Garantisce i permessi di esecuzione
cd ..
catkin_make
Implementazione del subscriber
Partiamo quindi dallo specificare un interprete per il nostro codice in Python, dall’importare i moduli necessari (rospy e in questo caso std_msgs/String, std_msgs/Float64, std_msgs/Bool) e dallo scrivere un main che inizializzi il nodo e invochi rospy.spin() per poter eseguire le callback del nostro subscriber.
main
multiple_subscriber.py import rospy
from std_msgs.msg import String, Float64, Bool
if __name__=="__main__":
rospy.init_node("multiple_subscriber")
topics = {"chatter0": [String, "Argomento passato da chatter0"], "chatter1": [Float64, "Argomento passato da chatter1"], "chatter2": [Bool, "Argomento passato da chatter2"]}
multiple_subscribe()
rospy.spin()
All’interno della funzione abbiamo definito anche topics, un dizionario contenente le specifiche dei topic che vogliamo andare a leggere, seguendo la seguente struttura: {"nome_topic": [Tipo, Argomento]}
Per ogni chiave topic abbiamo quindi definito una lista contente il tipo del messaggio inviato dal topic e un argomento (in questo caso una stringa che esplicita di essere l’argomento passato per lo specifico topic) .
multiple_subscribe
La funzionemultiple_subscribe() sarà definita perché il nodo possa seguire i 3 topic e perché i messaggi pubblicati possano innescare la callback.
Il nodo segue quindi ciascuno dei topic definiti nel dizionario, aspettandosi messaggi del relativo tipo, invocando una funzione callback e passando il relativo argomento tramite il parametro callback_args.
callback
Vediamo una semplice implementazione della callback per poter gestire i tre topic.
multiple_subscriber.py def callback(data, args):
rospy.loginfo(f"Ricevuto: {data.data}")
print(args)
print('\n')
In questo caso andiamo a stampare come info di ROS il contenuto del messaggio arrivato alla callback e stampiamo l’argomento passato corrispondente. Entrambi dipenderanno da quale topic ha innescato la callback e perciò a questo livello il messaggio può essere gestito a seconda del parametro ad esso associato, potendo implementare in qualche modo un’esecuzione “su misura” della funzione di callback.
Test del subscriber
Siamo pronti per poter eseguire il nostro subscriber!
Ciò che ci aspettiamo è che vengano stampati allo stesso tempo il messaggio e l’argomento relativi alla callback innescata dall’arrivo di un messaggio dal singolo topic. L’output dovrebbe quindi essere di questo tipo:
[INFO] [***.***]: Ricevuto: Questo pubblica una stringa
Argomento passato da chatter0
[INFO] [***.***]: Ricevuto: 0.42
Argomento passato da chatter1
[INFO] [***.***]: Ricevuto: True
Argomento passato da chatter2
Possiamo osservare come gli output si alternano ogni secondo a rotazione, ma questo è dovuto alla singola implementazione del publisher (che è possibile trovare all’interno del Rosject, vedi Risorse).
In ogni caso per ogni topic viene ricevuto, gestito e stampato un messaggio specifico relativo ad esso, e l’elaborazione del relativo parametro, con diverso risultatononostante l’utilizzo della medesima funzione di callback.