ROS (Robot Operating System) Kurzanleitung
ROS Introduction (captioned) from Open Robotics on 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 Werkzeugen, die Entwicklern helfen, Roboteranwendungen zu erstellen. ROS ist so konzipiert, dass es mit verschiedenen Roboterplattformen zusammenarbeitet, was es zu einem flexiblen und leistungsstarken Werkzeug für Robotiker macht.
Hauptmerkmale von ROS
-
Modulare Architektur: ROS hat eine modulare Architektur, die es Entwicklern ermöglicht, komplexe Systeme zu bauen, indem sie kleinere, wiederverwendbare Komponenten, sogenannte Nodes, kombinieren. Jeder Knoten erfüllt in der Regel eine bestimmte Funktion, und die Knoten kommunizieren untereinander mit Nachrichten über Topics oder Dienste.
-
Kommunikations-Middleware: ROS bietet eine robuste Kommunikationsinfrastruktur, die Kommunikation zwischen Prozessen und verteiltes Rechnen unterstützt. Dies wird durch ein Publish-Subscribe-Modell für Datenströme (Topics) und ein Request-Reply-Modell für Dienstaufrufe erreicht.
-
Hardware-Abstraktion: ROS bietet eine Abstraktionsebene über der Hardware, die es Entwicklern ermöglicht, geräteunabhängigen Code zu schreiben. So kann derselbe Code mit verschiedenen Hardwarekonfigurationen verwendet werden, was die Integration und das Experimentieren erleichtert.
-
Werkzeuge und Dienstprogramme: ROS wird mit einer Vielzahl von Tools und Dienstprogrammen für die Visualisierung, Fehlersuche und Simulation geliefert. RViz wird zum Beispiel zur Visualisierung von Sensordaten und Roboterzustandsinformationen 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 gibt zahlreiche Pakete für verschiedene Roboteranwendungen wie Navigation, Manipulation, Wahrnehmung und mehr. Die Community trägt aktiv zur Entwicklung und Pflege dieser Pakete bei.
Entwicklung der ROS-Versionen
Seit seiner Entwicklung im Jahr 2007 wurde ROS in mehreren Versionen weiterentwickelt, die jeweils neue Funktionen und Verbesserungen enthielten, um den wachsenden Anforderungen der Robotik-Gemeinschaft gerecht zu werden. Die Entwicklung von ROS kann in zwei Hauptreihen eingeteilt werden: 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 aber auch mit früheren Versionen funktionieren.
ROS 1 im Vergleich zu ROS 2
Während ROS 1 eine solide Grundlage für die Roboterentwicklung bot, behebt ROS 2 seine Mängel, indem es Folgendes bietet:
- Leistung in Echtzeit: Verbesserte Unterstützung für Echtzeitsysteme und deterministisches Verhalten.
- Sicherheit: Verbesserte Sicherheitsfunktionen für einen sicheren und zuverlässigen Betrieb in verschiedenen Umgebungen.
- Skalierbarkeit: Bessere Unterstützung für Multi-Roboter-Systeme und groß angelegte Einsätze.
- Plattformübergreifende 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 Kommunikation zwischen den Prozessen.
ROS Botschaften und Themen
In ROS wird die Kommunikation zwischen Knoten durch Nachrichten und Themen erleichtert. Eine Nachricht ist eine Datenstruktur, die die zwischen den Knoten ausgetauschten Informationen definiert, während ein Thema ein benannter Kanal ist, über den Nachrichten gesendet und empfangen werden. Knoten können Nachrichten in einem Topic veröffentlichen oder Nachrichten aus einem Topic abonnieren, um miteinander zu kommunizieren. Dieses Publish-Subscribe-Modell ermöglicht eine asynchrone Kommunikation und Entkopplung zwischen den Knotenpunkten. Jeder Sensor oder Aktor in einem Robotersystem veröffentlicht normalerweise Daten in einem Topic, die dann von anderen Knoten zur Verarbeitung oder Steuerung genutzt werden können. In diesem Leitfaden konzentrieren wir uns auf Bild-, Tiefen- und PointCloud-Nachrichten sowie auf Kamerathemen.
Aufrichtend Ultralytics YOLO mit ROS
Diese Anleitung wurde mit dieser ROS-Umgebung getestet, die ein Fork des ROSbot ROS-Repository ist. Diese Umgebung enthält das Paket Ultralytics YOLO , einen Docker-Container für die einfache Einrichtung, umfassende ROS-Pakete und Gazebo-Welten für schnelle Tests. Sie ist für den Husarion ROSbot 2 PRO konzipiert. Die mitgelieferten 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: Es wird für die schnelle Konvertierung zwischen ROS Image-Nachrichten und Numpy-Arrays benötigt.
-
Ultralytics Paket:
Gebrauchen Ultralytics mit ROS sensor_msgs/Image
Die sensor_msgs/Image
Nachrichtentyp is commonly used in ROS for representing image data. It contains fields for encoding, height, width, and pixel data, making it suitable for transmitting images captured by cameras or other sensors. Image messages are widely used in robotic applications for tasks such as visual perception, object detection, and navigation.
Bild-für-Schritt-Nutzung
Der folgende Codeschnipsel zeigt, wie du das Paket Ultralytics YOLO mit ROS nutzen kannst. In diesem Beispiel abonnieren wir ein Kamerathema, verarbeiten das eingehende Bild mit YOLO und veröffentlichen die erkannten Objekte in neuen Themen zur Erkennung und Segmentierung.
Importiere zunächst die erforderlichen Bibliotheken und instanziiere zwei Modelle: eines für Segmentierung und eine für Erkennung. Initialisiere 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, damit der Knoten genügend Zeit hat, die Verbindung herzustellen, bevor er fortfährt.
import time
import rospy
from ultralytics import YOLO
detection_model = YOLO("yolov8m.pt")
segmentation_model = YOLO("yolov8m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
Initialisiere zwei ROS-Themen: eines für Erkennung und eine für Segmentierung. Diese Themen werden verwendet, um die kommentierten Bilder zu veröffentlichen und sie für die weitere Bearbeitung zugänglich zu machen. Die Kommunikation zwischen den Knotenpunkten wird durch sensor_msgs/Image
Meldungen.
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 Abonnenten, der Nachrichten auf der Registerkarte /camera/color/image_raw
und ruft für jede neue Nachricht eine Callback-Funktion auf. Diese Callback-Funktion empfängt Nachrichten vom Typ sensor_msgs/Image
konvertiert sie in ein numpy-Array mit ros_numpy
verarbeitet die Bilder mit den zuvor instanziierten YOLO Modelle, kommentiert die Bilder und veröffentlicht sie dann wieder zu den jeweiligen Themen: /ultralytics/detection/image
zur Detektion 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("yolov8m.pt")
segmentation_model = YOLO("yolov8m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)
def callback(data):
"""Callback function to process image and publish annotated images."""
array = ros_numpy.numpify(data)
if det_image_pub.get_num_connections():
det_result = detection_model(array)
det_annotated = det_result[0].plot(show=False)
det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
if seg_image_pub.get_num_connections():
seg_result = segmentation_model(array)
seg_annotated = seg_result[0].plot(show=False)
seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
rospy.spin()
Debuggen
Das Debuggen 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, um den Datenfluss zu überprüfen.rostopic list
: Mit diesem Befehl listen Sie alle verfügbaren Themen im ROS-System auf und erhalten einen Überblick über die aktiven Datenströme.rqt_graph
: Dieses Visualisierungstool zeigt das Kommunikationsdiagramm zwischen Knoten an und bietet Einblicke in die Verbindung von Knoten und deren Interaktion.- Für komplexere Visualisierungen, wie z. B. 3D-Darstellungen, kannst du RViz. RViz (ROS Visualization) ist ein leistungsstarkes 3D-Visualisierungstool für ROS. Es ermöglicht es dir, den Zustand deines Roboters und seiner Umgebung in Echtzeit zu visualisieren. Mit RViz kannst du Sensordaten anzeigen (z. B.
sensors_msgs/Image
), Robotermodellzustände und verschiedene andere Arten von Informationen, die das Debuggen und Verstehen des Verhaltens Ihres Robotersystems erleichtern.
Erkannte Klassen veröffentlichen mit std_msgs/String
Zu den Standard-ROS-Nachrichten gehören auch std_msgs/String
Meldungen. In vielen Anwendungen ist es nicht erforderlich, das gesamte kommentierte Bild erneut zu veröffentlichen. Stattdessen werden nur die Klassen benötigt, die in der Ansicht des Roboters vorhanden sind. Das folgende Beispiel zeigt, wie Sie std_msgs/String
Nachrichten um die gefundenen Klassen auf der Website zu veröffentlichen. /ultralytics/detection/classes
Thema. Diese Nachrichten sind leichter und liefern wichtige Informationen, was sie für verschiedene Anwendungen wertvoll macht.
Beispiel für einen Anwendungsfall
Betrachten wir einen Lagerhausroboter, der mit einer Kamera und einem Objekt Erkennungsmodell. Anstatt große kommentierte Bilder über das Netzwerk zu senden, kann der Roboter eine Liste der erkannten Klassen als std_msgs/String
Meldungen. Wenn der Roboter beispielsweise Objekte wie "Box", "Palette" und "Gabelstapler" erkennt, veröffentlicht er diese Klassen an die /ultralytics/detection/classes
Thema. Diese Informationen können dann von einem zentralen Überwachungssystem verwendet werden, um den Bestand in Echtzeit zu verfolgen, die Wegplanung des Roboters zu optimieren, um Hindernissen auszuweichen, oder bestimmte 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.
Schritt-für-Schritt-Verwendung von Strings
In diesem Beispiel wird veranschaulicht, wie Sie die Ultralytics YOLO Paket mit ROS. In diesem Beispiel abonnieren wir ein Kamerathema, verarbeiten das eingehende Bild mit YOLOund veröffentlichen Sie die erkannten Objekte in einem neuen Thema /ultralytics/detection/classes
benutzend std_msgs/String
Meldungen. Das ros_numpy
-Paket wird verwendet, um die ROS-Image-Nachricht in ein numpy-Array für die Verarbeitung mit YOLO.
import time
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String
from ultralytics import YOLO
detection_model = YOLO("yolov8m.pt")
rospy.init_node("ultralytics")
time.sleep(1)
classes_pub = rospy.Publisher("/ultralytics/detection/classes", String, queue_size=5)
def callback(data):
"""Callback function to process image and publish detected classes."""
array = ros_numpy.numpify(data)
if classes_pub.get_num_connections():
det_result = detection_model(array)
classes = det_result[0].boxes.cls.cpu().numpy().astype(int)
names = [det_result[0].names[i] for i in classes]
classes_pub.publish(String(data=str(names)))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
rospy.spin()
Gebrauchen Ultralytics mit ROS-Tiefenbildern
Zusätzlich zu RGB-Bildern unterstützt ROS auch Tiefenbilder, die Informationen über die Entfernung von Objekten zur Kamera liefern. Tiefenbilder sind entscheidend für Roboteranwendungen wie Hindernisvermeidung, 3D-Kartierung und Lokalisierung.
Ein Tiefenbild ist ein Bild, bei dem jedes Pixel den Abstand von der Kamera zu einem Objekt darstellt. Im Gegensatz zu RGB-Bildern, die Farbe erfassen, erfassen Tiefenbilder räumliche Informationen, sodass Roboter die 3D-Struktur ihrer Umgebung wahrnehmen können.
Abrufen von Tiefenbildern
Tiefenbilder können mit verschiedenen Sensoren aufgenommen werden:
- Stereokameras: Verwende zwei Kameras, um die Tiefe anhand des Bildunterschieds zu berechnen.
- Time-of-Flight (ToF) Kameras: Sie messen die Zeit, die das Licht braucht, um von einem Objekt zurückzukehren.
- Strukturierte Lichtsensoren: Projiziere ein Muster und messe seine Verformung auf Oberflächen.
Benutzend YOLO mit Tiefenbildern
In ROS werden Tiefenbilder durch die sensor_msgs/Image
Nachrichtentyp, der Felder für Codierung, Höhe, Breite und Pixeldaten enthält. Das Codierungsfeld für Tiefenbilder verwendet häufig ein Format wie "16UC1", das eine 16-Bit-Ganzzahl ohne Vorzeichen pro Pixel angibt, wobei jeder Wert die Entfernung zum Objekt darstellt. Tiefenbilder werden häufig in Verbindung mit RGB-Bildern verwendet, um einen umfassenderen Überblick über die Umgebung zu erhalten.
Benutzend YOLOist es möglich, Informationen sowohl aus RGB- als auch aus Tiefenbildern zu extrahieren und zu kombinieren. Zum Beispiel YOLO kann Objekte innerhalb eines RGB-Bildes erkennen, und diese Erkennung kann verwendet werden, um entsprechende Bereiche im Tiefenbild zu lokalisieren. Dies ermöglicht die Extraktion präziser Tiefeninformationen für erkannte Objekte und verbessert die Fähigkeit des Roboters, seine Umgebung in drei Dimensionen zu verstehen.
RGB-D-Kameras
Bei der Arbeit mit Tiefenbildern ist es wichtig, dass die RGB- und Tiefenbilder korrekt aufeinander abgestimmt sind. RGB-D-Kameras, wie z. B. die Intel RealSense-Serie, liefern synchronisierte RGB- und Tiefenbilder, so dass es einfacher ist, Informationen aus beiden Quellen zu kombinieren. Wenn du getrennte RGB- und Tiefenkameras verwendest, ist es wichtig, sie zu kalibrieren, um eine genaue Ausrichtung zu gewährleisten.
Tiefe Schritt-für-Schritt-Nutzung
In diesem Beispiel verwenden wir YOLO , um ein Bild zu segmentieren, und wenden Sie die extrahierte Maske an, um das Objekt im Tiefenbild zu segmentieren. Auf diese Weise können wir den Abstand jedes Pixels des interessierenden Objekts vom Brennpunkt der Kamera bestimmen. Durch den Erhalt dieser Entfernungsinformationen können wir den Abstand zwischen der Kamera und dem spezifischen Objekt in der Szene berechnen. Beginnen Sie mit dem Importieren der erforderlichen 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("yolov8m-seg.pt")
classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)
Definieren Sie als Nächstes eine Rückruffunktion, die die eingehende Tiefenbildnachricht verarbeitet. Die Funktion wartet auf die Tiefenbild- und RGB-Bildmeldungen, konvertiert sie in numpy-Arrays und wendet das Segmentierungsmodell auf das RGB-Bild an. Anschließend extrahiert es die Segmentierungsmaske für jedes erkannte Objekt und berechnet anhand des Tiefenbildes den durchschnittlichen Abstand des Objekts von der Kamera. Die meisten Sensoren haben einen maximalen Abstand, den sogenannten Clip-Abstand, ab dem Werte als inf (np.inf
). Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen den Wert 0
. Schließlich veröffentlicht es die erkannten Objekte zusammen mit ihren durchschnittlichen Entfernungen zum /ultralytics/detection/distance
Thema.
import numpy as np
import ros_numpy
from sensor_msgs.msg import Image
def callback(data):
"""Callback function to process depth image and RGB image."""
image = rospy.wait_for_message("/camera/color/image_raw", Image)
image = ros_numpy.numpify(image)
depth = ros_numpy.numpify(data)
result = segmentation_model(image)
for index, cls in enumerate(result[0].boxes.cls):
class_index = int(cls.cpu().numpy())
name = result[0].names[class_index]
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
obj = depth[mask == 1]
obj = obj[~np.isnan(obj)]
avg_distance = np.mean(obj) if len(obj) else np.inf
classes_pub.publish(String(data=str(all_objects)))
rospy.Subscriber("/camera/depth/image_raw", Image, callback)
while True:
rospy.spin()
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("yolov8m-seg.pt")
classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)
def callback(data):
"""Callback function to process depth image and RGB image."""
image = rospy.wait_for_message("/camera/color/image_raw", Image)
image = ros_numpy.numpify(image)
depth = ros_numpy.numpify(data)
result = segmentation_model(image)
for index, cls in enumerate(result[0].boxes.cls):
class_index = int(cls.cpu().numpy())
name = result[0].names[class_index]
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
obj = depth[mask == 1]
obj = obj[~np.isnan(obj)]
avg_distance = np.mean(obj) if len(obj) else np.inf
classes_pub.publish(String(data=str(all_objects)))
rospy.Subscriber("/camera/depth/image_raw", Image, callback)
while True:
rospy.spin()
Gebrauchen Ultralytics mit ROS sensor_msgs/PointCloud2
Die sensor_msgs/PointCloud2
Nachrichtentyp ist eine Datenstruktur, die in ROS verwendet wird, um 3D-Punktwolkendaten darzustellen. Dieser Nachrichtentyp ist ein wesentlicher Bestandteil von Roboteranwendungen und ermöglicht Aufgaben wie 3D-Kartierung, Objekterkennung und Lokalisierung.
Eine Punktwolke ist eine Sammlung von Datenpunkten, die in einem dreidimensionalen Koordinatensystem definiert sind. Diese Datenpunkte stellen die äußere Oberfläche eines Objekts oder einer Szene dar, die mit 3D-Scantechnologien erfasst wird. Jeder Punkt in der Cloud hat X
, Y
, und Z
Koordinaten, die seiner Position im Raum entsprechen und auch zusätzliche Informationen wie Farbe und Intensität enthalten können.
Bezugsrahmen
Bei der Arbeit mit sensor_msgs/PointCloud2
ist es wichtig, das Bezugssystem des Sensors zu berücksichtigen, von dem die Punktwolkendaten erfasst wurden. Die Punktwolke wird zunächst im Bezugssystem des Sensors erfasst. Sie können dieses Bezugssystem bestimmen, indem Sie auf die /tf_static
Thema. Abhängig von Ihren spezifischen Anwendungsanforderungen müssen Sie die Punktwolke jedoch möglicherweise in ein anderes Bezugssystem konvertieren. Diese Transformation kann mit der tf2_ros
-Paket, das Werkzeuge zum Verwalten von Koordinatenrahmen und zum Transformieren von Daten zwischen ihnen bereitstellt.
Abrufen von Punktwolken
Punktwolken können mit verschiedenen Sensoren erhalten werden:
- LIDAR (Light Detection and Ranging): Uses laser pulses to measure distances to objects and create high-precision 3D maps.
- Tiefenkameras: Erfassen Tiefeninformationen für jedes Pixel und ermöglichen so eine 3D-Rekonstruktion der Szene.
- Stereokameras: Nutze zwei oder mehr Kameras, um durch Triangulation Tiefeninformationen zu erhalten.
- Strukturierte Licht-Scanner: Sie projizieren ein bekanntes Muster auf eine Oberfläche und messen die Verformung, um die Tiefe zu berechnen.
Benutzend YOLO mit Punktwolken
Integrieren YOLO mit sensor_msgs/PointCloud2
können wir eine ähnliche Methode wie bei Tiefenkarten anwenden. Durch die Nutzung der in der Punktwolke eingebetteten Farbinformationen können wir ein 2D-Bild extrahieren und eine Segmentierung dieses Bildes mit YOLO, und wenden Sie dann die resultierende Maske auf die dreidimensionalen Punkte an, um das gewünschte 3D-Objekt zu isolieren.
Für den Umgang mit Punktwolken empfehlen wir die Verwendung von Open3D (pip install open3d
), eine benutzerfreundliche Python Bibliothek. Open3D bietet robuste Tools für die Verwaltung von Punktwolken-Datenstrukturen, 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-basierte Segmentierung.
Punktwolken Schritt für Schritt verwenden
Importieren Sie die erforderlichen Bibliotheken und instanziieren Sie die YOLO Modell für die Segmentierung.
import time
import rospy
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolov8m-seg.pt")
Erstellen einer Funktion pointcloud2_to_array
, die eine sensor_msgs/PointCloud2
message in zwei numpy-Arrays ein. Das sensor_msgs/PointCloud2
Nachrichten enthalten n
Punkte auf der Grundlage der width
und height
des aufgenommenen Bildes. Zum Beispiel ist ein 480 x 640
Bild hat 307,200
Punkte. Jeder Punkt enthält drei Raumkoordinaten (xyz
) und die entsprechende Farbe in RGB
Format. Diese können als zwei getrennte Informationskanäle betrachtet werden.
Die Funktion gibt die xyz
Koordinaten und RGB
Werte im Format der ursprünglichen Kameraauflösung (width x height
). Die meisten Sensoren haben einen maximalen Abstand, den sogenannten Clip-Abstand, ab dem Werte als inf (np.inf
). Vor der Verarbeitung ist es wichtig, diese Nullwerte herauszufiltern und ihnen den Wert 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 die /camera/depth/points
, um die Punktwolkenmeldung zu empfangen und die sensor_msgs/PointCloud2
numpy-Arrays mit den XYZ-Koordinaten und RGB-Werten (mit dem pointcloud2_to_array
Funktion). Verarbeiten Sie das RGB-Bild mit der 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 einfach, da sie aus binären Werten besteht, mit 1
das Vorhandensein des Objekts anzuzeigen und 0
die Abwesenheit angeben. Um die Maske anzuwenden, multiplizieren Sie einfach die Originalkanäle mit der Maske. Dieser Vorgang isoliert effektiv das Objekt von Interesse 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 ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolov8m-seg.pt")
def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
"""
Convert a ROS PointCloud2 message to a numpy array.
Args:
pointcloud2 (PointCloud2): the PointCloud2 message
Returns:
(tuple): tuple containing (xyz, rgb)
"""
pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
nan_rows = np.isnan(xyz).all(axis=2)
xyz[nan_rows] = [0, 0, 0]
rgb[nan_rows] = [0, 0, 0]
return xyz, rgb
ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)
if not len(result[0].boxes.cls):
print("No objects detected")
sys.exit()
classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
mask_expanded = np.stack([mask, mask, mask], axis=2)
obj_rgb = rgb * mask_expanded
obj_xyz = xyz * mask_expanded
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
o3d.visualization.draw_geometries([pcd])
FAQ
Was ist das Robot Operating System (ROS)?
Das Robot Operating System (ROS) ist ein Open-Source-Framework, das in der Robotik häufig verwendet wird, um Entwicklern bei der Erstellung robuster Roboteranwendungen zu helfen. Es bietet eine Sammlung von Bibliotheken und Werkzeugen für den Aufbau von und den Umgang mit Robotersystemen und erleichtert die Entwicklung komplexer Anwendungen. ROS unterstützt die Kommunikation zwischen Knotenpunkten mit Hilfe von Nachrichten über Topics oder Dienste.
Wie integriere ich Ultralytics YOLO mit ROS zur Objekterkennung in Echtzeit?
Um Ultralytics YOLO in ROS zu integrieren, musst du eine ROS-Umgebung einrichten und YOLO für die Verarbeitung von Sensordaten verwenden. Beginne mit der Installation der erforderlichen Abhängigkeiten wie ros_numpy
und Ultralytics YOLO :
Als nächstes erstellst du einen ROS-Knoten und abonnierst ein Bildthema, um die eingehenden Daten 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("yolov8m.pt")
rospy.init_node("ultralytics")
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
def callback(data):
array = ros_numpy.numpify(data)
det_result = detection_model(array)
det_annotated = det_result[0].plot(show=False)
det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
rospy.spin()
Was sind ROS-Themen und wie werden sie in Ultralytics YOLO verwendet?
ROS-Topics erleichtern die Kommunikation zwischen Knoten in einem ROS-Netzwerk, indem sie ein Publish-Subscribe-Modell verwenden. Ein Topic ist ein benannter Kanal, den Knoten nutzen, um Nachrichten asynchron zu senden und zu empfangen. Im Kontext von Ultralytics YOLO kannst du einen Knoten dazu bringen, ein Bildthema zu abonnieren, die Bilder mit YOLO für Aufgaben wie Erkennung oder Segmentierung zu verarbeiten und die Ergebnisse in neuen Themen zu veröffentlichen.
Abonniere zum Beispiel ein Kamerathema und verarbeite das eingehende Bild zur Erkennung:
Warum Tiefenbilder mit Ultralytics YOLO in ROS verwenden?
Tiefenbilder in ROS, dargestellt durch sensor_msgs/Image
liefern die Entfernung von Objekten zur Kamera, die für Aufgaben wie Hindernisvermeidung, 3D-Kartierung und Lokalisierung entscheidend ist. Unter Verwendung 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 Masken auf Tiefenbilder anwenden, um präzise 3D-Objektinformationen zu erhalten und so die Fähigkeit des Roboters zu verbessern, zu navigieren und mit seiner Umgebung zu interagieren.
Wie kann ich 3D-Punktwolken mit YOLO in ROS visualisieren?
Um 3D-Punktwolken in ROS mit YOLO zu visualisieren:
- Konvertieren
sensor_msgs/PointCloud2
Nachrichten in Numpy-Arrays. - Verwende YOLO , um RGB-Bilder zu segmentieren.
- Wende die Segmentierungsmaske auf die Punktwolke an.
Hier ist ein Beispiel, das Open3D für die Visualisierung verwendet:
import sys
import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2
from ultralytics import YOLO
rospy.init_node("ultralytics")
segmentation_model = YOLO("yolov8m-seg.pt")
def pointcloud2_to_array(pointcloud2):
pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
return xyz, rgb
ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)
if not len(result[0].boxes.cls):
print("No objects detected")
sys.exit()
classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
mask_expanded = np.stack([mask, mask, mask], axis=2)
obj_rgb = rgb * mask_expanded
obj_xyz = xyz * mask_expanded
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((-1, 3)))
pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((-1, 3)) / 255)
o3d.visualization.draw_geometries([pcd])
Dieser Ansatz bietet eine 3D-Visualisierung von segmentierten Objekten, die für Aufgaben wie Navigation und Manipulation nützlich ist.