ROS (Robot Operating System) Schnellstartanleitung
ROS Einführung (mit Untertiteln) von Open Robotics auf Vimeo.
Was 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 Entwicklern bei der Erstellung von Roboteranwendungen helfen. ROS ist für die Zusammenarbeit mit verschiedenen Roboterplattformen konzipiert und somit ein flexibles und leistungsstarkes Werkzeug für Robotiker.
Hauptmerkmale von ROS
-
Modulare Architektur: ROS hat eine modulare Architektur, die es Entwicklern ermöglicht, komplexe Systeme zu erstellen, indem sie kleinere, wiederverwendbare Komponenten, sogenannte Knoten, kombinieren. Jeder Knoten führt typischerweise eine bestimmte Funktion aus, und Knoten kommunizieren miteinander über Nachrichten über Themen oder Dienste.
-
Kommunikations-Middleware: ROS bietet eine robuste Kommunikationsinfrastruktur, die die Interprozesskommunikation und das verteilte 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.
-
Hardware-Abstraktion: ROS bietet eine Abstraktionsschicht über der Hardware, die es Entwicklern ermöglicht, geräteunabhängigen Code zu schreiben. Dies ermöglicht die Verwendung desselben Codes mit verschiedenen Hardware-Setups und erleichtert so die Integration und das Experimentieren.
-
Tools und Hilfsprogramme: ROS verfügt über eine Vielzahl von Tools und Hilfsprogrammen für Visualisierung, Debugging und Simulation. Beispielsweise wird RViz zur Visualisierung von Sensordaten und Roboterstatusinformationen verwendet, während Gazebo eine leistungsstarke Simulationsumgebung zum Testen von Algorithmen und Roboterdesigns bietet.
-
Umfangreiches Ökosystem: Das ROS-Ökosystem ist riesig und wächst ständig. Es bietet zahlreiche Pakete für verschiedene Robotikanwendungen, darunter Navigation, Manipulation, Wahrnehmung und mehr. Die Community trägt aktiv zur Entwicklung und Wartung dieser Pakete bei.
Evolution von ROS-Versionen
Seit seiner Entwicklung im Jahr 2007 hat sich ROS durch mehrere Versionen weiterentwickelt, von denen jede neue Funktionen und Verbesserungen zur Erfüllung der wachsenden Anforderungen der Robotik-Community einführte. Die Entwicklung von ROS lässt sich in zwei Hauptreihen einteilen: 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 auch mit früheren Versionen funktionieren.
ROS 1 vs. ROS 2
Während ROS 1 eine solide Grundlage für die Roboterentwicklung bot, behebt ROS 2 seine Mängel durch:
- Echtzeitleistung: 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 Bereitstellungen.
- Cross-Platform-Unterstützung: Erweiterte Kompatibilität mit verschiedenen Betriebssystemen über Linux hinaus, einschließlich Windows und macOS.
- Flexible Kommunikation: Verwendung von DDS für eine flexiblere und effizientere Interprozesskommunikation.
ROS Nachrichten und Themen
In ROS wird die Kommunikation zwischen Knoten durch Nachrichten und Themen ermöglicht. Eine Nachricht ist eine Datenstruktur, die die zwischen Knoten ausgetauschten Informationen definiert, während ein Thema ein benannter Kanal ist, über den Nachrichten gesendet und empfangen werden. Knoten können Nachrichten zu einem Thema veröffentlichen oder Nachrichten von einem Thema abonnieren, wodurch sie miteinander kommunizieren können. Dieses Publish-Subscribe-Modell ermöglicht eine asynchrone Kommunikation und Entkopplung zwischen Knoten. Jeder Sensor oder Aktor in einem Robotersystem veröffentlicht typischerweise Daten zu einem Thema, das dann von anderen Knoten zur Verarbeitung oder Steuerung genutzt werden kann. Für die Zwecke dieses Leitfadens werden wir uns auf Bild-, Tiefen- und Punktwolken-Nachrichten sowie Kamerathemen konzentrieren.
Einrichten von Ultralytics YOLO mit ROS
Dieser Leitfaden wurde mit dieser ROS-Umgebung getestet, die ein Fork des ROSbot ROS-Repositorys ist. Diese Umgebung umfasst 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 die Verwendung mit dem Husarion ROSbot 2 PRO konzipiert. Die bereitgestellten Codebeispiele funktionieren in jeder ROS Noetic/Melodic-Umgebung, sowohl in der Simulation als auch in der realen Welt.
Installation von Abhängigkeiten
Abgesehen von der ROS-Umgebung müssen Sie die folgenden Abhängigkeiten installieren:
-
ROS Numpy-Paket: Dies ist für die schnelle Konvertierung zwischen ROS-Image-Nachrichten und Numpy-Arrays erforderlich.
pip install ros_numpy
-
Ultralytics Paket:
pip install ultralytics
Ultralytics mit ROS verwenden sensor_msgs/Image
Die sensor_msgs/Image
Nachrichtentyp wird häufig in ROS zur Darstellung von Bilddaten verwendet. Es enthält Felder für Kodierung, Höhe, Breite und Pixeldaten und eignet sich daher für die Übertragung von Bildern, die von Kameras oder anderen Sensoren aufgenommen wurden. Bildnachrichten werden häufig in Robotikanwendungen für Aufgaben wie die visuelle Wahrnehmung verwendet. Objekterkennung durchzuführen, und Navigation.
Schritt-für-Schritt-Anleitung zur Bildverwendung
Der folgende Code-Schnipsel demonstriert, wie das Ultralytics YOLO-Paket mit ROS verwendet wird. In diesem Beispiel abonnieren wir ein Kamerathema, verarbeiten das eingehende Bild mit YOLO und veröffentlichen die erkannten Objekte in neuen Themen für Erkennung und Segmentierung.
Importieren Sie zunächst die erforderlichen Bibliotheken und instanziieren Sie zwei Modelle: eines für Segmentierung und eine für Erkennung. Initialisieren Sie einen ROS-Knoten (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, die dem Knoten genügend Zeit gibt, die Verbindung herzustellen, bevor er fortfährt.
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)
Initialisieren Sie zwei ROS-Themen: eines für Erkennung und eine für Segmentierung. Diese Topics werden verwendet, um die annotierten Bilder zu veröffentlichen, sodass sie für die weitere Verarbeitung zugänglich sind. Die Kommunikation zwischen den Knoten wird erleichtert durch sensor_msgs/Image
Nachrichten.
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)
Erstellen Sie abschließend einen Subscriber, der Nachrichten auf dem /camera/color/image_raw
Topic und ruft eine Callback-Funktion für jede neue Nachricht auf. Diese Callback-Funktion empfängt Nachrichten vom Typ sensor_msgs/Image
, wandelt sie mit Hilfe von in ein NumPy-Array um ros_numpy
verarbeitet die Bilder mit den zuvor instanziierten YOLO-Modellen, annotiert die Bilder und veröffentlicht sie dann wieder in den jeweiligen Themen: /ultralytics/detection/image
zur 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("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()
Debugging
Das Debugging von ROS-Knoten (Robot Operating System) kann aufgrund der verteilten Natur des Systems eine Herausforderung darstellen. Mehrere Tools können bei diesem Prozess helfen:
rostopic echo <TOPIC-NAME>
: Mit diesem Befehl können Sie Nachrichten anzeigen, die zu einem bestimmten Thema veröffentlicht wurden, sodass Sie den Datenfluss überprüfen können.rostopic list
: Verwenden Sie diesen Befehl, um alle verfügbaren Themen im ROS-System aufzulisten und sich einen Überblick über die aktiven Datenströme zu verschaffen.rqt_graph
: Dieses Visualisierungstool zeigt den Kommunikationsgraphen zwischen Knoten an und bietet Einblicke, wie Knoten miteinander verbunden sind und wie sie interagieren.- Für komplexere Visualisierungen, wie z. B. 3D-Darstellungen, können Sie RViz. RViz (ROS Visualization) ist ein leistungsstarkes 3D-Visualisierungstool für ROS. Es ermöglicht Ihnen, den Zustand Ihres Roboters und seiner Umgebung in Echtzeit zu visualisieren. Mit RViz können Sie Sensordaten anzeigen (z. B.
sensors_msgs/Image
), Roboter-Modellzustände und verschiedene andere Arten von Informationen, wodurch es einfacher wird, das Verhalten Ihres Robotersystems zu debuggen und zu verstehen.
Veröffentlichen erkannter Klassen mit std_msgs/String
Standard-ROS-Nachrichten beinhalten auch std_msgs/String
Nachrichten. In vielen Anwendungen ist es nicht erforderlich, das gesamte annotierte Bild erneut zu veröffentlichen; stattdessen werden nur die im Sichtfeld des Roboters vorhandenen Klassen benötigt. Das folgende Beispiel zeigt, wie man std_msgs/String
Nachrichten um die erkannten Klassen auf dem /ultralytics/detection/classes
Themenbereich. Diese Nachrichten sind schlanker und liefern wesentliche Informationen, was sie für verschiedene Anwendungen wertvoll macht.
Anwendungsbeispiel
Betrachten Sie einen Lagerroboter, der mit einer Kamera und einem Objekt ausgestattet ist Erkennungsmodell. Anstatt große, annotierte Bilder über das Netzwerk zu senden, kann der Roboter eine Liste der erkannten Klassen als std_msgs/String
Nachrichten. Wenn der Roboter beispielsweise Objekte wie "Box", "Palette" und "Gabelstapler" erkennt, veröffentlicht er diese Klassen in der /ultralytics/detection/classes
Themenbereich. Diese Informationen können dann von einem zentralen Überwachungssystem verwendet werden, um den Lagerbestand in Echtzeit zu verfolgen, die Pfadplanung des Roboters zu optimieren, um Hindernisse zu vermeiden, oder bestimmte Aktionen auszulösen, wie z. B. das Aufnehmen einer erkannten Kiste. Dieser Ansatz reduziert die für die Kommunikation erforderliche Bandbreite und konzentriert sich auf die Übertragung kritischer Daten.
Schritt-für-Schritt-Nutzung von Zeichenketten
Dieses Beispiel zeigt, wie das Ultralytics YOLO-Paket mit ROS verwendet werden kann. In diesem Beispiel abonnieren wir ein Kamerathema, verarbeiten das eingehende Bild mit YOLO und veröffentlichen die erkannten Objekte in einem neuen Thema. /ultralytics/detection/classes
mit std_msgs/String
Nachrichten verwendet. Die ros_numpy
Paket wird verwendet, um die ROS-Bildnachricht zur Verarbeitung mit YOLO in ein NumPy-Array zu konvertieren.
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()
Ultralytics mit ROS-Tiefenbildern verwenden
Zusätzlich zu RGB-Bildern unterstützt ROS Tiefenbilder, die Informationen über die Entfernung von Objekten von der Kamera liefern. Tiefenbilder sind entscheidend für Roboteranwendungen wie Hindernisvermeidung, 3D-Mapping und Lokalisierung.
Ein Tiefenbild ist ein Bild, bei dem jedes Pixel die Entfernung von der Kamera zu einem Objekt darstellt. Im Gegensatz zu RGB-Bildern, die Farben erfassen, erfassen Tiefenbilder räumliche Informationen, wodurch Roboter die 3D-Struktur ihrer Umgebung wahrnehmen können.
Erhalten von Tiefenbildern
Tiefenbilder können mit verschiedenen Sensoren aufgenommen werden:
- Stereo-Kameras: Verwenden Sie zwei Kameras, um die Tiefe basierend auf der Bilddisparität zu berechnen.
- Time-of-Flight (ToF) Kameras: Messen Sie die Zeit, die Licht benötigt, um von einem Objekt zurückzukehren.
- Sensoren für strukturiertes Licht: Projizieren Sie ein Muster und messen Sie dessen Verformung auf Oberflächen.
Verwendung von YOLO mit Tiefenbildern
In ROS werden Tiefenbilder durch die sensor_msgs/Image
Nachrichtentyp, der Felder für Kodierung, Höhe, Breite und Pixeldaten enthält. Das Kodierungsfeld für Tiefenbilder verwendet oft ein Format wie "16UC1", das eine 16-Bit-Ganzzahl ohne Vorzeichen pro Pixel angibt, wobei jeder Wert den Abstand zum Objekt darstellt. Tiefenbilder werden häufig in Verbindung mit RGB-Bildern verwendet, um eine umfassendere Ansicht der Umgebung zu ermöglichen.
Durch die Verwendung von YOLO ist es möglich, Informationen aus RGB- und Tiefenbildern zu extrahieren und zu kombinieren. Beispielsweise kann YOLO Objekte innerhalb eines RGB-Bildes erkennen, und diese Erkennung kann verwendet werden, um entsprechende Regionen im Tiefenbild zu lokalisieren. Dies ermöglicht die Extraktion präziser Tiefeninformationen für erkannte Objekte, wodurch die Fähigkeit des Roboters verbessert wird, 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 z. B. die Intel RealSense-Serie, liefern synchronisierte RGB- und Tiefenbilder, was es einfacher macht, Informationen aus beiden Quellen zu kombinieren. Wenn Sie separate RGB- und Tiefenkameras verwenden, ist es wichtig, diese zu kalibrieren, um eine genaue Ausrichtung zu gewährleisten.
Schrittweise Nutzung der Tiefeninformationen
In diesem Beispiel verwenden wir YOLO, um ein Bild zu segmentieren und die extrahierte Maske anzuwenden, um das Objekt im Tiefenbild zu segmentieren. Dies ermöglicht es uns, die Entfernung jedes Pixels des interessierenden Objekts vom Brennpunkt der Kamera zu bestimmen. Durch die Gewinnung dieser Entfernungsinformationen können wir die Distanz zwischen der Kamera und dem spezifischen Objekt in der Szene berechnen. Beginnen Sie mit dem Importieren der notwendigen Bibliotheken, dem Erstellen eines ROS-Knotens und dem Instanziieren eines Segmentierungsmodells und eines ROS-Themas.
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)
Als Nächstes definieren Sie eine Callback-Funktion, die die eingehende Tiefenbildnachricht verarbeitet. Die Funktion wartet auf die Tiefenbild- und RGB-Bildnachrichten, wandelt sie in NumPy-Arrays um und wendet das Segmentierungsmodell auf das RGB-Bild an. Anschließend wird die Segmentierungsmaske für jedes erkannte Objekt extrahiert und die durchschnittliche Entfernung des Objekts von der Kamera mithilfe des Tiefenbilds berechnet. Die meisten Sensoren haben eine maximale Entfernung, die als Clip-Entfernung bezeichnet wird, über die hinaus Werte als unendlich (np.inf
). Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen einen Wert zuzuweisen. 0
. Schließlich veröffentlicht es die erkannten Objekte zusammen mit ihren durchschnittlichen Entfernungen zum /ultralytics/detection/distance
Topic.
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("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()
Ultralytics mit ROS verwenden sensor_msgs/PointCloud2
Die sensor_msgs/PointCloud2
Nachrichtentyp ist eine Datenstruktur, die in ROS verwendet wird, um 3D-Punktwolkendaten darzustellen. Dieser Nachrichtentyp ist integraler Bestandteil von Robotikanwendungen und ermöglicht Aufgaben wie 3D-Kartierung, Objekterkennung und Lokalisierung.
Eine Punktwolke ist eine Sammlung von Datenpunkten, die innerhalb eines dreidimensionalen Koordinatensystems definiert sind. Diese Datenpunkte stellen die äußere Oberfläche eines Objekts oder einer Szene dar, die durch 3D-Scantechnologien erfasst wurde. Jeder Punkt in der Wolke hat X
, Y
und Z
Koordinaten, die seiner Position im Raum entsprechen, und können auch zusätzliche Informationen wie Farbe und Intensität enthalten.
Referenzrahmen
Bei der Arbeit mit sensor_msgs/PointCloud2
ist es unerlässlich, den Bezugsrahmen des Sensors zu berücksichtigen, von dem die Punktwolkendaten erfasst wurden. Die Punktwolke wird anfänglich im Bezugsrahmen des Sensors erfasst. Sie können diesen Bezugsrahmen bestimmen, indem Sie auf den/die/das hören: /tf_static
Topic. Abhängig von Ihren spezifischen Anwendungsanforderungen müssen Sie die Punktwolke möglicherweise in einen anderen Referenzrahmen konvertieren. Diese Transformation kann mit dem tf2_ros
Paket, das Werkzeuge für die Verwaltung von Koordinatensystemen und die Transformation von Daten zwischen ihnen bereitstellt.
Erhalten von Punktwolken
Punktwolken können mit verschiedenen Sensoren erfasst werden:
- LIDAR (Light Detection and Ranging): Verwendet Laserimpulse, um Entfernungen zu Objekten zu messen und hoch-präzise 3D-Karten zu erstellen.
- Tiefenkameras: Erfassen Tiefeninformationen für jedes Pixel und ermöglichen so die 3D-Rekonstruktion der Szene.
- Stereo-Kameras: Verwenden Sie zwei oder mehr Kameras, um Tiefeninformationen durch Triangulation zu erhalten.
- Strukturierte Lichtscanner: Projizieren ein bekanntes Muster auf eine Oberfläche und messen die Verformung, um die Tiefe zu berechnen.
Verwendung von YOLO mit Punktwolken
Zur Integration von YOLO mit sensor_msgs/PointCloud2
Nachrichtentypen können wir eine ähnliche Methode wie für Tiefenkarten anwenden. Indem wir die in der Punktwolke eingebettete Farbinformation nutzen, können wir ein 2D-Bild extrahieren, eine Segmentierung dieses Bildes mit YOLO durchführen und dann die resultierende Maske auf die dreidimensionalen Punkte anwenden, um das 3D-Objekt von Interesse zu isolieren.
Für die Verarbeitung von Punktwolken empfehlen wir die Verwendung von Open3D (pip install open3d
), eine benutzerfreundliche Python-Bibliothek. Open3D bietet robuste Werkzeuge für die 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 der YOLO-basierten Segmentierung zu bearbeiten und zu analysieren.
Schrittweise Nutzung von Punktwolken
Importieren Sie die notwendigen Bibliotheken und instanziieren Sie 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("yolo11m-seg.pt")
Funktion erstellen pointcloud2_to_array
, das ein/e/n transformiert sensor_msgs/PointCloud2
Nachricht in zwei NumPy-Arrays. Die sensor_msgs/PointCloud2
Nachrichten enthalten n
Punkte basierend auf den width
und height
des aufgenommenen Bildes. Zum Beispiel ein 480 x 640
Bild wird haben 307,200
Punkte. Jeder Punkt enthält drei räumliche Koordinaten (xyz
) und die entsprechende Farbe in RGB
Format vorliegen. Diese können als zwei separate Informationskanäle betrachtet werden.
Die Funktion gibt Folgendes zurück: xyz
Koordinaten und RGB
Werte im Format der ursprünglichen Kameraauflösung (width x height
). Die meisten Sensoren haben eine maximale Entfernung, die als Clip-Distanz bezeichnet wird, über die hinaus Werte als inf (np.inf
). Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen einen Wert zuzuweisen. 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
Abonnieren Sie als Nächstes den /camera/depth/points
Topic, um die Punktwolken-Nachricht zu empfangen und die sensor_msgs/PointCloud2
Nachricht in NumPy-Arrays, die die XYZ-Koordinaten und RGB-Werte enthalten (unter Verwendung der pointcloud2_to_array
Funktion). Verarbeiten Sie das RGB-Bild mit dem YOLO-Modell, um segmentierte Objekte zu extrahieren. Extrahieren Sie für jedes erkannte Objekt die Segmentierungsmaske und wenden Sie 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
die das Vorhandensein des Objekts anzeigt, und 0
die das Fehlen angibt. Um die Maske anzuwenden, multiplizieren Sie einfach die ursprünglichen Kanäle mit der Maske. Diese Operation isoliert effektiv das interessierende Objekt innerhalb des Bildes. Erstellen Sie abschließend ein Open3D-Punktwolkenobjekt und visualisieren Sie 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("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
Was ist das Robot Operating System (ROS)?
Das Robot Operating System (ROS) ist ein Open-Source-Framework, das häufig in der Robotik verwendet wird, um Entwicklern bei der Erstellung robuster Roboteranwendungen zu helfen. Es bietet eine Sammlung von Bibliotheken und Tools zum Aufbau von und zur Schnittstellenbildung mit Robotersystemen, wodurch die Entwicklung komplexer Anwendungen erleichtert wird. ROS unterstützt die Kommunikation zwischen Knotenpunkten mithilfe von Nachrichten über Themen oder Dienste.
Wie integriere ich Ultralytics YOLO mit ROS für die Echtzeit-Objekterkennung?
Die Integration von Ultralytics YOLO mit ROS umfasst die Einrichtung einer ROS-Umgebung und die Verwendung von YOLO zur Verarbeitung von Sensordaten. Beginnen Sie mit der Installation der erforderlichen Abhängigkeiten wie ros_numpy
und Ultralytics YOLO:
pip install ros_numpy ultralytics
Als Nächstes erstellen Sie einen ROS-Knoten und abonnieren ein Bildthema, um die eingehenden Daten zu verarbeiten. Hier ist ein Minimalbeispiel:
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()
Was sind ROS-Themen und wie werden sie in Ultralytics YOLO verwendet?
ROS-Themen erleichtern die Kommunikation zwischen Knoten in einem ROS-Netzwerk mithilfe eines Publish-Subscribe-Modells. Ein Thema ist ein benannter Kanal, über den Knoten asynchron Nachrichten senden und empfangen. Im Kontext von Ultralytics YOLO können Sie einen Knoten einrichten, der ein Bildthema abonniert, die Bilder mit YOLO für Aufgaben wie Erkennung oder Segmentierung verarbeitet und Ergebnisse in neuen Themen veröffentlicht.
Abonnieren Sie beispielsweise ein Kameratopic und verarbeiten Sie das eingehende Bild zur Erkennung:
rospy.Subscriber("/camera/color/image_raw", Image, callback)
Warum Tiefenbilder mit Ultralytics YOLO in ROS verwenden?
Tiefenbilder in ROS, dargestellt durch sensor_msgs/Image
liefern die Entfernung von Objekten von der Kamera, die für Aufgaben wie Hindernisvermeidung, 3D-Kartierung und Lokalisierung entscheidend ist. Durch Verwendung von Tiefeninformationen zusammen mit RGB-Bildern können Roboter ihre 3D-Umgebung besser verstehen.
Mit YOLO können Sie Segmentierungsmasken aus RGB-Bildern extrahieren und diese Masken auf Tiefenbilder anwenden, um präzise 3D-Objektinformationen zu erhalten, wodurch die Fähigkeit des Roboters verbessert wird, sich in seiner Umgebung zu bewegen und mit ihr zu interagieren.
Wie kann ich 3D-Punktwolken mit YOLO in ROS visualisieren?
So visualisieren Sie 3D-Punktwolken in ROS mit YOLO:
- Konvertieren
sensor_msgs/PointCloud2
Nachrichten in NumPy-Arrays um. - Verwenden Sie YOLO, um RGB-Bilder zu segmentieren.
- Wenden Sie die Segmentierungsmaske auf die Punktwolke an.
Hier ist ein Beispiel für die Visualisierung mit Open3D:
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])
Dieser Ansatz bietet eine 3D-Visualisierung von segmentierten Objekten, die für Aufgaben wie Navigation und Manipulation in Robotikanwendungen nützlich ist.