Guide de démarrage rapide de ROS (Robot Operating System)
Introduction à ROS (sous-titrée) de Open Robotics sur Vimeo.
Qu'est-ce que ROS ?
Le Robot Operating System (ROS) est un framework open source largement utilisé dans la recherche en robotique et l'industrie. 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
-
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 effectue généralement une fonction spécifique, et les nœuds communiquent entre eux en utilisant des messages via des topics ou des services.
-
Middleware de Communication : ROS offre une infrastructure de communication robuste qui prend en charge la communication inter-processus et le calcul distribué. Ceci est réalisé grâce à un modèle de publication-abonnement pour les flux de données (topics) et un modèle de requête-réponse pour les appels de service.
-
Abstraction Matérielle : ROS fournit une couche d'abstraction au-dessus du matériel, permettant aux développeurs d'écrire du code indépendant du périphérique. Cela permet d'utiliser le même code avec différentes configurations matérielles, facilitant ainsi l'intégration et l'expérimentation.
-
Outils et Utilitaires : ROS est livré avec un ensemble riche 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.
-
Écosystème Étendu : L'écosystème ROS est vaste et en croissance continue, avec de nombreux packages disponibles pour différentes applications robotiques, notamment 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 améliorations pour répondre aux besoins croissants de la communauté robotique. Le développement de ROS peut être classé 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 vs. ROS 2
Alors que ROS 1 a fourni une base solide pour le développement robotique, ROS 2 corrige 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é : Fonctionnalités de sécurité améliorées pour un fonctionnement sûr et fiable dans divers environnements.
- Scalabilité : Meilleure prise en charge des systèmes multi-robots et des déploiements à grande échelle.
- Prise en Charge Multiplateforme : Compatibilité étendue avec divers systèmes d'exploitation au-delà de Linux, y compris Windows et macOS.
- Communication Flexible : Utilisation de DDS pour une communication inter-processus plus flexible et efficace.
Messages et sujets ROS
Dans ROS, la communication entre les nœuds est facilitée par des messages et des topics. Un message est une structure de données qui définit les informations échangées entre les nœuds, tandis qu'un topic est un canal nommé sur lequel les messages sont envoyés et reçus. Les nœuds peuvent publier des messages sur un topic ou s'abonner aux messages d'un topic, ce qui leur permet de communiquer entre eux. Ce modèle de publication-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 sur un topic, qui peut ensuite être consommé par d'autres nœuds pour le traitement ou le contrôle. Aux fins de ce guide, nous nous concentrerons sur les messages Image, Depth et PointCloud et les topics de caméra.
Configuration d'Ultralytics YOLO avec ROS
Ce guide a été testé en utilisant cet environnement ROS, qui est un fork du dépôt ROS ROSbot. Cet environnement comprend le package Ultralytics YOLO, un conteneur Docker pour une configuration facile, des packages 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.
Installation des dépendances
Outre l'environnement ROS, vous devrez installer les dépendances suivantes :
-
Package ROS Numpy : Ceci est nécessaire pour une conversion rapide entre les messages ROS Image et les tableaux numpy.
pip install ros_numpy
-
Package Ultralytics :
pip install ultralytics
Utiliser Ultralytics avec ROS sensor_msgs/Image
L'argument 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 de pixels, ce qui le rend approprié pour 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'objets, et la navigation.
Utilisation pas à pas des images
L'extrait de code suivant montre comment utiliser le package Ultralytics YOLO avec ROS. Dans cet exemple, nous nous abonnons à un topic de caméra, traitons l'image entrante en utilisant YOLO, et publions les objets détectés sur de nouveaux topics pour la détection et la segmentation.
Tout d'abord, importez les bibliothèques nécessaires et instanciez deux modèles : un pour la segmentation et un pour la détection. Initialisez un nœud ROS (avec le nom ultralytics
) pour permettre la communication avec le master 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("yolo11m.pt")
segmentation_model = YOLO("yolo11m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
Initialisez deux topics ROS : un pour détection et un pour la segmentation. Ces topics seront utilisés pour publier les images annotées, les rendant accessibles pour un traitement ultérieur. La communication entre les nœuds est facilitée en utilisant des 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 subscriber qui écoute les messages sur le /camera/color/image_raw
topic et appelle une fonction de callback pour chaque nouveau message. Cette fonction de callback reçoit les messages de type sensor_msgs/Image
, les convertit en un tableau numpy en utilisant ros_numpy
, traite les images avec les modèles YOLO précédemment instanciés, annote les images, puis les republie sur les topics respectifs : /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("yolo11m.pt")
segmentation_model = YOLO("yolo11m-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 :
rostopic echo <TOPIC-NAME>
: Cette commande vous permet de visualiser les messages publiés sur un topic spécifique, vous aidant ainsi à inspecter le flux de données.rostopic list
: Utilisez cette commande pour lister tous les topics disponibles dans le système ROS, ce qui vous donne une vue d'ensemble des flux de données actifs.rqt_graph
: Cet outil de visualisation affiche le graphe de communication entre les nœuds, offrant ainsi un aperçu de la manière dont les nœuds sont interconnectés et interagissent.- Pour des visualisations plus complexes, telles que les représentations 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 (par exemple,
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 ROS standard incluent également les std_msgs/String
messages. Dans de nombreuses applications, il n'est pas nécessaire de republier l'image annotée entière ; à la place, seules les classes présentes dans le champ de vision du robot sont nécessaires. L'exemple suivant montre comment utiliser les std_msgs/String
messages pour republier les classes détectées sur le /ultralytics/detection/classes
topic. 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érez un robot d'entrepôt équipé d'une caméra et d'un modèle de détection d'objets. Au lieu d'envoyer de grandes images annotées sur le réseau, le robot peut publier une liste des classes détectées sous forme de std_msgs/String
messages. Par exemple, lorsque le robot détecte des objets tels que « box », « pallet » et « forklift », il publie ces classes sur le /ultralytics/detection/classes
topic. 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 du trajet du robot pour éviter les obstacles ou déclencher des actions spécifiques telles que la collecte d'une boîte détectée. Cette approche réduit la bande passante requise pour la communication et se concentre sur la transmission des données critiques.
Utilisation pas à pas des chaînes de caractères
Cet exemple montre comment utiliser le package Ultralytics YOLO avec ROS. Dans cet exemple, nous nous abonnons à un topic de caméra, traitons l'image entrante à l'aide de YOLO et publions les objets détectés sur un nouveau topic /ultralytics/detection/classes
en utilisant les std_msgs/String
messages. Le ros_numpy
package 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("yolo11m.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 ROS
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 essentielles 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 de profondeur
Les images de profondeur peuvent être obtenues à l'aide de différents capteurs :
- Caméras stéréo : utilisez deux caméras pour calculer la profondeur en fonction de la disparité de l'image.
- Caméras Time-of-Flight (ToF) : Mesurent le temps que met la lumière pour revenir d'un objet.
- Capteurs à lumière structurée : Projettent un motif et mesurent sa déformation sur les surfaces.
Utilisation de YOLO avec des images de profondeur
Dans ROS, les images de profondeur sont représentées par le sensor_msgs/Image
type de message, qui comprend des champs pour l'encodage, la hauteur, la largeur et les données de pixel. 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 par rapport à l'objet. Les images de profondeur sont couramment utilisées conjointement avec les images RGB pour fournir une vue plus complète de l'environnement.
En utilisant YOLO, il est possible d'extraire et de combiner des informations provenant d'images RGB et de profondeur. Par exemple, YOLO peut détecter des objets dans une image RGB, et cette détection peut être utilisée pour identifier les régions correspondantes dans l'image de profondeur. Cela permet d'extraire des informations de profondeur précises pour les objets détectés, améliorant ainsi la capacité du robot à comprendre son environnement en trois dimensions.
Caméras RGB-D
Lorsque vous travaillez avec des images de profondeur, il est essentiel de s'assurer que les images RGB et de profondeur sont correctement alignées. Les caméras RGB-D, telles que la série Intel RealSense, fournissent des images RGB et de profondeur synchronisées, ce qui facilite la combinaison des informations provenant des deux sources. Si vous utilisez des caméras RGB et de profondeur séparées, il est essentiel de les calibrer pour garantir un alignement précis.
Utilisation pas à pas 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 dans 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 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("yolo11m-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 RGB, les convertit en tableaux numpy et applique le modèle de segmentation à l'image RGB. 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, appelée distance de coupure, 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 sur le /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)
all_objects = []
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
all_objects.append(f"{name}: {avg_distance:.2f}m")
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("yolo11m-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)
all_objects = []
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
all_objects.append(f"{name}: {avg_distance:.2f}m")
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
L'argument sensor_msgs/PointCloud2
type de message est une structure de données utilisée dans ROS pour représenter les données de nuage 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 tridimensionnel. Ces points de données représentent la surface externe d'un objet ou d'une scène, capturée via des technologies de numérisation 3D. Chaque point du nuage a X
, Y
, et Z
coordonnées, qui correspondent à sa position dans l'espace, et peut également inclure des informations supplémentaires telles que la couleur et l'intensité.
Référentiel
Lorsque vous travaillez avec sensor_msgs/PointCloud2
, il est essentiel de tenir compte du référentiel du capteur à partir duquel les données du nuage de points ont été acquises. Le nuage de points est initialement capturé dans le référentiel du capteur. Vous pouvez déterminer ce référentiel en écoutant le /tf_static
sujet. Cependant, selon les exigences spécifiques de votre application, vous devrez peut-être convertir le nuage de points dans un autre référentiel. Cette transformation peut être réalisée à l'aide du tf2_ros
package, qui fournit des outils pour gérer les référentiels et transformer les données entre eux.
Obtention de nuages de points
Les nuages de points peuvent être obtenus à l'aide de divers capteurs :
- 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.
- Caméras de profondeur : Capturent des informations de profondeur pour chaque pixel, permettant la reconstruction 3D de la scène.
- Caméras stéréo : Utilisez deux caméras ou plus pour obtenir des informations de profondeur par triangulation.
- Scanners à lumière structurée : Projetez un motif connu sur une surface et mesurez la déformation pour calculer la profondeur.
Utilisation de YOLO avec des nuages de points
Pour intégrer YOLO avec des sensor_msgs/PointCloud2
messages de type, nous pouvons employer 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 pour isoler l'objet 3D d'intérêt.
Pour la gestion des nuages de points, nous recommandons d'utiliser Open3D (pip install open3d
), une bibliothèque Python conviviale. Open3D fournit des outils robustes pour la gestion des structures de données de nuages de points, leur visualisation et l'exécution transparente d'opérations complexes. 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.
Utilisation pas à pas des nuages de points
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("yolo11m-seg.pt")
Créez une fonction pointcloud2_to_array
, qui transforme un sensor_msgs/PointCloud2
message en deux tableaux numpy. Les sensor_msgs/PointCloud2
messages contiennent n
points basés sur le width
et height
de l'image acquise. Par exemple, une 480 x 640
image aura 307,200
points. Chaque point comprend trois coordonnées spatiales (xyz
) et la couleur correspondante au format RGB
. Ceux-ci peuvent être considérés comme deux canaux d'information distincts.
La fonction renvoie les xyz
coordonnées et les RGB
valeurs au format de la résolution de la caméra d'origine (width x height
). La plupart des capteurs ont une distance maximale, appelée distance de clipping, 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 au /camera/depth/points
topic pour recevoir le message du nuage de points et convertir le sensor_msgs/PointCloud2
message en tableaux numpy contenant les coordonnées XYZ et les valeurs RGB (en utilisant la pointcloud2_to_array
fonction). Traitez l'image RGB à l'aide du modèle YOLO pour extraire les objets segmentés. Pour chaque objet détecté, extrayez le masque de segmentation et appliquez-le à la fois à l'image RGB et aux coordonnées XYZ pour isoler l'objet dans l'espace 3D.
Le traitement du masque est simple puisqu'il est constitué de valeurs binaires, avec 1
indiquant la présence de l'objet et 0
indiquant son absence. Pour appliquer le masque, il suffit de multiplier les canaux originaux 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 sensor_msgs.msg import PointCloud2
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-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])
FAQ
Qu'est-ce que le Robot Operating System (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 via des topics ou des services.
Comment intégrer Ultralytics YOLO avec ROS pour la détection d'objets en temps réel ?
L'intégration d'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 requises comme ros_numpy
et Ultralytics YOLO :
pip install ros_numpy ultralytics
Ensuite, créez un nœud ROS et abonnez-vous à un topic 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("yolo11m.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 topics 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 d'Ultralytics YOLO, vous pouvez 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 sur de nouveaux sujets.
Par exemple, abonnez-vous à un topic de caméra et traitez l'image entrante pour la détection :
rospy.Subscriber("/camera/color/image_raw", Image, callback)
Pourquoi utiliser des images de profondeur avec Ultralytics YOLO dans ROS ?
Les images de profondeur dans ROS, représentées par sensor_msgs/Image
, fournissent la distance des objets par rapport à la caméra, ce qui est essentiel pour des tâches telles que l'évitement d'obstacles, la cartographie 3D et la localisation. En utilisant les informations de profondeur avec des images RGB, les robots peuvent mieux comprendre leur environnement 3D.
Avec YOLO, vous pouvez extraire des masques de segmentation à partir d'images RVB et appliquer ces masques à des images de profondeur pour obtenir des informations 3D précises sur les objets, 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 les nuages de points 3D dans ROS avec YOLO :
- Convertissez
sensor_msgs/PointCloud2
les messages en tableaux numpy. - Utilisez YOLO pour segmenter les images RGB.
- Appliquez le masque de segmentation au nuage de points.
Voici un exemple d'utilisation d'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("yolo11m-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 fournit une visualisation 3D des objets segmentés, utile pour des tâches telles que la navigation et la manipulation dans les applications robotiques.