Skip to content

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

ROS Introduction (captioned) from Open Robotics on Vimeo.

Qu'est-ce que le ROS ?

Le Robot Operating System (ROS) est un cadre 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 différentes plateformes robotiques, ce qui en fait un outil flexible et puissant pour les roboticiens.

Principales caractéristiques de ROS

  1. Architecture modulaire: ROS a 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 thèmes ou des services.

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

  3. Abstraction matérielle: ROS fournit une couche d'abstraction sur le matériel, permettant aux développeurs d'écrire un code agnostique. Le même code peut ainsi être utilisé 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, y compris la navigation, la manipulation, la perception, et plus encore. La communauté contribue activement au développement et à la maintenance de ces packages.

É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 des 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 : 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, mais le code devrait également fonctionner avec les versions antérieures.

ROS 1 vs. ROS 2

Alors que ROS 1 offrait une base solide pour le développement de la robotique, ROS 2 comble ses lacunes en offrant.. :

  • Performance en temps rĂ©el: AmĂ©lioration de la prise en charge 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 autres que Linux, notamment Windows et macOS.
  • Communication flexible: Utilisation de DDS pour une communication inter-processus plus souple et plus efficace.

Messages et sujets 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 dans un thème, 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, Depth et PointCloud, ainsi que sur les thèmes des caméras.

Configuration de Ultralytics YOLO avec ROS

Ce guide a été testé en utilisant cet environnement ROS, qui est un fork 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, qu'il s'agisse d'une simulation ou d'un environnement 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 l'emballage:

    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 des données d'image. Il contient des champs pour l'encodage, la hauteur, la largeur et les données de 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, détection d'objetset la navigation.

DĂ©tection et segmentation dans ROS Gazebo

Image Utilisation pas Ă  pas

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

Tout d'abord, il convient d'importer les bibliothèques nécessaires et d'instancier deux modèles : l'un pour les segmentation et un pour détection. Initialiser 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)

Initialiser deux thèmes ROS : un pour détection et un pour segmentation. Ces thèmes seront utilisés pour publier les images annotées, les rendant ainsi accessibles pour un traitement ultérieur. La communication entre les nœuds est facilitée par l'utilisation de sensor_msgs/Image des 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 serveur /camera/color/image_raw et appelle une fonction de rappel pour chaque nouveau message. Cette fonction de rappel reçoit les messages de type sensor_msgs/Imageles convertit en un tableau numpy à l'aide de ros_numpytraite les images avec les modèles YOLO précédemment instanciés, annote les images, puis les publie dans les rubriques correspondantes : /ultralytics/detection/image pour la détection et la /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 s'avérer difficile en raison de la nature distribuée du système. Plusieurs outils peuvent faciliter 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 tous les sujets disponibles dans le système ROS, vous donnant 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, ce qui permet de comprendre comment les nœuds sont interconnectés et comment ils interagissent.
  4. Pour des visualisations plus complexes, telles que des représentations en 3D, vous pouvez utiliser RViz. RViz (ROS Visualization) est un puissant outil de visualisation 3D pour ROS. Il vous permet de visualiser l'état de votre robot et de son environnement en temps réel. Avec RViz, vous pouvez visualiser les données des capteurs (ex. sensors_msgs/Image), les états du modèle de 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 standard de ROS comprennent é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 ; 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 utiles pour diverses applications.

Exemple de cas d'utilisation

Considérons 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 la base de données /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 afin d'éviter les obstacles, ou déclencher des actions spécifiques telles que le ramassage d'une boîte détectée. Cette approche réduit la bande passante nécessaire à la communication et se concentre sur la transmission des données critiques.

Utilisation de la ficelle Ă©tape par Ă©tape

Cet exemple 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 en utilisant YOLO, et nous publions les objets détectés dans un nouveau sujet. /ultralytics/detection/classes en utilisant std_msgs/String des messages. Les 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 les images de profondeur de ROS

Outre les 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 essentielles pour les applications robotiques telles que l'évitement d'obstacles, la cartographie 3D et la localisation.

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

Obtention d'images de profondeur

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

  1. Caméras stéréo: Utiliser deux caméras pour calculer la profondeur sur la base 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: Projeter un motif et mesurer sa déformation sur des surfaces.

Utilisation de YOLO avec des images de profondeur

Dans ROS, les images de profondeur sont représentées par le sensor_msgs/Image qui comprend des champs pour le codage, la hauteur, la largeur et les données relatives aux pixels. Le champ d'encodage des 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 de 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.

En utilisant YOLO, il est possible d'extraire et de combiner des informations provenant à la fois d'images RVB et d'images 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 (Intel ), fournissent des images RVB et de profondeur synchronisées, ce qui facilite la combinaison des informations provenant des deux sources. Si vous utilisez des caméras RVB et des caméras de profondeur séparées, il est essentiel de les calibrer pour garantir un alignement précis.

Profondeur Utilisation pas Ă  pas

Dans cet exemple, nous utilisons YOLO pour segmenter une image et appliquons 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 dans la scène. Commencez par importer les bibliothèques nécessaires, créez un nœud ROS et instanciez un modèle de segmentation et un sujet 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 d'image de profondeur et d'image RVB, les convertit en tableaux numpy et applique le modèle de segmentation à l'image RVB. Elle 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, connue sous le nom de distance d'écrêtage, au-delà de laquelle les valeurs sont représentées comme 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 sur le site Web de l'organisation. /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 des données de nuages de points 3D. Ce type de message fait partie intégrante des applications robotiques, permettant 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 tridimensionnelles. Ces points de données représentent la surface externe d'un objet ou d'une scène, capturée par des technologies de numérisation 3D. Chaque point du nuage a X, Yet Z les coordonnées, 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 l'on travaille avec sensor_msgs/PointCloud2Il 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 la commande /tf_static sujet. Cependant, en fonction des exigences spécifiques de votre application, vous pouvez avoir besoin de convertir le nuage de points dans un autre cadre de référence. Cette transformation peut être réalisée à l'aide de la fonction tf2_ros qui fournit des outils pour gérer les cadres de coordonnées et transformer les données entre eux.

Obtention de 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 entre les objets et créer des cartes 3D de haute précision.
  2. Caméras de profondeur: Elles capturent des informations sur la profondeur pour chaque pixel, ce qui permet de reconstruire la scène en 3D.
  3. Caméras stéréo: L'utilisation de deux caméras ou plus permet d'obtenir des informations sur la profondeur par triangulation.
  4. Scanners à lumière structurée: Ils projettent un motif connu sur une surface et mesurent la déformation pour calculer la profondeur.

Utilisation de YOLO avec des nuages de points

Pour intégrer YOLO avec sensor_msgs/PointCloud2 Dans le cas des messages de type "point", 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 appliquer le masque résultant aux points tridimensionnels afin d'isoler l'objet 3D d'intérêt.

Pour traiter les nuages de points, nous recommandons d'utiliser Open3D (pip install open3d), une bibliothèque conviviale Python . Open3D fournit des outils robustes pour gérer les structures de données des 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 la segmentation basée sur YOLO.

Nuages de points Utilisation pas Ă  pas

Importez les bibliothèques nécessaires et instanciez le modèle YOLO 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_arrayqui transforme un sensor_msgs/PointCloud2 dans deux tableaux numpy. Les sensor_msgs/PointCloud2 les messages contiennent n points sur la base de la width et height de l'image acquise. Par exemple, un 480 x 640 l'image aura 307,200 les points. Chaque point comprend trois coordonnées spatiales (xyz) et la couleur correspondante dans RGB le format. Ils peuvent être considérés comme deux canaux d'information distincts.

La fonction renvoie le xyz coordonnées et RGB au format de la résolution originale de l'appareil photo (width x height). La plupart des capteurs ont une distance maximale, connue sous le nom de distance d'écrêtage, au-delà de laquelle les valeurs sont représentées comme 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, il faut s'abonner à l'application /camera/depth/points pour recevoir le message de nuage de points et convertir le fichier sensor_msgs/PointCloud2 en tableaux numpy contenant les coordonnées XYZ et les valeurs RGB (à l'aide de la fonction pointcloud2_to_array ). Traiter l'image RVB à l'aide du modèle YOLO pour extraire les objets segmentés. Pour chaque objet détecté, extraire le masque de segmentation et l'appliquer à 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 s'agit 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 originaux par le masque. Cette opération permet d'isoler 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 la construction et l'interfaçage avec des systèmes robotiques, facilitant ainsi 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 avec 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. Commencez par installer les dépendances nécessaires telles que ros_numpy et Ultralytics YOLO :

pip install ros_numpy ultralytics

Ensuite, créez un nœud ROS et abonnez-vous à un thème 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()

Quels sont les thèmes de 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 et d'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 , vous pouvez faire en sorte qu'un nœud s'abonne à un sujet d'image, traite les images en utilisant 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 thème relatif à une caméra et traiter l'image entrante en vue de sa détection :

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/ImageCes données sont cruciales pour des tâches telles que l'évitement d'obstacles, la cartographie en 3D et la localisation. Par l'utilisation d'informations sur la profondeur avec les images RVB, les robots peuvent mieux comprendre leur environnement en 3D.

Avec YOLO, vous pouvez 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 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 vers des tableaux numpy.
  2. Utilisez YOLO pour segmenter les images RVB.
  3. Appliquer 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 une visualisation en 3D des objets segmentés, utile pour des tâches telles que la navigation et la manipulation.

📅C réé il y a 5 mois ✏️ Mis à jour il y a 2 mois

Commentaires