ROS: carte de profondeur sur le Raspberry Pi "low blood"

image

Si vous utilisez ROS lors de la création de robots, vous savez probablement qu'il prend en charge le travail avec des caméras stéréo. Vous pouvez par exemple créer une carte de profondeur de la partie visible de l'espace ou un nuage de points. Et je me demandais à quel point il serait facile d'utiliser une caméra stéréo StereoPi à base de framboise dans ROS. Plus tÎt, j'étais déjà convaincu que la carte de profondeur était parfaitement construite par OpenCV, mais je n'ai jamais traité de ROS. Et j'ai décidé de l'essayer. Je veux parler de mes aventures dans la recherche d'une solution.

1. Y a-t-il un ROS sur le Raspberry Pi?


Au début, j'ai décidé de découvrir s'il était possible de construire ROS pour le Raspberry Pi. La premiÚre chose que Google m'a dit était une liste d'instructions pour l'installation de différentes versions de ROS sur le Raspberry Pi, à savoir cette page wiki ROS

Eh bien, il y a dĂ©jĂ  quelque chose pour commencer! Je me suis bien souvenu du temps qu'il a fallu pour crĂ©er OpenCV sur Raspberry (environ huit heures), j'ai donc dĂ©cidĂ© de rechercher des images de carte MicroSD prĂȘtes Ă  l'emploi pour gagner du temps.

2. Existe-t-il des images de cartes microSD prĂȘtes Ă  l'emploi avec ROS pour Raspberry?


Il s'est avéré que ce problÚme avait déjà été résolu par plusieurs équipes de développement. Si vous ne prenez pas de versions uniques par des passionnés, deux images se sont démarquées qui sont constamment mises à jour avec la sortie de nouvelles versions du systÚme d'exploitation et de ROS.

La premiĂšre option est ROS installĂ© sur le systĂšme d'exploitation Raspbian natif de l'Ă©quipe ROSbots, voici une page avec un lien d'image mis Ă  jour: image prĂȘte Ă  l'emploi-raspbian-stretch-ros-opencv

La seconde est les images d'Ubiquiti Robotics sur Ubuntu .

Eh bien, la deuxiÚme question a également été assez rapidement close. Il est temps de plonger plus profondément.

3. Comment fonctionne ROS avec la caméra Raspberry Pi?


Et quelles camĂ©ras stĂ©rĂ©o sont gĂ©nĂ©ralement prises en charge dans ROS? J'ai regardĂ© la page avec les camĂ©ras stĂ©rĂ©o, pour laquelle la disponibilitĂ© de pilotes prĂȘts Ă  l'emploi pour ROS a Ă©tĂ© dĂ©clarĂ©e, celle-ci:

wiki.ros.org/Sensors

Il y avait deux sections:
2.3 Capteurs 3D (télémÚtres et caméras RGB-D)
2.5 Caméras
Il s'est avéré que dans la premiÚre section, non seulement les caméras stéréo, mais aussi les capteurs TOF et les lidars à balayage sont répertoriés - en général, tout ce qui peut donner immédiatement des informations en 3D. Et dans le second, il y a déjà des caméras stéréo. Essayer de voir les pilotes de plusieurs caméras stéréo n'a pas ajouté à ma joie, car cela faisait allusion à une sérieuse immersion dans le code.

D'accord, reculez d'une étape. Comment ça marche avec une seule caméra Raspberry Pi dans ROS?

Trois agréables surprises m'attendaient ici:

  • il se trouve que pour ROS il y a un nƓud spĂ©cial raspicam_node juste pour travailler avec la camĂ©ra Raspberry Pi
  • sortes de nƓuds se trouvent sur le github, le code est activement maintenu et bien documentĂ©: github.com/UbiquityRobotics/raspicam_node
  • l'auteur du nƓud Rohan Agrawal ( @Rohbotics ) travaille pour une entreprise qui prend activement en charge l'une des images prĂȘtes Ă  l'emploi pour le Raspberry Pi

J'ai regardé le dépÎt gaspub raspicam_node et j'ai regardé les problÚmes. Là, j'ai trouvé un problÚme ouvert avec le nom volumineux "mode stéréo" il y a prÚs de sept mois, sans réponses ni commentaires. En fait, tous les événements s'y sont développés davantage.

4. Hardcore ou pas?


Afin de ne pas poser de questions aux enfants aux auteurs, j'ai décidé de regarder le code source et d'évaluer ce qui menace l'ajout du mode stéréo. J'étais plus intéressé par la partie systÚme ici: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/src
Eh bien, les gars ont Ă©crit le pilote plongeant dans le niveau MMAL. Je me suis Ă©galement souvenu que le code source des framboises en mode stĂ©rĂ©o est Ă©galement ouvert (l'Ă©volution peut ĂȘtre retracĂ©e ici sur le forum framboise ), et la tĂąche d'Ă©crire un pilote stĂ©rĂ©o Ă  part entiĂšre est rĂ©soluble, mais Ă  grande Ă©chelle. En regardant la description des pilotes d'autres camĂ©ras, j'ai rĂ©alisĂ© qu'il Ă©tait nĂ©cessaire de publier non seulement les images de gauche et de droite, mais aussi de donner les paramĂštres des deux camĂ©ras, d'appliquer des rĂ©sultats d'Ă©talonnage Ă  chacune et de faire bien d'autres choses. Cela a attirĂ© des expĂ©riences d'un mois ou deux. Par consĂ©quent, j'ai dĂ©cidĂ© de parallĂ©liser l'approche, Ă  savoir: Ă©crire Ă  l'auteur une question sur le support stĂ©rĂ©o et rechercher moi-mĂȘme une solution plus simple mais fonctionnelle.

5. Dialogues avec l'auteur


Dans le fil sur le mode stéréo sur le github, j'ai posé une question à l'auteur, mentionnant que la stéréo est supportée par les framboises depuis 2014, et lui ai suggéré, si nécessaire, de lui envoyer une carte de débogage pour des expériences. Permettez-moi de vous rappeler que je doutais encore que dans cette distribution, la stéréo fonctionnera comme dans le Raspbian natif.

Rohan a répondu étonnamment rapidement, disant que leur distribution utilise un noyau de framboise et que tout devrait fonctionner. Et a demandé de le vérifier sur l'un de leurs assemblées.

Noyau de framboise! Hourra! ThĂ©oriquement, une image stĂ©rĂ©o doit ĂȘtre capturĂ©e sans danser avec un tambourin!

J'ai tĂ©lĂ©chargĂ© et dĂ©ployĂ© leur derniĂšre image en utilisant un lien de Rohan et j'ai exĂ©cutĂ© un simple script python pour capturer une paire stĂ©rĂ©o. Ça a marchĂ©!

image

AprÚs cela, Rohan a écrit qu'il examinerait le code du pilote pour le mode stéréo et a écrit quelques questions. Par exemple, notre mode stéréo produit une image collée, et nous aurions besoin de la couper en deux - gauche et droite. Et la deuxiÚme question sur les paramÚtres d'étalonnage de chaque caméra est de savoir comment y faire face.

J'ai dit que dans un premier temps, vous pouvez prendre des photos à partir d'appareils photo indépendamment. Oui, ils ne seront pas synchronisés dans le temps de capture et les paramÚtres (tels que la luminosité, le contraste et la balance des blancs), mais dans un premier temps, cela pourrait bien se produire.

Rohan a rapidement déployé un correctif qui vous permet de spécifier directement à partir de ROS l'appareil photo à partir duquel prendre des photos. Je l'ai vérifié - le choix d'un appareil photo fonctionne, c'est déjà un excellent résultat.

6. Aide inattendue


Et puis un commentaire de l'utilisateur Wezzoid apparaßt dans le fil. Il a dit qu'il faisait un projet basé sur une caméra stéréo sur un Pi Compute 3 utilisant des devboards framboises. Son robot marchant à quatre pattes a suivi la position d'un objet dans l'espace, a changé la position des caméras et a gardé une distance prédéterminée par rapport à celui-ci (le projet est affiché sur hackaday.io ici ).

image

Et il a partagĂ© le code dans lequel il a saisi l'image, l'a coupĂ©e en deux avec des moyens python et l'a partagĂ©e comme des nƓuds des camĂ©ras gauche et droite.
Python n'est pas un ami trÚs rapide dans ces domaines, il a donc utilisé une faible résolution de 320x240 et un bon hack de vie. Si nous capturons une image stéréo cÎte à cÎte (une caméra à gauche de l'image stéréo, la seconde à droite), le python doit couper chacune des 240 lignes en deux. Mais si vous faites une image de haut en bas (la caméra de gauche est la moitié supérieure du cadre, la droite est en bas), le python coupe le tableau en deux en une seule opération. Ce qui a été fait avec succÚs par l'utilisateur Wezzoid.
De plus, il a publié son code python sur Pastebin, qui a effectué cette opération. Le voici:

Code Wezzoid pour publier les nƓuds de deux camĂ©ras Ă  partir d'une paire stĂ©rĂ©o
#!/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. Commencez Ă  publier les nƓuds des camĂ©ras gauche et droite


Au premier dĂ©marrage, le code a maudit qu'il n'y avait pas d'accĂšs aux fichiers YML avec les paramĂštres de la camĂ©ra. J'ai utilisĂ© des camĂ©ras V2 de couleur framboise et je me suis souvenu que des fichiers prĂȘts avec des rĂ©sultats d'Ă©talonnage pour diffĂ©rents modĂšles de camĂ©ras Ă©taient arrivĂ©s Ă  raspicam_node sur le github: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/camera_info
J'ai pris l'un d'eux, en ai fait deux copies et je l'ai enregistré sous les noms left.yml et right.yml, en y inscrivant la résolution de la caméra à partir du script de l'auteur. Voici ce qui s'est passé pour la caméra de gauche:

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] 


Pour la droite, le nom de la camĂ©ra est remplacĂ© par droite et le fichier lui-mĂȘme est nommĂ© right.yml. Le reste du fichier est identique.

Comme je n'avais pas prévu de faire un projet complexe, je n'ai pas répété les longs chemins de l'auteur avec des sous-dossiers et j'ai simplement mis les fichiers à la racine du dossier personnel à cÎté du script python. Le code a démarré avec succÚs, affichant des messages d'état dans la console.

image

Il ne restait plus qu'à voir ce qui a finalement été publié par nos caméras gauche et droite. Pour ce faire, j'ai lancé rqt_image_view. Les éléments / left / image_raw et / right / image_raw sont apparus dans le menu déroulant. Lorsque je les ai sélectionnés, j'ai vu des images des caméras gauche et droite.

image

Eh bien, cette chose a gagné! Maintenant, la partie amusante.

8. Nous regardons la carte des profondeurs.


Pour voir la carte de profondeur, je n'ai pas trouvé ma propre approche et j'ai parcouru le manuel ROS classique pour régler les paramÚtres stéréo .
À partir de lĂ , j'ai dĂ©couvert qu'il serait bien de publier les deux nƓuds dans un espace de noms spĂ©cifique, et non Ă  la racine comme l'a fait Wezzoid. En consĂ©quence, les anciennes lignes de publication du formulaire

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

a commencé à ressembler à ceci:

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

AprĂšs cela, nous lançons le nƓud de traitement du mode stĂ©rĂ©o stereo_image_proc:

 ROS_NAMESPACE=stereo rosrun stereo_image_proc stereo_ige_proc 

Eh bien, nous voulons également regarder le résultat, alors nous commençons l'observateur:

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

Et pour configurer les paramÚtres de la carte de profondeur, exécutez l'utilitaire de configuration:

 rosrun rqt_reconfigure rqt_reconfigure 

En conséquence, nous voyons l'image au tout début de l'article. Voici un peu plus grand:

image

Tous les fichiers que j'ai postés sur le github: github.com/realizator/StereoPi-ROS-depth-map-test

9. Plans immédiats


AprÚs ma publication du résultat dans une discussion sur le github, Rohan a écrit «Cool! Je dois aller chercher mon StereoPi. " Nous lui avons écrit par mail, je lui ai envoyé des honoraires. J'espÚre qu'avec le matériel de travail entre ses mains, il lui sera plus facile de terminer et de déboguer un pilote stéréo à part entiÚre pour ROS et Raspberry.

10. Résumé


Une carte de profondeur Ă  partir d'une image stĂ©rĂ©o sur des framboises dans ROS peut ĂȘtre obtenue de plusieurs façons. Le chemin choisi pour une vĂ©rification rapide n'est pas le plus optimal en termes de performances, mais peut ĂȘtre utilisĂ© Ă  des fins d'application. La beautĂ© de sa simplicitĂ© et la possibilitĂ© de commencer immĂ©diatement des expĂ©riences.

Eh bien, du drÎle: aprÚs avoir reçu les résultats, j'ai remarqué que Wezzoid, qui a proposé sa solution, était l'auteur de la question sur la publication de deux images stéréo. Il s'est demandé, il a décidé.

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


All Articles