ROS: Tiefenkarte auf dem Raspberry Pi "low blood"

Bild

Wenn Sie beim Erstellen von Robotern ROS verwenden, wissen Sie wahrscheinlich, dass es die Arbeit mit Stereokameras unterstützt. Sie können beispielsweise eine Tiefenkarte des sichtbaren Teils des Raums oder eine Punktwolke erstellen. Und ich fragte mich, wie einfach es sein würde, eine Himbeer-basierte StereoPi-Stereokamera in ROS zu verwenden. Früher war ich bereits davon überzeugt, dass die Tiefenkarte perfekt von OpenCV erstellt wurde, aber ich habe mich nie mit ROS befasst. Und ich habe beschlossen, es zu versuchen. Ich möchte über meine Abenteuer bei der Suche nach einer Lösung sprechen.

1. Gibt es ROS auf dem Raspberry Pi?


Zuerst habe ich mich entschlossen herauszufinden, ob es möglich ist, ROS für den Raspberry Pi zu bauen. Das erste, was Google mir sagte, war eine Liste mit Anweisungen zum Installieren verschiedener Versionen von ROS auf dem Raspberry Pi, nämlich diese ROS-Wiki- Seite

Nun, es gibt schon etwas, von dem aus man anfangen kann! Ich erinnerte mich gut daran, wie lange es gedauert hatte, OpenCV auf Raspberry zu erstellen (ungefähr acht Stunden), und entschied mich daher, nach vorgefertigten Bildern von MicroSD-Karten zu suchen, um Zeit zu sparen.

2. Gibt es fertige microSD-Kartenbilder mit ROS für Himbeere?


Es stellte sich heraus, dass dieses Problem bereits von mehreren Entwicklungsteams behoben wurde. Wenn Sie keine einmaligen Builds von Enthusiasten erstellen, fallen einige Bilder auf, die mit der Veröffentlichung neuer Versionen von OS und ROS ständig aktualisiert werden.

Die erste Option ist ROS, das vom ROSbots-Team auf dem nativen Raspbian-Betriebssystem installiert wurde. Hier ist eine Seite mit einem aktualisierten Image-Link: Ready-to-Use-Image-Raspbian-Stretch-Ros-OpenCV

Das zweite sind die Bilder von Ubiquiti Robotics auf Ubuntu .

Nun, die zweite Frage war auch schnell genug geschlossen. Es ist Zeit, tiefer zu tauchen.

3. Wie funktioniert ROS mit der Raspberry Pi-Kamera?


Und welche Stereokameras werden in ROS generell unterstützt? Ich habe mir die Seite mit Stereokameras angesehen, für die die Verfügbarkeit von vorgefertigten Treibern für ROS deklariert wurde:

wiki.ros.org/Sensors

Es gab zwei Abschnitte:
2.3 3D-Sensoren (Entfernungsmesser und RGB-D-Kameras)
2.5 Kameras
Es stellte sich heraus, dass im ersten Abschnitt nicht nur Stereokameras, sondern auch TOF-Sensoren und Scan-Lidars aufgelistet sind - im Allgemeinen alles, was sofort Informationen in 3D liefern kann. Und im zweiten gibt es schon Stereokameras. Der Versuch, Treiber für mehrere Stereokameras zu sehen, trug nicht zu meiner Freude bei, da dies auf ein ernsthaftes Eintauchen in den Code hindeutete.

Okay, tritt einen Schritt zurück. Wie funktioniert es mit einer einzelnen Raspberry Pi-Kamera in ROS?

Drei angenehme Überraschungen erwarteten mich hier:

  • Es stellt sich heraus, dass es für ROS einen speziellen raspicam_node- Knoten gibt, der nur für die Arbeit mit der Raspberry Pi-Kamera vorgesehen ist
  • Arten des Knotens liegen auf dem Github, der Code wird aktiv gepflegt und gut dokumentiert: github.com/UbiquityRobotics/raspicam_node
  • Der Autor des Rohan Agrawal-Knotens ( @Rohbotics ) arbeitet für ein Unternehmen, das eines der vorgefertigten Bilder für den Raspberry Pi aktiv unterstützt

Ich habe mir das Github-Repository raspicam_node angesehen und mir Probleme angesehen. Dort fand ich vor fast sieben Monaten ein offenes Problem mit dem geräumigen Namen "Stereomodus", ohne Antworten und Kommentare. Tatsächlich haben sich darin alle Ereignisse weiterentwickelt.

4. Hardcore oder nicht?


Um den Autoren keine Kinderfragen zu stellen, habe ich mich entschlossen, den Quellcode zu betrachten und die Gefahr des Hinzufügens des Stereomodus zu bewerten. Der Systemteil hier interessierte mich mehr: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/src
Nun, die Jungs haben geschrieben, dass der Fahrer in das MMAL-Level eingestiegen ist. Ich erinnerte mich auch daran, dass der Quellcode für Himbeeren im Stereomodus ebenfalls offen ist (die Entwicklung kann hier im Himbeerforum verfolgt werden ) und die Aufgabe, einen vollwertigen Stereotreiber zu schreiben, lösbar, aber umfangreich ist. Als ich mir die Beschreibung der Treiber anderer Kameras ansah, wurde mir klar, dass es notwendig war, nicht nur das linke und das rechte Bild zu veröffentlichen, sondern auch die Parameter beider Kameras anzugeben, Kalibrierungsergebnisse auf jede Kamera anzuwenden und viele andere Dinge zu tun. Dies zog Experimente ein oder zwei Monate lang an. Aus diesem Grund habe ich mich entschlossen, den Ansatz zu parallelisieren: Schreiben Sie dem Autor eine Frage zur Stereo-Unterstützung und suchen Sie selbst nach einer einfacheren, aber funktionierenden Lösung.

5. Dialoge mit dem Autor


Im Thread über den Stereomodus auf dem Github stellte ich dem Autor eine Frage, in der ich erwähnte, dass die Stereoanlage seit 2014 von Himbeeren unterstützt wird, und schlug vor, ihm bei Bedarf ein Debugboard für Experimente zu senden. Ich möchte Sie daran erinnern, dass ich immer noch daran gezweifelt habe, dass die Stereoanlage in dieser Distribution wie in der einheimischen Raspbian funktioniert.

Rohan antwortete überraschend schnell und sagte, dass ihre Distribution einen Himbeerkern verwendet und alles funktionieren sollte. Und bat darum, es auf einer ihrer Baugruppen zu überprüfen.

Himbeerkern! Hurra! Theoretisch sollte ein Stereobild aufgenommen werden, ohne mit einem Tamburin zu tanzen!

Ich habe das neueste Image über einen Link von Rohan heruntergeladen und bereitgestellt und ein einfaches Python-Skript ausgeführt, um ein Stereopaar zu erfassen. Es hat funktioniert!

Bild

Danach schrieb Rohan, dass er sich den Treibercode für den Stereomodus ansehen würde, und schrieb ein paar Fragen. Zum Beispiel erzeugt unser Stereomodus ein geklebtes Bild, und wir müssten es in zwei Teile schneiden - links und rechts. Und die zweite Frage zu den Kalibrierungsparametern jeder Kamera ist, wie man damit umgeht.

Ich sagte, dass Sie als ersten Schritt unabhängig voneinander Bilder von Kameras aufnehmen können. Ja, sie werden in Bezug auf Aufnahmezeit und Einstellungen (z. B. Helligkeitskontrast-Weißabgleich) nicht synchronisiert. In einem ersten Schritt kann dies jedoch zu Problemen führen.

Rohan hat umgehend einen Patch veröffentlicht , mit dem Sie direkt von ROS aus festlegen können, von welcher Kamera Bilder aufgenommen werden sollen. Ich habe es überprüft - die Auswahl einer Kamera funktioniert, es ist bereits ein hervorragendes Ergebnis.

6. Unerwartete Hilfe


Und dann erscheint ein Kommentar des Wezzoid-Benutzers im Thread. Er sagte, dass er ein Projekt basierend auf einer Stereokamera auf einem Pi Compute 3 mit Himbeer-Devboards mache. Sein vierbeiniger Laufroboter verfolgte die Position eines Objekts im Weltraum, änderte die Position der Kameras und hielt einen festgelegten Abstand dazu (das Projekt ist hier auf hackaday.io veröffentlicht).

Bild

Und er teilte den Code, in dem er das Bild aufgenommen hatte, schnitt es mit Python in zwei Hälften und teilte es wie Knoten der linken und rechten Kamera.
Python ist in diesen Angelegenheiten kein sehr schneller Freund, daher verwendete er eine niedrige Auflösung von 320x240 und einen guten Life-Hack. Wenn wir ein Side-by-Sibe-Stereobild aufnehmen (eine Kamera links vom Stereobild, die zweite rechts), sollte die Python jede der 240 Zeilen halbieren. Wenn Sie jedoch ein Bild von oben nach unten erstellen (die linke Kamera ist die obere Hälfte des Rahmens, die rechte die untere), schneidet die Python das Array in einem Arbeitsgang in zwei Hälften. Was vom Benutzer Wezzoid erfolgreich durchgeführt wurde.
Außerdem hat er seinen Python-Code auf Pastebin gepostet, der diese Operation ausgeführt hat. Da ist er:

Wezzoid-Code zum Veröffentlichen von Knoten zweier Kameras aus einem Stereopaar
#!/usr/bin/env python # picamera stereo ROS node using dual CSI Pi CS3 board # Wes Freeman 2018 # modified from code by Adrian Rosebrock, pyimagesearch.com # and jensenb, https://gist.github.com/jensenb/7303362 from picamera.array import PiRGBArray from picamera import PiCamera import time import rospy from sensor_msgs.msg import CameraInfo, Image import yaml import io import signal # for ctrl-C handling import sys def parse_calibration_yaml(calib_file): with file(calib_file, 'r') as f: params = yaml.load(f) cam_info = CameraInfo() cam_info.height = params['image_height'] cam_info.width = params['image_width'] cam_info.distortion_model = params['distortion_model'] cam_info.K = params['camera_matrix']['data'] cam_info.D = params['distortion_coefficients']['data'] cam_info.R = params['rectification_matrix']['data'] cam_info.P = params['projection_matrix']['data'] return cam_info # cam resolution res_x = 320 #320 # per camera res_y = 240 #240 target_FPS = 15 # initialize the camera print "Init camera..." camera = PiCamera(stereo_mode = 'top-bottom',stereo_decimate=False) camera.resolution = (res_x, res_y*2) # top-bottom stereo camera.framerate = target_FPS # using several camera options can cause instability, hangs after a while camera.exposure_mode = 'antishake' #camera.video_stabilization = True # fussy about res? stream = io.BytesIO() # ---------------------------------------------------------- #setup the publishers print "init publishers" # queue_size should be roughly equal to FPS or that causes lag? left_img_pub = rospy.Publisher('left/image_raw', Image, queue_size=1) right_img_pub = rospy.Publisher('right/image_raw', Image, queue_size=1) left_cam_pub = rospy.Publisher('left/camera_info', CameraInfo, queue_size=1) right_cam_pub = rospy.Publisher('right/camera_info', CameraInfo, queue_size=1) rospy.init_node('stereo_pub') # init messages left_img_msg = Image() left_img_msg.height = res_y left_img_msg.width = res_x left_img_msg.step = res_x*3 # bytes per row: pixels * channels * bytes per channel (1 normally) left_img_msg.encoding = 'rgb8' left_img_msg.header.frame_id = 'stereo_camera' # TF frame right_img_msg = Image() right_img_msg.height = res_y right_img_msg.width = res_x right_img_msg.step = res_x*3 right_img_msg.encoding = 'rgb8' right_img_msg.header.frame_id = 'stereo_camera' imageBytes = res_x*res_y*3 # parse the left and right camera calibration yaml files left_cam_info = parse_calibration_yaml('/home/pi/catkin_ws/src/mmstereocam/camera_info/left.yaml') right_cam_info = parse_calibration_yaml('/home/pi/catkin_ws/src/mmstereocam/camera_info/right.yaml') # --------------------------------------------------------------- # this is supposed to shut down gracefully on CTRL-C but doesn't quite work: def signal_handler(signal, frame): print 'CTRL-C caught' print 'closing camera' camera.close() time.sleep(1) print 'camera closed' sys.exit(0) signal.signal(signal.SIGINT, signal_handler) #----------------------------------------------------------- print "Setup done, entering main loop" framecount=0 frametimer=time.time() toggle = True # capture frames from the camera for frame in camera.capture_continuous(stream, format="rgb", use_video_port=True): framecount +=1 stamp = rospy.Time.now() left_img_msg.header.stamp = stamp right_img_msg.header.stamp = stamp left_cam_info.header.stamp = stamp right_cam_info.header.stamp = stamp left_cam_pub.publish(left_cam_info) right_cam_pub.publish(right_cam_info) frameBytes = stream.getvalue() left_img_msg.data = frameBytes[:imageBytes] right_img_msg.data = frameBytes[imageBytes:] #publish the image pair left_img_pub.publish(left_img_msg) right_img_pub.publish(right_img_msg) # console info if time.time() > frametimer +1.0: if toggle: indicator = ' o' # just so it's obviously alive if values aren't changing else: indicator = ' -' toggle = not toggle print 'approx publish rate:', framecount, 'target FPS:', target_FPS, indicator frametimer=time.time() framecount=0 # clear the stream ready for next frame stream.truncate(0) stream.seek(0) 


7. Veröffentlichen Sie die Knoten der linken und rechten Kamera


Beim ersten Start verfluchte der Code, dass es keinen Zugriff auf YML-Dateien mit Kameraparametern gab. Ich habe himbeerfarbene V2-Kameras verwendet und mich daran erinnert, dass fertige Dateien mit Kalibrierungsergebnissen für verschiedene Kameramodelle zum raspicam_node auf dem Github kamen: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/camera_info
Ich nahm eine davon, machte zwei Kopien und speicherte sie mit den Namen left.yml und right.yml, wobei ich die Kameraauflösung aus dem Skript des Autors schrieb. Folgendes ist für die linke Kamera passiert:

left.yml
 image_width: 320 image_height: 240 camera_name: left camera_matrix: rows: 3 cols: 3 data: [1276.704618338571, 0, 634.8876509199106, 0, 1274.342831275509, 379.8318028940378, 0, 0, 1] distortion_model: plumb_bob distortion_coefficients: rows: 1 cols: 5 data: [0.1465167016954302, -0.2847343180128725, 0.00134017721235817, -0.004309553450829512, 0] rectification_matrix: rows: 3 cols: 3 data: [1, 0, 0, 0, 1, 0, 0, 0, 1] projection_matrix: rows: 3 cols: 4 data: [1300.127197265625, 0, 630.215390285608, 0, 0, 1300.670166015625, 380.1702884455881, 0, 0, 0, 1, 0] 


Rechts wird der Kameraname durch rechts ersetzt und die Datei selbst heißt right.yml. Der Rest der Datei ist identisch.

Da ich nicht vorhatte, ein komplexes Projekt durchzuführen, habe ich die langen Pfade des Autors nicht mit Unterordnern wiederholt und die Dateien einfach im Stammverzeichnis des Basisordners neben dem Python-Skript abgelegt. Der Code wurde erfolgreich gestartet und zeigt Statusmeldungen in der Konsole an.

Bild

Es blieb nur zu sehen, was schließlich von unserer linken und rechten Kamera veröffentlicht wurde. Dazu habe ich rqt_image_view gestartet. Die Elemente / left / image_raw und / right / image_raw wurden im Dropdown-Menü angezeigt. Als ich sie auswählte, sah ich Bilder von der linken und rechten Kamera.

Bild

Nun, das Ding hat verdient! Nun der lustige Teil.

8. Wir betrachten die Karte der Tiefen.


Um die Tiefenkarte anzuzeigen, habe ich mir keinen eigenen Ansatz ausgedacht und das klassische ROS-Handbuch zum Einstellen der Stereoparameter durchgesehen .
Von dort fand ich heraus, dass es schön wäre, beide Knoten in einem bestimmten Namespace zu veröffentlichen und nicht wie Wezzoid im Stammverzeichnis. Infolgedessen sind die alten Veröffentlichungszeilen des Formulars

 left_img_pub = rospy.Publisher('left/image_raw', Image, queue_size=1) 

begann so auszusehen:

 left_img_pub = rospy.Publisher('stereo/right/image_raw', Image, queue_size=1) 

Danach starten wir den Stereomodus-Verarbeitungsknoten stereo_image_proc:

 ROS_NAMESPACE=stereo rosrun stereo_image_proc stereo_ige_proc 

Nun, wir wollen uns auch das Ergebnis ansehen, also starten wir den Beobachter:

 rosrun image_view stereo_view stereo:=/stereo image:=image_rect_color 

Führen Sie das Konfigurationsdienstprogramm aus, um die Parameter der Tiefenkarte zu konfigurieren:

 rosrun rqt_reconfigure rqt_reconfigure 

Infolgedessen sehen wir das Bild ganz am Anfang des Artikels. Hier ist etwas größer:

Bild

Alle Dateien, die ich auf dem Github gepostet habe: github.com/realizator/StereoPi-ROS-depth-map-test

9. Sofortige Pläne


Nach meiner Veröffentlichung des Ergebnisses in einer Diskussion über den Github schrieb Rohan: „Cool! Ich muss mein StereoPi abholen. “ Wir haben ihm per Post geschrieben, ich habe ihm eine Gebühr geschickt. Ich hoffe, dass es ihm mit der funktionierenden Hardware in seinen Händen leichter fällt, einen vollwertigen Stereotreiber für ROS und Raspberry fertigzustellen und zu debuggen.

10. Zusammenfassung


Eine Tiefenkarte aus einem Stereobild auf Himbeeren in ROS kann auf verschiedene Arten erhalten werden. Der für die schnelle Überprüfung gewählte Pfad ist hinsichtlich der Leistung nicht der optimalste, kann jedoch für Anwendungszwecke verwendet werden. Die Schönheit seiner Einfachheit und die Fähigkeit, sofort mit Experimenten zu beginnen.

Nun, aus dem Witzigen: Nachdem ich die Ergebnisse erhalten hatte, bemerkte ich, dass Wezzoid, der seine Lösung vorschlug, der Autor der Frage nach der Veröffentlichung von zwei Stereobildern war. Er fragte sich, entschied er.

Source: https://habr.com/ru/post/de431092/


All Articles