Skip to content

Guide de démarrage rapide ROS (Robot Operating System)

ROS Introduction (captioned) from Open Robotics on Vimeo.

Qu’est-ce que ROS ?

Le Robot Operating System (ROS) est un framework open-source largement utilisé dans la recherche et l'industrie de la robotique. ROS fournit une collection de bibliothèques et d'outils pour aider les développeurs à créer des applications robotiques. ROS est conçu pour fonctionner avec diverses plateformes robotiques, ce qui en fait un outil flexible et puissant pour les roboticiens.

Principales caractéristiques de ROS

  1. Architecture modulaire: ROS possède une architecture modulaire, permettant aux développeurs de construire des systèmes complexes en combinant des composants plus petits et réutilisables appelés nœuds. Chaque nœud exécute généralement une fonction spécifique, et les nœuds communiquent entre eux à l'aide de messages sur des sujets ou des services.

  2. Logiciel intermédiaire de communication: ROS offre une infrastructure de communication robuste qui prend en charge la communication inter-processus et l'informatique distribuée. Ceci est réalisé grâce à un modèle de publication-abonnement pour les flux de données (sujets) et un modèle de demande-réponse pour les appels de service.

  3. Abstraction du matériel: ROS fournit une couche d'abstraction sur le matériel, ce qui permet aux développeurs d'écrire du code agnostique par rapport aux appareils. Cela permet d'utiliser le même code avec différentes configurations matérielles, ce qui facilite l'intégration et l'expérimentation.

  4. Outils et utilitaires: ROS est livré avec un riche ensemble d'outils et d'utilitaires pour la visualisation, le débogage et la simulation. Par exemple, RViz est utilisé pour visualiser les données des capteurs et les informations sur l'état du robot, tandis que Gazebo fournit un environnement de simulation puissant pour tester les algorithmes et les conceptions de robots.

  5. Un écosystème étendu: L'écosystème ROS est vaste et se développe continuellement, avec de nombreux paquets disponibles pour différentes applications robotiques, notamment la navigation, la manipulation, la perception, et bien plus encore. La communauté contribue activement au développement et à la maintenance de ces paquets.

Évolution des versions de ROS

Depuis son développement en 2007, ROS a évolué à travers de multiples versions, chacune introduisant de nouvelles fonctionnalités et améliorations pour répondre aux besoins croissants de la communauté robotique. Le développement de ROS peut être catégorisé en deux séries principales : ROS 1 et ROS 2. Ce guide se concentre sur la version Long Term Support (LTS) de ROS 1, connue sous le nom de ROS Noetic Ninjemys, le code devrait également fonctionner avec les versions antérieures.

ROS 1 contre ROS 2

Alors que ROS 1 a fourni une base solide pour le développement robotique, ROS 2 comble ses lacunes en offrant :

  • Performance en temps rĂ©el: Prise en charge amĂ©liorĂ©e des systèmes en temps rĂ©el et du comportement dĂ©terministe.
  • SĂ©curitĂ©: Fonctions de sĂ©curitĂ© amĂ©liorĂ©es pour un fonctionnement sĂ»r et fiable dans divers environnements.
  • ÉvolutivitĂ©: Meilleure prise en charge des systèmes multi-robots et des dĂ©ploiements Ă  grande Ă©chelle.
  • Prise en charge multiplateforme: CompatibilitĂ© Ă©largie avec divers systèmes d'exploitation au-delĂ  de Linux, notamment Windows et macOS.
  • Communication flexible: Utilisation de DDS pour une communication inter-processus plus souple et plus efficace.

Messages et rubriques ROS

Dans ROS, la communication entre les nœuds est facilitée par les messages et les sujets. Un message est une structure de données qui définit les informations échangées entre les nœuds, tandis qu'un sujet est un canal nommé sur lequel les messages sont envoyés et reçus. Les nœuds peuvent publier des messages vers un sujet ou s'abonner à des messages provenant d'un sujet, ce qui leur permet de communiquer entre eux. Ce modèle de publication et d'abonnement permet une communication asynchrone et un découplage entre les nœuds. Chaque capteur ou actionneur d'un système robotique publie généralement des données vers un sujet, qui peuvent ensuite être consommées par d'autres nœuds à des fins de traitement ou de contrôle. Dans le cadre de ce guide, nous nous concentrerons sur les messages Image, Profondeur et PointCloud ainsi que sur les sujets des caméras.

Configuration Ultralytics YOLO avec ROS

Ce guide a été testé en utilisant cet environnement ROS, qui est une fourche du dépôt ROSbot ROS. Cet environnement comprend le paquet Ultralytics YOLO , un conteneur Docker pour une installation facile, des paquets ROS complets et des mondes Gazebo pour des tests rapides. Il est conçu pour fonctionner avec le Husarion ROSbot 2 PRO. Les exemples de code fournis fonctionneront dans n'importe quel environnement ROS Noetic/Melodic, y compris la simulation et le monde réel.

Husarion ROSbot 2 PRO

Installation des dépendances

Outre l’environnement ROS, vous devrez installer les dépendances suivantes :

  • Paquet ROS Numpy: Il est nĂ©cessaire pour une conversion rapide entre les messages ROS Image et les tableaux numpy.

    pip install ros_numpy
    
  • Ultralytics paquet:

    pip install ultralytics
    

Utiliser Ultralytics avec ROS sensor_msgs/Image

Le sensor_msgs/Image type de message est couramment utilisé dans ROS pour représenter les données d'image. Il contient des champs pour l'encodage, la hauteur, la largeur et les données relatives aux pixels, ce qui le rend adapté à la transmission d'images capturées par des caméras ou d'autres capteurs. Les messages d'image sont largement utilisés dans les applications robotiques pour des tâches telles que la perception visuelle, la détection d'objets et la navigation.

DĂ©tection et segmentation dans ROS Gazebo

Utilisation étape par étape de l’image

L'extrait de code suivant montre comment utiliser le paquet Ultralytics YOLO avec ROS. Dans cet exemple, nous nous abonnons à un sujet de caméra, nous traitons l'image entrante à l'aide de YOLO, et nous publions les objets détectés dans de nouveaux sujets pour la détection et la segmentation.

Tout d'abord, importe les bibliothèques nécessaires et instancie deux modèles : l'un pour les segmentation et un pour détection. Initialise un nœud ROS (avec le nom ultralytics) pour permettre la communication avec le maître ROS. Pour assurer une connexion stable, nous incluons une brève pause, donnant au nœud suffisamment de temps pour établir la connexion avant de continuer.

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)

Initialise deux sujets ROS : un pour détection et un pour segmentation. Ces sujets seront utilisés pour publier les images annotées, ce qui les rendra accessibles pour un traitement ultérieur. La communication entre les nœuds est facilitée par l'utilisation de sensor_msgs/Image Messages.

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)

Enfin, créez un abonné qui écoute les messages sur le /camera/color/image_raw topic et appelle une fonction de rappel pour chaque nouveau message. Cette fonction de rappel reçoit des messages de type sensor_msgs/Image, les convertit en un tableau numpy à l’aide de ros_numpy, traite les images avec les images précédemment instanciées YOLO modélise, annote les images, puis les publie dans les rubriques respectives : /ultralytics/detection/image pour la détection et /ultralytics/segmentation/image pour la segmentation.

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()
Code complet
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()
DĂ©bogage

Le débogage des nœuds ROS (Robot Operating System) peut être difficile en raison de la nature distribuée du système. Plusieurs outils peuvent vous aider dans ce processus :

  1. rostopic echo <TOPIC-NAME> : Cette commande vous permet d’afficher les messages publiés sur un sujet spécifique, ce qui vous aide à inspecter le flux de données.
  2. rostopic list: Utilisez cette commande pour lister toutes les rubriques disponibles dans le système ROS, ce qui vous donne une vue d’ensemble des flux de données actifs.
  3. rqt_graph: Cet outil de visualisation affiche le graphique de communication entre les nœuds, fournissant des informations sur la façon dont les nœuds sont interconnectés et comment ils interagissent.
  4. Pour des visualisations plus complexes, telles que des représentations en 3D, tu peux utiliser RViz. RViz (ROS Visualization) est un puissant outil de visualisation 3D pour ROS. Il te permet de visualiser l'état de ton robot et de son environnement en temps réel. Avec RViz, tu peux visualiser les données des capteurs (par ex. sensors_msgs/Image), les états du modèle du robot et divers autres types d’informations, ce qui facilite le débogage et la compréhension du comportement de votre système robotique.

Publier les classes détectées avec std_msgs/String

Les messages ROS standard incluent également std_msgs/String Messages. Dans de nombreuses applications, il n’est pas nécessaire de republier l’intégralité de l’image annotée ; au lieu de cela, seules les classes présentes dans la vue du robot sont nécessaires. L’exemple suivant montre comment utiliser std_msgs/String messages pour republier les classes détectées sur le site /ultralytics/detection/classes sujet. Ces messages sont plus légers et fournissent des informations essentielles, ce qui les rend précieux pour diverses applications.

Exemple de cas d’utilisation

Considère un robot d'entrepôt équipé d'une caméra et d'un objet. modèle de détection. Au lieu d'envoyer de grandes images annotées sur le réseau, le robot peut publier une liste des classes détectées en tant que std_msgs/String Messages. Par exemple, lorsque le robot détecte des objets tels que « boîte », « palette » et « chariot élévateur », il publie ces classes dans le fichier /ultralytics/detection/classes sujet. Ces informations peuvent ensuite être utilisées par un système de surveillance central pour suivre l’inventaire en temps réel, optimiser la planification de la trajectoire du robot pour éviter les obstacles ou déclencher des actions spécifiques telles que la prise d’une boîte détectée. Cette approche réduit la bande passante requise pour la communication et se concentre sur la transmission de données critiques.

Utilisation étape par étape de la chaîne

Cet exemple montre comment utiliser l’attribut Ultralytics YOLO avec ROS. Dans cet exemple, nous nous abonnons à une rubrique de caméra, traitons l’image entrante à l’aide de YOLOet publier les objets détectés dans une nouvelle rubrique /ultralytics/detection/classes Utilisant std_msgs/String Messages. Le ros_numpy est utilisé pour convertir le message ROS Image en un tableau numpy pour le traitement avec 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()

Utiliser Ultralytics avec ROS Depth Images

En plus des images RVB, ROS prend en charge les images de profondeur, qui fournissent des informations sur la distance des objets par rapport à la caméra. Les images de profondeur sont cruciales pour les applications robotiques telles que l'évitement d'obstacles, la cartographie 3D et la localisation.

Une image de profondeur est une image où chaque pixel représente la distance entre la caméra et un objet. Contrairement aux images RVB qui capturent la couleur, les images de profondeur capturent des informations spatiales, permettant aux robots de percevoir la structure 3D de leur environnement.

Obtention d’images en profondeur

Les images de profondeur peuvent être obtenues à l’aide de différents capteurs :

  1. Caméras stéréo: Utilise deux caméras pour calculer la profondeur en fonction de la disparité des images.
  2. Caméras à temps de vol (ToF): Mesurent le temps que met la lumière à revenir d'un objet.
  3. Capteurs de lumière structurée: Projette un motif et mesure sa déformation sur les surfaces.

Utilisant YOLO avec des images en profondeur

Dans ROS, les images de profondeur sont représentées par le sensor_msgs/Image type de message, qui comprend des champs pour les données d’encodage, de hauteur, de largeur et de pixels. Le champ d’encodage pour les images de profondeur utilise souvent un format tel que « 16UC1 », indiquant un entier non signé de 16 bits par pixel, où chaque valeur représente la distance à l’objet. Les images de profondeur sont couramment utilisées en conjonction avec les images RVB pour fournir une vue plus complète de l’environnement.

Utilisant YOLO, il est possible d’extraire et de combiner des informations à partir d’images RVB et de profondeur. Par exemple YOLO peut détecter des objets dans une image RVB, et cette détection peut être utilisée pour localiser les régions correspondantes dans l’image de profondeur. Cela permet d’extraire des informations précises sur la profondeur des objets détectés, améliorant ainsi la capacité du robot à comprendre son environnement en trois dimensions.

Caméras RVB-D

Lorsque l'on travaille avec des images de profondeur, il est essentiel de s'assurer que les images RVB et de profondeur sont correctement alignées. Les caméras RVB-D, telles que la série RealSense de Intel , fournissent des images RVB et de profondeur synchronisées, ce qui facilite la combinaison des informations provenant des deux sources. Si tu utilises des caméras RVB et de profondeur séparées, il est essentiel de les calibrer pour garantir un alignement précis.

Utilisation Ă©tape par Ă©tape de la profondeur

Dans cet exemple, nous utilisons YOLO pour segmenter une image et appliquer le masque extrait pour segmenter l’objet dans l’image de profondeur. Cela nous permet de déterminer la distance de chaque pixel de l’objet d’intérêt par rapport au centre focal de la caméra. En obtenant ces informations de distance, nous pouvons calculer la distance entre la caméra et l’objet spécifique de la scène. Commencez par importer les bibliothèques nécessaires, créer un nœud ROS et instancier un modèle de segmentation et une rubrique 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)

Ensuite, définissez une fonction de rappel qui traite le message d’image de profondeur entrant. La fonction attend les messages de l’image de profondeur et de l’image RVB, les convertit en tableaux numériques et applique le modèle de segmentation à l’image RVB. Il extrait ensuite le masque de segmentation pour chaque objet détecté et calcule la distance moyenne de l’objet par rapport à la caméra à l’aide de l’image de profondeur. La plupart des capteurs ont une distance maximale, appelée distance de découpe, au-delà de laquelle les valeurs sont représentées par inf (np.inf). Avant le traitement, il est important de filtrer ces valeurs nulles et de leur attribuer une valeur de 0. Enfin, il publie les objets détectés ainsi que leurs distances moyennes par rapport à la /ultralytics/detection/distance sujet.

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()
Code complet
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()

Utiliser Ultralytics avec ROS sensor_msgs/PointCloud2

DĂ©tection et segmentation dans ROS Gazebo

Le sensor_msgs/PointCloud2 type de message est une structure de données utilisée dans ROS pour représenter les données des nuages de points en 3D. Ce type de message fait partie intégrante des applications robotiques, permettant d'effectuer des tâches telles que la cartographie 3D, la reconnaissance d'objets et la localisation.

Un nuage de points est un ensemble de points de données définis dans un système de coordonnées tridimensionnel. Ces points de données représentent la surface externe d’un objet ou d’une scène, capturée par les technologies de numérisation 3D. Chaque point du cloud a X, Yet Z , qui correspondent à sa position dans l’espace, et peuvent également inclure des informations supplémentaires telles que la couleur et l’intensité.

Cadre de référence

Lorsque vous travaillez avec sensor_msgs/PointCloud2, il est essentiel de prendre en compte le cadre de référence du capteur à partir duquel les données du nuage de points ont été acquises. Le nuage de points est initialement capturé dans le cadre de référence du capteur. Vous pouvez déterminer ce cadre de référence en écoutant le /tf_static sujet. Toutefois, en fonction des exigences spécifiques de votre application, vous devrez peut-être convertir le nuage de points en un autre référentiel. Cette transformation peut être réalisée à l’aide de l’outil tf2_ros , qui fournit des outils pour gérer les repères de coordonnées et transformer les données entre eux.

Obtenir des nuages de points

Les nuages de points peuvent être obtenus à l’aide de différents capteurs :

  1. LIDAR (Light Detection and Ranging): Utilise des impulsions laser pour mesurer les distances aux objets et créer des cartes 3D de haute précision.
  2. Caméras de profondeur: Captent les informations de profondeur pour chaque pixel, ce qui permet de reconstruire la scène en 3D.
  3. Caméras stéréo: Utilise deux caméras ou plus pour obtenir des informations sur la profondeur par triangulation.
  4. Scanners à lumière structurée: Projettent un motif connu sur une surface et mesurent la déformation pour calculer la profondeur.

Utilisant YOLO avec des nuages de points

Intégrer YOLO avec sensor_msgs/PointCloud2 , nous pouvons utiliser une méthode similaire à celle utilisée pour les cartes de profondeur. En exploitant les informations de couleur intégrées dans le nuage de points, nous pouvons extraire une image 2D, effectuer une segmentation sur cette image à l’aide de YOLO, puis appliquez le masque obtenu aux points tridimensionnels pour isoler l’objet 3D d’intérêt.

Pour la gestion des nuages de points, nous vous recommandons d’utiliser Open3D (pip install open3d), un Python bibliothèque. Open3D fournit des outils robustes pour gérer les structures de données de nuages de points, les visualiser et exécuter des opérations complexes de manière transparente. Cette bibliothèque peut simplifier considérablement le processus et améliorer notre capacité à manipuler et à analyser les nuages de points en conjonction avec YOLO.

Utilisation des nuages de points Ă©tape par Ă©tape

Importez les bibliothèques nécessaires et instanciez le YOLO modèle pour la segmentation.

import time

import rospy

from ultralytics import YOLO

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

Créer une fonction pointcloud2_to_array, qui transforme un sensor_msgs/PointCloud2 message dans deux tableaux numpy. Le sensor_msgs/PointCloud2 Les messages contiennent n en fonction de la width et height de l’image acquise. Par exemple, un 480 x 640 image aura 307,200 aiguillage. Chaque point comprend trois coordonnées spatiales (xyz) et la couleur correspondante dans RGB format. Ceux-ci peuvent être considérés comme deux canaux d’information distincts.

La fonction renvoie la fonction xyz coordonnées et RGB au format de la résolution d’origine de l’appareil photo (width x height). La plupart des capteurs ont une distance maximale, appelée distance de découpe, au-delà de laquelle les valeurs sont représentées par inf (np.inf). Avant le traitement, il est important de filtrer ces valeurs nulles et de leur attribuer une valeur de 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

Ensuite, abonnez-vous à la /camera/depth/points pour recevoir le message de nuage de points et convertir le sensor_msgs/PointCloud2 dans des tableaux numpy contenant les coordonnées XYZ et les valeurs RVB (en utilisant le paramètre pointcloud2_to_array fonction). Traitez l’image RVB à l’aide de la commande YOLO pour extraire des objets segmentés. Pour chaque objet détecté, extrayez le masque de segmentation et appliquez-le à la fois à l’image RVB et aux coordonnées XYZ pour isoler l’objet dans l’espace 3D.

Le traitement du masque est simple puisqu’il se compose de valeurs binaires, avec 1 indiquant la présence de l’objet et 0 indiquant l’absence. Pour appliquer le masque, il suffit de multiplier les canaux d’origine par le masque. Cette opération isole efficacement l’objet d’intérêt dans l’image. Enfin, créez un objet nuage de points Open3D et visualisez l’objet segmenté dans l’espace 3D avec les couleurs associées.

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])
Code complet
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])

Segmentation des nuages de points avec Ultralytics

FAQ

Qu'est-ce que le système d'exploitation des robots (ROS) ?

Le Robot Operating System (ROS) est un framework open-source couramment utilisé en robotique pour aider les développeurs à créer des applications robotiques robustes. Il fournit une collection de bibliothèques et d'outils pour construire et interfacer avec des systèmes robotiques, ce qui permet de faciliter le développement d'applications complexes. ROS prend en charge la communication entre les nœuds à l'aide de messages sur des sujets ou des services.

Comment intégrer Ultralytics YOLO à ROS pour la détection d'objets en temps réel ?

L'intégration de Ultralytics YOLO avec ROS implique la mise en place d'un environnement ROS et l'utilisation de YOLO pour le traitement des données des capteurs. Commence par installer les dépendances nécessaires comme ros_numpy et Ultralytics YOLO :

pip install ros_numpy ultralytics

Ensuite, crée un nœud ROS et abonne-toi à un sujet d'image pour traiter les données entrantes. Voici un exemple minimal :

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()

Que sont les sujets ROS et comment sont-ils utilisés dans Ultralytics YOLO ?

Les sujets ROS facilitent la communication entre les nœuds d'un réseau ROS en utilisant un modèle de publication-abonnement. Un sujet est un canal nommé que les nœuds utilisent pour envoyer et recevoir des messages de manière asynchrone. Dans le contexte de Ultralytics YOLO , tu peux faire en sorte qu'un nœud s'abonne à un sujet d'image, traite les images à l'aide de YOLO pour des tâches telles que la détection ou la segmentation, et publie les résultats dans de nouveaux sujets.

Par exemple, s'abonner à un sujet sur la caméra et traiter l'image entrante pour la détecter :

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

Pourquoi utiliser des images de profondeur avec Ultralytics YOLO dans ROS ?

Images en profondeur dans ROS, représentées par sensor_msgs/ImageL'analyse de la distance entre les objets et la caméra est cruciale pour des tâches telles que l'évitement d'obstacles, la cartographie en 3D et la localisation. Par utiliser les informations sur la profondeur Avec les images RVB, les robots peuvent mieux comprendre leur environnement en 3D.

Avec YOLO, tu peux extraire des masques de segmentation des images RVB et appliquer ces masques aux images de profondeur pour obtenir des informations précises sur les objets en 3D, ce qui améliore la capacité du robot à naviguer et à interagir avec son environnement.

Comment puis-je visualiser des nuages de points 3D avec YOLO dans ROS ?

Pour visualiser des nuages de points 3D dans ROS avec YOLO:

  1. Convertir sensor_msgs/PointCloud2 des messages vers des tableaux numpy.
  2. Utilise YOLO pour segmenter les images RVB.
  3. Applique le masque de segmentation au nuage de points.

Voici un exemple utilisant Open3D pour la visualisation :

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])

Cette approche permet d'obtenir une visualisation en 3D des objets segmentés, utile pour des tâches telles que la navigation et la manipulation.



Créé le 2024-06-19, Mis à jour le 2024-07-05
Auteurs : glenn-jocher (2), RizwanMunawar (1), ambitious-octopus (3)

Commentaires