Meet YOLO26: next-gen vision AI.

Link to this sectionROS (Robot Operating System) Kurzanleitung#

Dieser Leitfaden zeigt dir, wie du Ultralytics YOLO in einen Roboter mit ROS Noetic integrierst, um Echtzeit-Objekterkennung und Segmentierung auf RGB-Bildern, Tiefenbildern und Punktwolken auszuführen.

Springe zur Einrichtung von YOLO mit ROS und arbeite dann mit RGB-Bildern, Tiefenbildern oder Punktwolken.

ROS Introduction (captioned) from Open Robotics on Vimeo.

Link to this sectionWas ist ROS?#

Das Robot Operating System (ROS) ist ein Open-Source-Framework, das in der Robotikforschung und -industrie weit verbreitet ist. ROS bietet eine Sammlung von Bibliotheken und Tools, die Entwickler bei der Erstellung von Roboteranwendungen unterstützen. ROS wurde für die Arbeit mit verschiedenen Roboterplattformen entwickelt und ist somit ein flexibles und leistungsstarkes Werkzeug für Robotiker.

Link to this sectionHauptmerkmale von ROS#

  1. Modulare Architektur: ROS besitzt eine modulare Architektur, die es Entwicklern ermöglicht, komplexe Systeme durch die Kombination kleinerer, wiederverwendbarer Komponenten, sogenannter Nodes, aufzubauen. Jeder Node führt typischerweise eine spezifische Funktion aus, und die Nodes kommunizieren untereinander mittels Nachrichten über Topics oder Services.

  2. Kommunikations-Middleware: ROS bietet eine robuste Kommunikationsinfrastruktur, die Inter-Prozess-Kommunikation und verteiltes Rechnen unterstützt. Dies wird durch ein Publish-Subscribe-Modell für Datenströme (Topics) und ein Request-Reply-Modell für Service-Aufrufe erreicht.

  3. Hardware-Abstraktion: ROS bietet eine Abstraktionsschicht über der Hardware, wodurch Entwickler geräteunabhängigen Code schreiben können. Dies erlaubt es, denselben Code mit unterschiedlichen Hardware-Setups zu verwenden, was die Integration und Experimente erleichtert.

  4. Tools und Dienstprogramme: ROS enthält eine reichhaltige Auswahl an Tools und Dienstprogrammen für Visualisierung, Debugging und Simulation. Zum Beispiel wird RViz zur Visualisierung von Sensordaten und Roboterzustandsinformationen verwendet, während Gazebo eine leistungsstarke Simulationsumgebung für das Testen von Algorithmen und Roboterdesigns bereitstellt.

  5. Umfangreiches Ökosystem: Das ROS-Ökosystem ist riesig und wächst stetig, mit zahlreichen verfügbaren Paketen für verschiedene Roboteranwendungen, einschließlich Navigation, Manipulation, Wahrnehmung und mehr. Die Community trägt aktiv zur Entwicklung und Wartung dieser Pakete bei.

Entwicklung der ROS-Versionen

Seit seiner Entwicklung im Jahr 2007 hat sich ROS über mehrere Versionen hinweg weiterentwickelt, wobei jede neue Funktionen und Verbesserungen einführte, um den wachsenden Anforderungen der Robotik-Community gerecht zu werden. Die Entwicklung von ROS lässt sich in zwei Hauptserien unterteilen: ROS 1 und ROS 2. Dieser Leitfaden konzentriert sich auf die Long Term Support (LTS) Version von ROS 1, bekannt als ROS Noetic Ninjemys; der Code sollte jedoch auch mit früheren Versionen funktionieren.

Link to this sectionROS 1 vs. ROS 2#

Während ROS 1 eine solide Grundlage für die Robotikentwicklung bot, behebt ROS 2 dessen Schwachstellen durch folgende Neuerungen:

  • Echtzeitfähigkeit: Verbesserte Unterstützung für Echtzeitsysteme und deterministisches Verhalten.
  • Sicherheit: Erweiterte Sicherheitsfunktionen für einen sicheren und zuverlässigen Betrieb in verschiedenen Umgebungen.
  • Skalierbarkeit: Bessere Unterstützung für Multi-Roboter-Systeme und groß angelegte Implementierungen.
  • Plattformübergreifende Unterstützung: Erweiterte Kompatibilität mit verschiedenen Betriebssystemen jenseits von Linux, einschließlich Windows und macOS.
  • Flexible Kommunikation: Verwendung von DDS für eine flexiblere und effizientere Inter-Prozess-Kommunikation.

Link to this sectionROS Nachrichten und Topics#

In ROS wird die Kommunikation zwischen Nodes durch Messages und Topics erleichtert. Eine Message ist eine Datenstruktur, die den zwischen den Nodes ausgetauschten Informationsinhalt definiert, während ein Topic ein benannter Kanal ist, über den Messages gesendet und empfangen werden. Nodes können Messages an ein Topic senden (publizieren) oder Messages von einem Topic empfangen (abonnieren), was ihre gegenseitige Kommunikation ermöglicht. Dieses Publish-Subscribe-Modell erlaubt eine asynchrone Kommunikation und Entkopplung zwischen den Nodes. Jeder Sensor oder Aktor in einem Robotersystem publiziert typischerweise Daten in ein Topic, das dann von anderen Nodes für die Verarbeitung oder Steuerung verwendet werden kann. Für diesen Leitfaden konzentrieren wir uns auf Image-, Depth- und PointCloud-Messages sowie Kamera-Topics.

Link to this sectionEinrichtung von Ultralytics YOLO mit ROS#

Dieser Leitfaden wurde unter Verwendung dieser ROS-Umgebung getestet, die ein Fork des ROSbot ROS-Repositorys ist. Diese Umgebung enthält das Ultralytics YOLO-Paket, einen Docker-Container für eine einfache Einrichtung, umfassende ROS-Pakete und Gazebo-Welten für schnelle Tests. Sie ist für den Betrieb mit dem Husarion ROSbot 2 PRO ausgelegt. Die bereitgestellten Codebeispiele funktionieren in jeder ROS Noetic/Melodic-Umgebung, sowohl in der Simulation als auch in der realen Welt.

Husarion ROSbot 2 PRO autonomous robot platform

Link to this sectionInstallation der Abhängigkeiten#

Neben der ROS-Umgebung musst du die folgenden Abhängigkeiten installieren:

  • ROS NumPy-Paket: Dies wird für die schnelle Konvertierung zwischen ROS Image-Messages und NumPy-Arrays benötigt.

    pip install ros_numpy
  • Ultralytics-Paket:

    pip install ultralytics

Link to this sectionVerwende Ultralytics mit ROS sensor_msgs/Image#

Der sensor_msgs/Image Message-Typ wird in ROS häufig zur Darstellung von Bilddaten verwendet. Er enthält Felder für Kodierung, Höhe, Breite und Pixeldaten, was ihn für die Übertragung von Bildern geeignet macht, die von Kameras oder anderen Sensoren erfasst wurden. Image-Messages werden in Roboteranwendungen häufig für Aufgaben wie visuelle Wahrnehmung, Objekterkennung und Navigation eingesetzt.

Detection and Segmentation in ROS Gazebo

Link to this sectionSchritt-für-Schritt-Anleitung zur Bildnutzung#

Der folgende Code-Schnipsel zeigt, wie das Ultralytics YOLO-Paket mit ROS verwendet wird. In diesem Beispiel abonnieren wir ein Kamera-Topic, verarbeiten das eingehende Bild mit YOLO und publizieren die erkannten Objekte an neue Topics für Erkennung und Segmentierung.

Importiere zuerst die notwendigen Bibliotheken und instanziiere zwei Modelle: eines für Segmentierung und eines für Erkennung. Initialisiere einen ROS-Node (mit dem Namen ultralytics), um die Kommunikation mit dem ROS-Master zu ermöglichen. Um eine stabile Verbindung zu gewährleisten, fügen wir eine kurze Pause ein, damit der Node genügend Zeit hat, die Verbindung herzustellen, bevor er fortfährt.

import time

import rospy

from ultralytics import YOLO

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

Initialisiere zwei ROS-Topics: eines für Erkennung und eines für Segmentierung. Diese Topics werden verwendet, um die annotierten Bilder zu veröffentlichen und sie so für die weitere Verarbeitung zugänglich zu machen. Die Kommunikation zwischen den Nodes erfolgt über 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)

Erstelle schließlich einen Subscriber, der auf Messages im Topic /camera/color/image_raw hört und für jede neue Message eine Callback-Funktion aufruft. Diese Callback-Funktion empfängt Messages des Typs sensor_msgs/Image, konvertiert diese mittels ros_numpy in ein NumPy-Array, verarbeitet die Bilder mit den zuvor instanziierten YOLO-Modellen, annotiert die Bilder und veröffentlicht sie dann zurück in die entsprechenden Topics: /ultralytics/detection/image für die Erkennung und /ultralytics/segmentation/image für die Segmentierung.

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()
Vollständiger Code
import time

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolo26m.pt")
segmentation_model = YOLO("yolo26m-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()
Debugging

Das Debugging von ROS (Robot Operating System)-Nodes kann aufgrund der verteilten Natur des Systems eine Herausforderung sein. Mehrere Tools können bei diesem Prozess helfen:

  1. rostopic echo <TOPIC-NAME> : Dieser Befehl ermöglicht es dir, die auf einem bestimmten Topic veröffentlichten Messages anzuzeigen, was dir hilft, den Datenfluss zu überprüfen.
  2. rostopic list: Verwende diesen Befehl, um alle verfügbaren Topics im ROS-System aufzulisten, was dir einen Überblick über die aktiven Datenströme gibt.
  3. rqt_graph: Dieses Visualisierungstool zeigt den Kommunikationsgraphen zwischen den Nodes an und bietet Einblicke darin, wie Nodes miteinander verbunden sind und wie sie interagieren.
  4. Für komplexere Visualisierungen, wie 3D-Darstellungen, kannst du RViz verwenden. RViz (ROS Visualization) ist ein leistungsstarkes 3D-Visualisierungstool für ROS. Es ermöglicht dir, den Zustand deines Roboters und seiner Umgebung in Echtzeit zu visualisieren. Mit RViz kannst du Sensordaten (z. B. sensor_msgs/Image), Roboter-Modellzustände und verschiedene andere Arten von Informationen anzeigen, was es einfacher macht, das Verhalten deines Robotersystems zu debuggen und zu verstehen.

Link to this sectionPubliziere erkannte Klassen mit std_msgs/String#

Standard-ROS-Messages beinhalten auch std_msgs/String Messages. In vielen Anwendungen ist es nicht notwendig, das gesamte annotierte Bild erneut zu veröffentlichen; stattdessen werden nur die Klassen benötigt, die im Sichtfeld des Roboters vorhanden sind. Das folgende Beispiel zeigt, wie std_msgs/String Messages verwendet werden, um die erkannten Klassen im Topic /ultralytics/detection/classes zu veröffentlichen. Diese Messages sind leichter und bieten essenzielle Informationen, was sie für verschiedene Anwendungen wertvoll macht.

Link to this sectionAnwendungsbeispiel#

Stell dir einen Lagerroboter vor, der mit einer Kamera und einem Erkennungsmodell ausgestattet ist. Anstatt große annotierte Bilder über das Netzwerk zu senden, kann der Roboter eine Liste der erkannten Klassen als std_msgs/String Messages publizieren. Wenn der Roboter beispielsweise Objekte wie "box", "pallet" und "forklift" erkennt, veröffentlicht er diese Klassen im Topic /ultralytics/detection/classes. Diese Informationen können dann von einem zentralen Überwachungssystem genutzt werden, um den Lagerbestand in Echtzeit zu verfolgen, die Pfadplanung des Roboters zur Vermeidung von Hindernissen zu optimieren oder spezifische Aktionen wie das Aufheben einer erkannten Kiste auszulösen. Dieser Ansatz reduziert die für die Kommunikation erforderliche Bandbreite und konzentriert sich auf die Übertragung kritischer Daten.

Link to this sectionSchritt-für-Schritt-Anleitung zur String-Nutzung#

Dieses Beispiel zeigt, wie das Ultralytics YOLO-Paket mit ROS verwendet wird. In diesem Beispiel abonnieren wir ein Kamera-Topic, verarbeiten das eingehende Bild mit YOLO und publizieren die erkannten Objekte an ein neues Topic /ultralytics/detection/classes unter Verwendung von std_msgs/String Messages. Das ros_numpy-Paket wird verwendet, um die ROS Image-Message für die Verarbeitung mit YOLO in ein NumPy-Array umzuwandeln.

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("yolo26m.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()

Link to this sectionVerwendung von Ultralytics mit ROS-Tiefenbildern#

Zusätzlich zu RGB-Bildern unterstützt ROS Tiefenbilder, die Informationen über den Abstand von Objekten zur Kamera liefern. Tiefenbilder sind entscheidend für Roboteranwendungen wie Hindernisvermeidung, 3D-Kartierung und Lokalisierung.

Ein Tiefenbild ist ein Bild, bei dem jeder Pixel den Abstand von der Kamera zu einem Objekt darstellt. Im Gegensatz zu RGB-Bildern, die Farbe erfassen, erfassen Tiefenbilder räumliche Informationen, was es Robotern ermöglicht, die 3D-Struktur ihrer Umgebung wahrzunehmen.

Erhalten von Tiefenbildern

Tiefenbilder können mit verschiedenen Sensoren gewonnen werden:

  1. Stereokameras: Verwenden zwei Kameras, um die Tiefe basierend auf der Bilddisparität zu berechnen.
  2. Time-of-Flight (ToF)-Kameras: Messen die Zeit, die das Licht benötigt, um von einem Objekt zurückzukehren.
  3. Strukturlichtsensoren: Projizieren ein Muster und messen dessen Verformung auf Oberflächen.

Link to this sectionVerwendung von YOLO mit Tiefenbildern#

In ROS werden Tiefenbilder durch den Message-Typ sensor_msgs/Image dargestellt, der Felder für Kodierung, Höhe, Breite und Pixeldaten enthält. Die Kodierung für Tiefenbilder verwendet oft ein Format wie "16UC1", was auf einen 16-Bit-Integer ohne Vorzeichen pro Pixel hinweist, wobei jeder Wert den Abstand zum Objekt darstellt. Tiefenbilder werden häufig in Verbindung mit RGB-Bildern verwendet, um eine umfassendere Sicht auf die Umgebung zu erhalten.

Mit YOLO ist es möglich, Informationen aus RGB- und Tiefenbildern zu extrahieren und zu kombinieren. Zum Beispiel kann YOLO Objekte in einem RGB-Bild erkennen, und diese Erkennung kann genutzt werden, um entsprechende Regionen im Tiefenbild zu lokalisieren. Dies ermöglicht die Extraktion präziser Tiefeninformationen für erkannte Objekte, was die Fähigkeit des Roboters verbessert, seine Umgebung in drei Dimensionen zu verstehen.

RGB-D-Kameras

Bei der Arbeit mit Tiefenbildern ist es wichtig sicherzustellen, dass die RGB- und Tiefenbilder korrekt ausgerichtet sind. RGB-D-Kameras, wie die Intel RealSense-Serie, liefern synchronisierte RGB- und Tiefenbilder, was die Kombination von Informationen aus beiden Quellen erleichtert. Bei Verwendung separater RGB- und Tiefenkameras ist es entscheidend, diese zu kalibrieren, um eine genaue Ausrichtung sicherzustellen.

Link to this sectionSchritt-für-Schritt-Anleitung zur Tiefennutzung#

In diesem Beispiel verwenden wir YOLO, um ein Bild zu segmentieren, und wenden die extrahierte Maske an, um das Objekt im Tiefenbild zu segmentieren. Dies ermöglicht uns, den Abstand jedes Pixels des interessierenden Objekts vom Fokuszentrum der Kamera zu bestimmen. Durch den Erhalt dieser Abstandsdaten können wir den Abstand zwischen der Kamera und dem spezifischen Objekt in der Szene berechnen. Beginne mit dem Importieren der notwendigen Bibliotheken, dem Erstellen eines ROS-Nodes und der Instanziierung eines Segmentierungsmodells sowie eines ROS-Topics.

import time

import rospy
from std_msgs.msg import String

from ultralytics import YOLO

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

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

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

Definiere als Nächstes eine Callback-Funktion, die die eingehende Tiefenbild-Message verarbeitet. Die Funktion wartet auf die Tiefenbild- und RGB-Bild-Messages, konvertiert sie in NumPy-Arrays und wendet das Segmentierungsmodell auf das RGB-Bild an. Anschließend extrahiert sie die Segmentierungsmaske für jedes erkannte Objekt und berechnet den durchschnittlichen Abstand des Objekts zur Kamera unter Verwendung des Tiefenbildes. Die meisten Sensoren haben einen maximalen Abstand, den sogenannten Clip-Abstand, jenseits dessen Werte als inf (np.inf) dargestellt werden. Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen einen Wert von 0 zuzuweisen. Schließlich veröffentlicht sie die erkannten Objekte zusammen mit ihren durchschnittlichen Abständen im Topic /ultralytics/detection/distance.

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()
Vollständiger Code
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("yolo26m-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()

Link to this sectionVerwendung von Ultralytics mit ROS sensor_msgs/PointCloud2#

Detection and Segmentation in ROS Gazebo

Der sensor_msgs/PointCloud2 Message-Typ ist eine Datenstruktur, die in ROS zur Darstellung von 3D-Punktwolkendaten verwendet wird. Dieser Message-Typ ist ein integraler Bestandteil von Roboteranwendungen und ermöglicht Aufgaben wie 3D-Kartierung, Objekterkennung und Lokalisierung.

Eine Punktwolke ist eine Ansammlung von Datenpunkten, die in einem dreidimensionalen Koordinatensystem definiert sind. Diese Datenpunkte stellen die äußere Oberfläche eines Objekts oder einer Szene dar, erfasst durch 3D-Scantechnologien. Jeder Punkt in der Wolke verfügt über X-, Y- und Z-Koordinaten, die seiner Position im Raum entsprechen, und kann zusätzlich Informationen wie Farbe und Intensität enthalten.

Referenzrahmen

Bei der Arbeit mit sensor_msgs/PointCloud2 ist es essenziell, den Referenzrahmen des Sensors zu berücksichtigen, von dem die Punktwolkendaten bezogen wurden. Die Punktwolke wird zunächst im Referenzrahmen des Sensors erfasst. Du kannst diesen Referenzrahmen bestimmen, indem du das Topic /tf_static abhörst. Abhängig von deinen spezifischen Anwendungsanforderungen musst du die Punktwolke jedoch möglicherweise in einen anderen Referenzrahmen konvertieren. Diese Transformation kann mit dem tf2_ros-Paket erreicht werden, das Tools zur Verwaltung von Koordinatenrahmen und zur Transformation von Daten zwischen ihnen bereitstellt.

Erhalten von Punktwolken

Punktwolken können mit verschiedenen Sensoren gewonnen werden:

  1. LIDAR (Light Detection and Ranging): Verwendet Laserpulse, um Abstände zu Objekten zu messen und hochpräzise 3D-Karten zu erstellen.
  2. Tiefenkameras: Erfassen Tiefeninformationen für jeden Pixel, was eine 3D-Rekonstruktion der Szene ermöglicht.
  3. Stereokameras: Nutzen zwei oder mehr Kameras, um Tiefeninformationen durch Triangulation zu erhalten.
  4. Strukturlichtscanner: Projizieren ein bekanntes Muster auf eine Oberfläche und messen die Verformung, um die Tiefe zu berechnen.

Link to this sectionVerwendung von YOLO mit Punktwolken#

Um YOLO mit sensor_msgs/PointCloud2-Messages zu integrieren, können wir eine Methode anwenden, die der für Tiefenkarten verwendeten ähnelt. Indem wir die in der Punktwolke eingebetteten Farbinformationen nutzen, können wir ein 2D-Bild extrahieren, darauf eine Segmentierung mit YOLO durchführen und dann die resultierende Maske auf die dreidimensionalen Punkte anwenden, um das 3D-Objekt von Interesse zu isolieren.

Für den Umgang mit Punktwolken empfehlen wir die Verwendung von Open3D (pip install open3d), einer benutzerfreundlichen Python-Bibliothek. Open3D bietet robuste Tools zur Verwaltung von Punktwolkendatenstrukturen, deren Visualisierung und die nahtlose Ausführung komplexer Operationen. Diese Bibliothek kann den Prozess erheblich vereinfachen und unsere Fähigkeit verbessern, Punktwolken in Verbindung mit YOLO-basierter Segmentierung zu manipulieren und zu analysieren.

Link to this sectionSchritt-für-Schritt-Anleitung zur Punktwolken-Nutzung#

Importiere die notwendigen Bibliotheken und instanziiere das YOLO-Modell für die Segmentierung.

import time

import rospy

from ultralytics import YOLO

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

Create a function pointcloud2_to_array, which transforms a sensor_msgs/PointCloud2 message into two NumPy arrays. The sensor_msgs/PointCloud2 messages contain n points based on the width and height of the acquired image. For instance, a 480 x 640 image will have 307,200 points. Each point includes three spatial coordinates (xyz) and the corresponding color in RGB format. These can be considered as two separate channels of information.

Die Funktion gibt die xyz-Koordinaten und RGB-Werte im Format der ursprünglichen Kameraauflösung (width x height) zurück. Die meisten Sensoren haben einen maximalen Abstand, den sogenannten Clip-Abstand, jenseits dessen Werte als inf (np.inf) dargestellt werden. Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen einen Wert von 0 zuzuweisen.

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

Abonniere als Nächstes das Topic /camera/depth/points, um die Punktwolken-Message zu empfangen, und konvertiere die sensor_msgs/PointCloud2-Message in NumPy-Arrays, die die XYZ-Koordinaten und RGB-Werte enthalten (unter Verwendung der Funktion pointcloud2_to_array). Verarbeite das RGB-Bild mit dem YOLO-Modell, um segmentierte Objekte zu extrahieren. Extrahiere für jedes erkannte Objekt die Segmentierungsmaske und wende sie sowohl auf das RGB-Bild als auch auf die XYZ-Koordinaten an, um das Objekt im 3D-Raum zu isolieren.

Die Verarbeitung der Maske ist unkompliziert, da sie aus Binärwerten besteht, wobei 1 das Vorhandensein des Objekts und 0 die Abwesenheit anzeigt. Um die Maske anzuwenden, multipliziere einfach die ursprünglichen Kanäle mit der Maske. Diese Operation isoliert das Objekt von Interesse innerhalb des Bildes effektiv. Erstelle abschließend ein Open3D-Punktwolkenobjekt und visualisiere das segmentierte Objekt im 3D-Raum mit den zugehörigen Farben.

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])
Vollständiger Code
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("yolo26m-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])

Point Cloud Segmentation with Ultralytics

Link to this sectionFazit#

Mit der Integration von Ultralytics YOLO in ROS kann dein Roboter Objekterkennung und Segmentierung für RGB-Bilder, Tiefenbilder und Punktwolken durchführen und so rohe Sensor-Streams in verwertbare Wahrnehmung umwandeln. Erkunde von hier aus den Vorhersagemodus für weitere Inferenzoptionen oder befolge die Schritte eines Computer-Vision-Projekts, um deine Robotik-Anwendung vom Prototyp zur Produktion zu bringen.

Link to this sectionFAQ#

Link to this sectionWas ist das Robot Operating System (ROS)?#

Das Robot Operating System (ROS) ist ein Open-Source-Framework, das in der Robotik üblicherweise verwendet wird, um Entwickler bei der Erstellung robuster Roboteranwendungen zu unterstützen. Es bietet eine Sammlung von Bibliotheken und Tools zum Aufbau von Robotersystemen und zur Schnittstellenbildung mit diesen, was die Entwicklung komplexer Anwendungen erleichtert. ROS unterstützt die Kommunikation zwischen Nodes mittels Messages über Topics oder Services.

Link to this sectionWie integriere ich Ultralytics YOLO mit ROS für Objekterkennung in Echtzeit?#

Die Integration von Ultralytics YOLO mit ROS umfasst das Einrichten einer ROS-Umgebung und die Verwendung von YOLO zur Verarbeitung von Sensordaten. Beginne mit der Installation der erforderlichen Abhängigkeiten wie ros_numpy und Ultralytics YOLO:

pip install ros_numpy ultralytics

Erstelle als Nächstes einen ROS-Knoten und abonniere ein Bild-Topic, um die eingehenden Daten für die Objekterkennung zu verarbeiten. Hier ist ein minimales Beispiel:

import ros_numpy
import rospy
from sensor_msgs.msg import Image

from ultralytics import YOLO

detection_model = YOLO("yolo26m.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()

Link to this sectionWas sind ROS-Topics und wie werden sie in Ultralytics YOLO verwendet?#

ROS-Topics erleichtern die Kommunikation zwischen Knoten in einem ROS-Netzwerk durch die Verwendung eines Publish-Subscribe-Modells. Ein Topic ist ein benannter Kanal, den Knoten verwenden, um Nachrichten asynchron zu senden und zu empfangen. Im Kontext von Ultralytics YOLO kannst du einen Knoten dazu bringen, ein Bild-Topic zu abonnieren, die Bilder mit YOLO für Aufgaben wie Erkennung oder Segmentierung zu verarbeiten und die Ergebnisse in neuen Topics zu veröffentlichen.

Abonniere zum Beispiel ein Kamera-Topic und verarbeite das eingehende Bild für die Erkennung:

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

Link to this sectionWarum Tiefenbilder mit Ultralytics YOLO in ROS verwenden?#

Tiefenbilder in ROS, dargestellt durch sensor_msgs/Image, liefern den Abstand von Objekten zur Kamera, was entscheidend für Aufgaben wie Hindernisvermeidung, 3D-Kartierung und Lokalisierung ist. Durch Nutzung von Tiefeninformationen zusammen mit RGB-Bildern können Roboter ihre 3D-Umgebung besser verstehen.

Mit YOLO kannst du Segmentierungsmasken aus RGB-Bildern extrahieren und diese auf Tiefenbilder anwenden, um präzise 3D-Objektinformationen zu erhalten, was die Fähigkeit des Roboters verbessert, zu navigieren und mit seiner Umgebung zu interagieren.

Link to this sectionWie kann ich 3D-Punktwolken mit YOLO in ROS visualisieren?#

Zur Visualisierung von 3D-Punktwolken in ROS mit YOLO:

  1. Konvertiere sensor_msgs/PointCloud2-Messages in NumPy-Arrays.
  2. Verwende YOLO, um RGB-Bilder zu segmentieren.
  3. Wende die Segmentierungsmaske auf die Punktwolke an.

Hier ist ein Beispiel unter Verwendung von Open3D für die Visualisierung:

import sys

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")
segmentation_model = YOLO("yolo26m-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])

Dieser Ansatz bietet eine 3D-Visualisierung segmentierter Objekte, was für Aufgaben wie Navigation und Manipulation in Robotikanwendungen nützlich ist.

Kommentare