Vai al contenuto

Guida rapida ROS (Robot Operating System)

Introduzione a ROS (didascalia) da Open Robotics su Vimeo.

Che cos'è il ROS?

Il Robot Operating System (ROS) è un framework open-source ampiamente utilizzato nella ricerca e nell'industria robotica. ROS fornisce una raccolta di librerie e strumenti per aiutare gli sviluppatori a creare applicazioni robotiche. ROS è progettato per funzionare con diverse piattaforme robotiche, il che lo rende uno strumento flessibile e potente per i robotisti.

Caratteristiche principali di ROS

  1. Architettura modulare: ROS ha un'architettura modulare che consente agli sviluppatori di costruire sistemi complessi combinando componenti pi√Ļ piccoli e riutilizzabili chiamati nodi. Ogni nodo svolge una funzione specifica e i nodi comunicano tra loro utilizzando messaggi su argomenti o servizi.

  2. Middleware di comunicazione: ROS offre una solida infrastruttura di comunicazione che supporta la comunicazione tra processi e il calcolo distribuito. Ciò avviene attraverso un modello publish-subscribe per i flussi di dati (topic) e un modello request-reply per le chiamate di servizio.

  3. Astrazione dell'hardware: ROS fornisce un livello di astrazione sull'hardware, consentendo agli sviluppatori di scrivere codice indipendente dal dispositivo. Questo permette di utilizzare lo stesso codice con diverse configurazioni hardware, facilitando l'integrazione e la sperimentazione.

  4. Strumenti e utilità: ROS è dotato di una ricca serie di strumenti e utilità per la visualizzazione, il debug e la simulazione. Ad esempio, RViz viene utilizzato per visualizzare i dati dei sensori e le informazioni sullo stato del robot, mentre Gazebo fornisce un potente ambiente di simulazione per testare algoritmi e progetti di robot.

  5. Ecosistema esteso: L'ecosistema ROS è vasto e in continua crescita, con numerosi pacchetti disponibili per diverse applicazioni robotiche, tra cui navigazione, manipolazione, percezione e altro ancora. La comunità contribuisce attivamente allo sviluppo e alla manutenzione di questi pacchetti.

Evoluzione delle versioni ROS

Dal suo sviluppo nel 2007, ROS si è evoluto attraverso diverse versioni, ognuna delle quali ha introdotto nuove funzionalità e miglioramenti per soddisfare le crescenti esigenze della comunità robotica. Lo sviluppo di ROS può essere suddiviso in due serie principali: ROS 1 e ROS 2. Questa guida si concentra sulla versione Long Term Support (LTS) di ROS 1, nota come ROS Noetic Ninjemys, ma il codice dovrebbe funzionare anche con le versioni precedenti.

ROS 1 vs. ROS 2

Mentre ROS 1 ha fornito una solida base per lo sviluppo robotico, ROS 2 affronta le sue carenze offrendo:

  • Prestazioni in tempo reale: Supporto migliorato per i sistemi in tempo reale e comportamento deterministico.
  • Sicurezza: Funzioni di sicurezza avanzate per un funzionamento sicuro e affidabile in vari ambienti.
  • Scalabilit√†: Migliore supporto per i sistemi multi-robot e per le implementazioni su larga scala.
  • Supporto multipiattaforma: Compatibilit√† ampliata con diversi sistemi operativi oltre a Linux, tra cui Windows e macOS.
  • Comunicazione flessibile: Utilizzo del DDS per una comunicazione inter-processo pi√Ļ flessibile ed efficiente.

Messaggi e argomenti ROS

In ROS, la comunicazione tra i nodi è facilitata da messaggi e argomenti. Un messaggio è una struttura di dati che definisce le informazioni scambiate tra i nodi, mentre un argomento è un canale denominato su cui vengono inviati e ricevuti i messaggi. I nodi possono pubblicare messaggi su un argomento o sottoscrivere messaggi da un argomento, consentendo loro di comunicare tra loro. Questo modello publish-subscribe consente la comunicazione asincrona e il disaccoppiamento tra i nodi. Ogni sensore o attuatore di un sistema robotico pubblica tipicamente dei dati in un argomento, che possono poi essere utilizzati da altri nodi per l'elaborazione o il controllo. Ai fini di questa guida, ci concentreremo sui messaggi e sugli argomenti delle telecamere Image, Depth e PointCloud.

Impostazione Ultralytics YOLO con ROS

Questa guida √® stata testata utilizzando questo ambiente ROS, che √® un fork del repository ROSbot ROS. Questo ambiente include il pacchetto Ultralytics YOLO , un contenitore Docker per una facile configurazione, pacchetti ROS completi e mondi Gazebo per test rapidi. √ą stato progettato per funzionare con Husarion ROSbot 2 PRO. Gli esempi di codice forniti funzionano in qualsiasi ambiente ROS Noetic/Melodic, sia di simulazione che reale.

Husarion ROSbot 2 PRO

Installazione delle dipendenze

Oltre all'ambiente ROS, sarà necessario installare le seguenti dipendenze:

  • Pacchetto ROS Numpy: √ą necessario per la conversione rapida tra i messaggi ROS Image e gli array numpy.

    pip install ros_numpy
    
  • Ultralytics pacchetto:

    pip install ultralytics
    

Usare Ultralytics con ROS sensor_msgs/Image

Il sensor_msgs/Image tipo di messaggio è comunemente utilizzato in ROS per rappresentare i dati delle immagini. Contiene campi per la codifica, l'altezza, la larghezza e i dati dei pixel, che lo rendono adatto alla trasmissione di immagini catturate da telecamere o altri sensori. I messaggi immagine sono ampiamente utilizzati nelle applicazioni robotiche per compiti quali la percezione visiva, il rilevamento di oggetti e la navigazione.

Rilevamento e segmentazione in ROS Gazebo

Utilizzo passo-passo dell'immagine

Il seguente frammento di codice dimostra come utilizzare il pacchetto Ultralytics YOLO con ROS. In questo esempio, ci abboniamo all'argomento di una telecamera, elaboriamo l'immagine in arrivo utilizzando YOLO e pubblichiamo gli oggetti rilevati in nuovi argomenti per il rilevamento e la segmentazione.

Per prima cosa, importa le librerie necessarie e istanzia due modelli: uno per segmentazione e uno per rilevamento. Inizializza un nodo ROS (con il nome di ultralytics) per abilitare la comunicazione con il master ROS. Per garantire una connessione stabile, includiamo una breve pausa, dando al nodo il tempo sufficiente per stabilire la connessione prima di procedere.

import time

import rospy

from ultralytics import YOLO

detection_model = YOLO("yolov8m.pt")
segmentation_model = YOLO("yolov8m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)

Inizializza due argomenti ROS: uno per rilevamento e uno per segmentazione. Questi argomenti verranno utilizzati per pubblicare le immagini annotate, rendendole accessibili per ulteriori elaborazioni. La comunicazione tra i nodi è facilitata dall'utilizzo di sensor_msgs/Image Messaggi.

from sensor_msgs.msg import Image

det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)

Infine, creare un sottoscrittore che ascolti i messaggi sul /camera/color/image_raw e chiama una funzione di callback per ogni nuovo messaggio. Questa funzione di callback riceve messaggi di tipo sensor_msgs/Image, li converte in un array numpy usando ros_numpy, elabora le immagini con l'oggetto precedentemente istanziato YOLO modella, annota le immagini e quindi le pubblica di nuovo nei rispettivi argomenti: /ultralytics/detection/image per il rilevamento e la /ultralytics/segmentation/image per la segmentazione.

import ros_numpy


def callback(data):
    """Callback function to process image and publish annotated images."""
    array = ros_numpy.numpify(data)
    if det_image_pub.get_num_connections():
        det_result = detection_model(array)
        det_annotated = det_result[0].plot(show=False)
        det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))

    if seg_image_pub.get_num_connections():
        seg_result = segmentation_model(array)
        seg_annotated = seg_result[0].plot(show=False)
        seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))


rospy.Subscriber("/camera/color/image_raw", Image, callback)

while True:
    rospy.spin()
Codice completo
import time

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolov8m.pt")
segmentation_model = YOLO("yolov8m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)

det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)


def callback(data):
    """Callback function to process image and publish annotated images."""
    array = ros_numpy.numpify(data)
    if det_image_pub.get_num_connections():
        det_result = detection_model(array)
        det_annotated = det_result[0].plot(show=False)
        det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))

    if seg_image_pub.get_num_connections():
        seg_result = segmentation_model(array)
        seg_annotated = seg_result[0].plot(show=False)
        seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))


rospy.Subscriber("/camera/color/image_raw", Image, callback)

while True:
    rospy.spin()
Debug

Il debug dei nodi ROS (Robot Operating System) può essere impegnativo a causa della natura distribuita del sistema. Diversi strumenti possono aiutare in questo processo:

  1. rostopic echo <TOPIC-NAME> : questo comando consente di visualizzare i messaggi pubblicati su un argomento specifico, consentendo di esaminare il flusso di dati.
  2. rostopic list: Utilizzare questo comando per elencare tutti gli argomenti disponibili nel sistema ROS, fornendo una panoramica dei flussi di dati attivi.
  3. rqt_graph: questo strumento di visualizzazione visualizza il grafico di comunicazione tra i nodi, fornendo informazioni dettagliate su come i nodi sono interconnessi e su come interagiscono.
  4. Per visualizzazioni pi√Ļ complesse, come ad esempio le rappresentazioni in 3D, puoi utilizzare RViz. RViz (ROS Visualization) √® un potente strumento di visualizzazione 3D per ROS. Ti permette di visualizzare lo stato del tuo robot e del suo ambiente in tempo reale. Con RViz puoi visualizzare i dati dei sensori (ad es. sensors_msgs/Image), gli stati del modello di robot e vari altri tipi di informazioni, semplificando il debug e la comprensione del comportamento del sistema robotico.

Pubblicare le classi rilevate con std_msgs/String

I messaggi ROS standard includono anche std_msgs/String Messaggi. In molte applicazioni, non √® necessario ripubblicare l'intera immagine annotata; Al contrario, sono necessarie solo le classi presenti nella vista del robot. Nell'esempio seguente viene illustrato come utilizzare std_msgs/String messaggi per ripubblicare le classi rilevate sul sito /ultralytics/detection/classes argomento. Questi messaggi sono pi√Ļ leggeri e forniscono informazioni essenziali, rendendoli preziosi per varie applicazioni.

Esempio di caso d'uso

Consideriamo un robot da magazzino dotato di una telecamera e di un oggetto modello di rilevamento. Invece di inviare in rete immagini annotate di grandi dimensioni, il robot può pubblicare un elenco di classi rilevate come std_msgs/String Messaggi. Ad esempio, quando il robot rileva oggetti come "scatola", "pallet" e "carrello elevatore", pubblica queste classi nel file /ultralytics/detection/classes argomento. Queste informazioni possono quindi essere utilizzate da un sistema di monitoraggio centrale per tracciare l'inventario in tempo reale, ottimizzare la pianificazione del percorso del robot per evitare ostacoli o attivare azioni specifiche come il prelievo di una scatola rilevata. Questo approccio riduce la larghezza di banda necessaria per la comunicazione e si concentra sulla trasmissione di dati critici.

Utilizzo dettagliato delle stringhe

In questo esempio viene illustrato come utilizzare il metodo Ultralytics YOLO pacchetto con ROS. In questo esempio, ci sottoscriviamo a un argomento della fotocamera, elaboriamo l'immagine in arrivo usando YOLOe pubblicare gli oggetti rilevati in un nuovo argomento /ultralytics/detection/classes Utilizzando std_msgs/String Messaggi. Le ros_numpy viene utilizzato per convertire il messaggio ROS Image in un array numpy per l'elaborazione con YOLO.

import time

import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String

from ultralytics import YOLO

detection_model = YOLO("yolov8m.pt")
rospy.init_node("ultralytics")
time.sleep(1)
classes_pub = rospy.Publisher("/ultralytics/detection/classes", String, queue_size=5)


def callback(data):
    """Callback function to process image and publish detected classes."""
    array = ros_numpy.numpify(data)
    if classes_pub.get_num_connections():
        det_result = detection_model(array)
        classes = det_result[0].boxes.cls.cpu().numpy().astype(int)
        names = [det_result[0].names[i] for i in classes]
        classes_pub.publish(String(data=str(names)))


rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
    rospy.spin()

Usare Ultralytics con ROS Depth Images

Oltre alle immagini RGB, ROS supporta le immagini di profondità, che forniscono informazioni sulla distanza degli oggetti dalla telecamera. Le immagini di profondità sono fondamentali per le applicazioni robotiche come l'evitamento degli ostacoli, la mappatura 3D e la localizzazione.

Un'immagine di profondità è un'immagine in cui ogni pixel rappresenta la distanza tra la fotocamera e un oggetto. A differenza delle immagini RGB che catturano il colore, le immagini di profondità acquisiscono informazioni spaziali, consentendo ai robot di percepire la struttura 3D del loro ambiente.

Ottenimento di immagini di profondità

Le immagini di profondità possono essere ottenute utilizzando vari sensori:

  1. Telecamere stereo: Utilizza due telecamere per calcolare la profondità in base alla disparità delle immagini.
  2. Telecamere a tempo di volo (ToF): Misurano il tempo che la luce impiega per tornare da un oggetto.
  3. Sensori a luce strutturata: Proietta un modello e misura la sua deformazione sulle superfici.

Utilizzando YOLO con immagini di profondità

In ROS, le immagini di profondit√† sono rappresentate dal sensor_msgs/Image tipo di messaggio, che include i campi per la codifica, l'altezza, la larghezza e i dati pixel. Il campo di codifica per le immagini di profondit√† utilizza spesso un formato come "16UC1", che indica un numero intero senza segno a 16 bit per pixel, in cui ogni valore rappresenta la distanza dall'oggetto. Le immagini di profondit√† sono comunemente utilizzate in combinazione con le immagini RGB per fornire una visione pi√Ļ completa dell'ambiente.

Utilizzando YOLO, è possibile estrarre e combinare informazioni sia da immagini RGB che da immagini di profondità. Per esempio YOLO è in grado di rilevare gli oggetti all'interno di un'immagine RGB e questo rilevamento può essere utilizzato per individuare le regioni corrispondenti nell'immagine di profondità. Ciò consente l'estrazione di informazioni precise sulla profondità degli oggetti rilevati, migliorando la capacità del robot di comprendere l'ambiente circostante in tre dimensioni.

Telecamere RGB-D

Quando si lavora con le immagini di profondit√†, √® essenziale assicurarsi che le immagini RGB e di profondit√† siano allineate correttamente. Le telecamere RGB-D, come la serie RealSense di Intel , forniscono immagini RGB e di profondit√† sincronizzate, rendendo pi√Ļ semplice la combinazione delle informazioni provenienti da entrambe le fonti. Se si utilizzano telecamere RGB e di profondit√† separate, √® fondamentale calibrarle per garantire un allineamento preciso.

Utilizzo passo-passo della profondità

In questo esempio, usiamo YOLO per segmentare un'immagine e applicare la maschera estratta per segmentare l'oggetto nell'immagine di profondità. Questo ci permette di determinare la distanza di ogni pixel dell'oggetto di interesse dal centro focale della fotocamera. Ottenendo queste informazioni sulla distanza, possiamo calcolare la distanza tra la telecamera e l'oggetto specifico nella scena. Iniziare importando le librerie necessarie, creando un nodo ROS e creando un'istanza di un modello di segmentazione e di un argomento ROS.

import time

import rospy
from std_msgs.msg import String

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)

segmentation_model = YOLO("yolov8m-seg.pt")

classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)

Definire quindi una funzione di callback che elabori il messaggio dell'immagine di profondità in ingresso. La funzione attende i messaggi relativi all'immagine di profondità e all'immagine RGB, li converte in array numpy e applica il modello di segmentazione all'immagine RGB. Estrae quindi la maschera di segmentazione per ogni oggetto rilevato e calcola la distanza media dell'oggetto dalla telecamera utilizzando l'immagine di profondità. La maggior parte dei sensori ha una distanza massima, nota come distanza di ritaglio, oltre la quale i valori sono rappresentati come inf (np.inf). Prima dell'elaborazione, è importante filtrare questi valori Null e assegnare loro un valore di 0. Infine, pubblica gli oggetti rilevati insieme alle loro distanze medie dal /ultralytics/detection/distance argomento.

import numpy as np
import ros_numpy
from sensor_msgs.msg import Image


def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf

    classes_pub.publish(String(data=str(all_objects)))


rospy.Subscriber("/camera/depth/image_raw", Image, callback)

while True:
    rospy.spin()
Codice completo
import time

import numpy as np
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)

segmentation_model = YOLO("yolov8m-seg.pt")

classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)


def callback(data):
    """Callback function to process depth image and RGB image."""
    image = rospy.wait_for_message("/camera/color/image_raw", Image)
    image = ros_numpy.numpify(image)
    depth = ros_numpy.numpify(data)
    result = segmentation_model(image)

    for index, cls in enumerate(result[0].boxes.cls):
        class_index = int(cls.cpu().numpy())
        name = result[0].names[class_index]
        mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
        obj = depth[mask == 1]
        obj = obj[~np.isnan(obj)]
        avg_distance = np.mean(obj) if len(obj) else np.inf

    classes_pub.publish(String(data=str(all_objects)))


rospy.Subscriber("/camera/depth/image_raw", Image, callback)

while True:
    rospy.spin()

Usare Ultralytics con ROS sensor_msgs/PointCloud2

Rilevamento e segmentazione in ROS Gazebo

Il sensor_msgs/PointCloud2 tipo di messaggio è una struttura di dati utilizzata in ROS per rappresentare i dati delle nuvole di punti 3D. Questo tipo di messaggio è parte integrante delle applicazioni robotiche e consente di svolgere attività come la mappatura 3D, il riconoscimento degli oggetti e la localizzazione.

Una nuvola di punti è una raccolta di punti dati definiti all'interno di un sistema di coordinate tridimensionale. Questi punti dati rappresentano la superficie esterna di un oggetto o di una scena, catturata tramite tecnologie di scansione 3D. Ogni punto nella nuvola ha X, Y, e Z coordinate, che corrispondono alla sua posizione nello spazio e possono anche includere informazioni aggiuntive come il colore e l'intensità.

Quadro di riferimento

Quando si lavora con sensor_msgs/PointCloud2, √® fondamentale considerare il sistema di riferimento del sensore da cui sono stati acquisiti i dati della nuvola di punti. La nuvola di punti viene inizialmente catturata nel sistema di riferimento del sensore. √ą possibile determinare questo sistema di riferimento ascoltando il /tf_static argomento. Tuttavia, a seconda dei requisiti specifici dell'applicazione, potrebbe essere necessario convertire la nuvola di punti in un altro sistema di riferimento. Questa trasformazione pu√≤ essere ottenuta utilizzando il metodo tf2_ros , che fornisce strumenti per la gestione dei frame di coordinate e la trasformazione dei dati tra di essi.

Ottenimento di nuvole di punti

Le nuvole di punti possono essere ottenute utilizzando vari sensori:

  1. LIDAR (Light Detection and Ranging): Utilizza impulsi laser per misurare le distanze degli oggetti e creare mappe 3D di alta precisione.
  2. Telecamere di profondità: Acquisiscono informazioni sulla profondità per ogni pixel, consentendo la ricostruzione 3D della scena.
  3. Telecamere stereo: Utilizza due o pi√Ļ telecamere per ottenere informazioni sulla profondit√† attraverso la triangolazione.
  4. Scanner a luce strutturata: Proiettano un modello noto su una superficie e misurano la deformazione per calcolare la profondità.

Utilizzando YOLO con nuvole di punti

Per integrare YOLO con sensor_msgs/PointCloud2 digitare messaggi, possiamo utilizzare un metodo simile a quello utilizzato per le mappe di profondità. Sfruttando le informazioni sul colore incorporate nella nuvola di punti, possiamo estrarre un'immagine 2D, eseguire la segmentazione su questa immagine utilizzando YOLO, quindi applicare la maschera risultante ai punti tridimensionali per isolare l'oggetto 3D di interesse.

Per la gestione delle nuvole di punti, si consiglia di utilizzare Open3D (pip install open3d), un sistema di facile utilizzo Python biblioteca. Open3D fornisce strumenti robusti per la gestione delle strutture di dati delle nuvole di punti, la loro visualizzazione e l'esecuzione di operazioni complesse senza soluzione di continuità. Questa libreria può semplificare significativamente il processo e migliorare la nostra capacità di manipolare e analizzare le nuvole di punti in combinazione con YOLO-segmentazione basata su .

Utilizzo passo dopo passo delle nuvole di punti

Importare le librerie necessarie e creare un'istanza del file YOLO modello per la segmentazione.

import time

import rospy

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolov8m-seg.pt")

Creare una funzione pointcloud2_to_array, che trasforma un sensor_msgs/PointCloud2 messaggio in due array numpy. Le sensor_msgs/PointCloud2 i messaggi contengono n punti in base al width e height dell'immagine acquisita. Ad esempio, un 480 x 640 l'immagine avrà 307,200 scambio ferroviario. Ogni punto include tre coordinate spaziali (xyz) e il colore corrispondente in RGB formato. Questi possono essere considerati come due canali separati di informazione.

La funzione restituisce il metodo xyz coordinate e RGB valori nel formato della risoluzione originale della fotocamera (width x height). La maggior parte dei sensori ha una distanza massima, nota come distanza di ritaglio, oltre la quale i valori sono rappresentati come inf (np.inf). Prima dell'elaborazione, è importante filtrare questi valori Null e assegnare loro un valore di 0.

import numpy as np
import ros_numpy


def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
    """
    Convert a ROS PointCloud2 message to a numpy array.

    Args:
        pointcloud2 (PointCloud2): the PointCloud2 message

    Returns:
        (tuple): tuple containing (xyz, rgb)
    """
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    nan_rows = np.isnan(xyz).all(axis=2)
    xyz[nan_rows] = [0, 0, 0]
    rgb[nan_rows] = [0, 0, 0]
    return xyz, rgb

Successivamente, iscriviti al /camera/depth/points per ricevere il messaggio della nuvola di punti e convertire il sensor_msgs/PointCloud2 messaggio in array numpy contenenti le coordinate XYZ e i valori RGB (usando il metodo pointcloud2_to_array funzione). Elaborare l'immagine RGB utilizzando il comando YOLO modello per estrarre oggetti segmentati. Per ogni oggetto rilevato, estrai la maschera di segmentazione e applicala sia all'immagine RGB che alle coordinate XYZ per isolare l'oggetto nello spazio 3D.

L'elaborazione della maschera è semplice poiché è costituita da valori binari, con 1 indicando la presenza dell'oggetto e 0 indicando l'assenza. Per applicare la maschera, è sufficiente moltiplicare i canali originali per la maschera. Questa operazione isola efficacemente l'oggetto di interesse all'interno dell'immagine. Infine, creare un oggetto nuvola di punti Open3D e visualizzare l'oggetto segmentato nello spazio 3D con i colori associati.

import sys

import open3d as o3d

ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])
Codice completo
import sys
import time

import numpy as np
import open3d as o3d
import ros_numpy
import rospy

from ultralytics import YOLO

rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolov8m-seg.pt")


def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
    """
    Convert a ROS PointCloud2 message to a numpy array.

    Args:
        pointcloud2 (PointCloud2): the PointCloud2 message

    Returns:
        (tuple): tuple containing (xyz, rgb)
    """
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    nan_rows = np.isnan(xyz).all(axis=2)
    xyz[nan_rows] = [0, 0, 0]
    rgb[nan_rows] = [0, 0, 0]
    return xyz, rgb


ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])

Segmentazione della nuvola di punti con Ultralytics

DOMANDE FREQUENTI

Che cos'è il Robot Operating System (ROS)?

Il Robot Operating System (ROS) √® un framework open-source comunemente utilizzato nella robotica per aiutare gli sviluppatori a creare applicazioni robotiche robuste. Fornisce una raccolta di librerie e strumenti per la costruzione e l'interfacciamento con i sistemi robotici, consentendo uno sviluppo pi√Ļ semplice di applicazioni complesse. ROS supporta la comunicazione tra i nodi utilizzando messaggi su argomenti o servizi.

Come posso integrare Ultralytics YOLO con ROS per il rilevamento di oggetti in tempo reale?

L'integrazione di Ultralytics YOLO con ROS comporta la creazione di un ambiente ROS e l'utilizzo di YOLO per l'elaborazione dei dati dei sensori. Inizia installando le dipendenze necessarie, come ad esempio ros_numpy e Ultralytics YOLO :

pip install ros_numpy ultralytics

Quindi, crea un nodo ROS e iscriviti a un topic sulle immagini per elaborare i dati in arrivo. Ecco un esempio minimo:

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolov8m.pt")
rospy.init_node("ultralytics")
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)


def callback(data):
    array = ros_numpy.numpify(data)
    det_result = detection_model(array)
    det_annotated = det_result[0].plot(show=False)
    det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))


rospy.Subscriber("/camera/color/image_raw", Image, callback)
rospy.spin()

Cosa sono gli argomenti ROS e come vengono utilizzati in Ultralytics YOLO ?

Gli argomenti di ROS facilitano la comunicazione tra i nodi di una rete ROS utilizzando un modello publish-subscribe. Un argomento è un canale denominato che i nodi utilizzano per inviare e ricevere messaggi in modo asincrono. Nel contesto di Ultralytics YOLO , puoi fare in modo che un nodo si iscriva a un topic di immagini, elabori le immagini utilizzando YOLO per attività come il rilevamento o la segmentazione e pubblichi i risultati a nuovi topic.

Ad esempio, sottoscrivere l'argomento di una telecamera ed elaborare l'immagine in arrivo per il rilevamento:

rospy.Subscriber("/camera/color/image_raw", Image, callback)

Perché utilizzare le immagini di profondità con Ultralytics YOLO in ROS?

Immagini di profondità in ROS, rappresentate da sensor_msgs/Imageforniscono la distanza degli oggetti dalla telecamera, fondamentale per attività come l'evitamento degli ostacoli, la mappatura 3D e la localizzazione. Da utilizzando le informazioni sulla profondità Insieme alle immagini RGB, i robot possono comprendere meglio il loro ambiente 3D.

Con YOLO, puoi estrarre maschere di segmentazione dalle immagini RGB e applicare queste maschere alle immagini di profondità per ottenere informazioni precise sugli oggetti 3D, migliorando la capacità del robot di navigare e interagire con l'ambiente circostante.

Come posso visualizzare le nuvole di punti 3D con YOLO in ROS?

Per visualizzare le nuvole di punti 3D in ROS con YOLO:

  1. Convertire sensor_msgs/PointCloud2 in array numpy.
  2. Usa YOLO per segmentare le immagini RGB.
  3. Applica la maschera di segmentazione alla nuvola di punti.

Ecco un esempio che utilizza Open3D per la visualizzazione:

import sys

import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2

from ultralytics import YOLO

rospy.init_node("ultralytics")
segmentation_model = YOLO("yolov8m-seg.pt")


def pointcloud2_to_array(pointcloud2):
    pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
    split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
    rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
    xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
    xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
    return xyz, rgb


ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)

if not len(result[0].boxes.cls):
    print("No objects detected")
    sys.exit()

classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
    mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
    mask_expanded = np.stack([mask, mask, mask], axis=2)

    obj_rgb = rgb * mask_expanded
    obj_xyz = xyz * mask_expanded

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((-1, 3)))
    pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((-1, 3)) / 255)
    o3d.visualization.draw_geometries([pcd])

Questo approccio fornisce una visualizzazione 3D degli oggetti segmentati, utile per attività come la navigazione e la manipolazione.



Creato 2024-06-19, Aggiornato 2024-07-05
Autori: glenn-jocher (2), RizwanMunawar (1), ambitious-octopus (3)

Commenti