ROS: خريطة عميقة على Raspberry Pi "الدم المنخفض"

الصورة

إذا كنت تستخدم ROS عند إنشاء الروبوتات ، فمن المحتمل أنك تعرف أن لديها دعمًا للعمل مع كاميرات الاستريو. يمكنك ، على سبيل المثال ، إنشاء خريطة عمق للجزء المرئي من المساحة أو سحابة نقطة. وتساءلت عن مدى سهولة استخدام كاميرا ستريو ستريبي المعتمدة على التوت في ROS. في وقت سابق ، كنت مقتنعًا بالفعل بأن خريطة العمق مبنية بشكل مثالي بواسطة OpenCV ، لكنني لم أتعامل مع ROS. وقررت أن أجربها. أريد أن أتحدث عن مغامراتي في إيجاد حل.

1. هل هناك أي ROS على Raspberry Pi؟


في البداية ، قررت معرفة ما إذا كان من الممكن بناء ROS لـ Raspberry Pi. أول ما أخبرتني به Google هو قائمة تعليمات تثبيت إصدارات مختلفة من ROS على Raspberry Pi ، وهي صفحة ROS wiki هذه

حسنًا ، هناك بالفعل شيء يمكن البدء منه! تذكرت جيدًا كم من الوقت استغرق بناء OpenCV على Raspberry (حوالي ثماني ساعات) ، لذلك قررت البحث عن صور جاهزة لبطاقات MicroSD لتوفير الوقت.

2. هل هناك أي صور بطاقة microSD جاهزة مع ROS لـ Raspberry؟


اتضح أن هذه المشكلة قد تم حلها بالفعل من قبل العديد من فرق التطوير. إذا كنت لا تأخذ إصدارات لمرة واحدة من قبل المتحمسين ، فقد برزت صورتان يتم تحديثهما باستمرار مع إصدار إصدارات جديدة من نظام التشغيل و ROS.

الخيار الأول هو ROS مثبت على نظام التشغيل Raspbian الأصلي من فريق ROSbots ، وهنا صفحة تحتوي على رابط صورة محدث: جاهز للاستخدام image-raspbian-stretch-ros-opencv

والثاني هو صور من Ubiquiti Robotics على أوبونتو .

حسنًا ، تم إغلاق السؤال الثاني بسرعة كافية أيضًا. حان الوقت للتعمق أكثر.

3. كيف تعمل ROS مع كاميرا Raspberry Pi؟


وأي كاميرات ستيريو مدعومة بشكل عام في ROS؟ نظرت إلى الصفحة بكاميرات استريو ، والتي تم الإعلان عن توفر برامج تشغيل جاهزة لها لـ ROS ، وهذا:

wiki.ros.org/Sensors

كان هناك قسمان:
2.3 مستشعرات ثلاثية الأبعاد (محددات المدى وكاميرات RGB-D)
2.5 كاميرات
اتضح أنه في القسم الأول ، ليس فقط كاميرات الاستريو ، ولكن أيضًا مستشعرات TOF ، ومسح الأغطية مدرجة - بشكل عام ، كل ما يمكن أن يعطي المعلومات على الفور في 3D. وفي الثانية هناك بالفعل كاميرات ستيريو. إن محاولة رؤية برامج التشغيل للعديد من كاميرات الاستريو لم تضيف إلى فرحي ، حيث ألمح إلى انغماس خطير في الشفرة.

حسنا ، خطوة خطوة إلى الوراء. كيف يعمل مع كاميرا Raspberry Pi واحدة في ROS؟

انتظرتني ثلاث مفاجآت سارة هنا:

  • اتضح أنه بالنسبة لـ ROS هناك عقدة raspicam_node خاصة فقط للعمل مع كاميرا Raspberry Pi
  • أنواع العقدة تقع على جيثوب ، ويتم الحفاظ على الكود بشكل نشط وموثق جيدًا: github.com/UbiquityRobotics/raspicam_node
  • مؤلف عقدة Rohan Agrawal ( Rohbotics ) يعمل لصالح شركة تدعم بنشاط إحدى الصور الجاهزة لـ Raspberry Pi

نظرت إلى raspicam_node لمستودع github ونظرت إلى المشكلات. لقد وجدت مشكلة مفتوحة مع الاسم الواسع "وضع ستيريو" قبل سبعة أشهر تقريبًا ، بدون إجابات وتعليقات. في الواقع ، تطورت فيه جميع الأحداث بشكل أكبر.

4. فاضح أم لا؟


لكي لا أطرح أسئلة على المؤلفين ، قررت أن أنظر إلى شفرة المصدر وأن أقيّم ما الذي يهدد إضافة وضع الاستريو. كنت أكثر اهتمامًا بجزء النظام هنا: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/src
حسنًا ، كتب الرجال السائق وهو يغرق في مستوى MMAL. تذكرت أيضًا أن شفرة المصدر للتوت في وضع الاستريو مفتوحة أيضًا (يمكن تتبع التطور هنا في منتدى التوت ) ، ومهمة كتابة برنامج تشغيل استريو كامل قابلة للحل ، ولكن على نطاق واسع. بالنظر إلى وصف محركات الكاميرات الأخرى ، أدركت أنه من الضروري نشر ليس فقط الصور اليمنى واليسرى ، ولكن أيضًا لإعطاء معلمات كلتا الكاميرتين ، وتطبيق نتائج المعايرة على كل واحدة ، والقيام بأشياء أخرى كثيرة. اجتذب هذا التجارب لمدة شهر أو شهرين. لذلك ، قررت موازاة النهج ، أي: اكتب إلى المؤلف سؤالًا حول دعم الاستريو ، وابحث عن حل أبسط ولكن عملي.

5. حوارات مع المؤلف


في الخيط حول وضع الاستريو على جيثب ، سألت المؤلف سؤالًا ، مشيراً إلى أن الستيريو مدعوم من التوت منذ 2014 ، واقترحت ، إذا لزم الأمر ، إرسال لوحة تصحيح للتجارب. دعني أذكرك بأنني ما زلت أشك في أن هذا الاستريو سيعمل في هذا التوزيع كما هو الحال في Raspbian الأصلي.

استجاب روهان بسرعة مدهشة ، قائلاً أن توزيعها يستخدم نواة التوت ويجب أن يعمل كل شيء. وطلب التحقق من ذلك في أحد تجمعاتهم.

قلب التوت! الصيحة! من الناحية النظرية ، يجب التقاط صورة مجسمة دون الرقص مع الدف!

لقد قمت بتنزيل ونشر أحدث صورهم باستخدام رابط من روهان وقمت بتشغيل نص ثعباني بسيط لالتقاط زوج ستيريو. عملت!

الصورة

بعد ذلك ، كتب روهان أنه سينظر إلى رمز السائق لوضع الاستريو ، وكتب بضعة أسئلة. على سبيل المثال ، ينتج وضع الاستريو الخاص بنا صورة واحدة ملصقة ، وسوف نحتاج إلى تقطيعها إلى قسمين - يسار ويمين. والسؤال الثاني حول معلمات المعايرة لكل كاميرا هو كيفية التعامل معها.

قلت أنه كخطوة أولى ، يمكنك التقاط صور من الكاميرات بشكل مستقل. نعم ، لن تتم مزامنتها في وقت الالتقاط والإعدادات (مثل توازن السطوع والتباين والأبيض) ، ولكن كخطوة أولى ، قد ينخفض ​​هذا.

طرح روهان على الفور رقعة تسمح لك بتحديد الكاميرا التي تلتقط الصور منها مباشرة من ROS. راجعت - اختيار الكاميرا يعمل ، إنها بالفعل نتيجة ممتازة.

6. مساعدة غير متوقعة


ثم يظهر تعليق من مستخدم Wezzoid في الموضوع. قال إنه كان يقوم بمشروع قائم على كاميرا ستيريو على جهاز Pi Compute 3 باستخدام لوحات التوت. قام روبوت المشي الذي يعمل بأربع أرجل بتتبع موضع جسم في الفضاء ، وتغيير موضع الكاميرات والحفاظ على مسافة محددة سلفًا إليه (يتم نشر المشروع على hackaday.io هنا ).

الصورة

وشارك الرمز الذي التقط فيه الصورة ، وقطعها إلى النصف باستخدام الثعبان وشاركها مثل عقد الكاميرات اليسرى واليمنى.
Python ليس صديقًا سريعًا جدًا في هذه الأمور ، لذلك استخدم دقة منخفضة 320 × 240 واختراق حياة جيد. إذا التقطنا صورة استريو جنبًا إلى جنب (كاميرا واحدة على يسار صورة الاستريو ، والثانية على اليمين) ، فيجب أن يقطع الثعبان كل من الخطوط الـ 240 إلى النصف. ولكن إذا قمت بعمل صورة من أعلى إلى أسفل (الكاميرا اليسرى هي النصف العلوي من الإطار ، فإن اليمين هو الأسفل) ، فإن الثعبان يقطع الصفيف إلى النصف في عملية واحدة. وهو ما قام به المستخدم Wezzoid بنجاح.
بالإضافة إلى ذلك ، قام بنشر كود بيثون الخاص به على Pastebin ، التي قامت بهذه العملية. ها هي:

كود Wezzoid لنشر عقد كاميرتين من زوج ستيريو
#!/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. ابدأ في نشر عقد الكاميرات اليسرى واليمنى


في البداية ، لعن الكود أنه لم يكن هناك وصول إلى ملفات YML مع معلمات الكاميرا. استخدمت كاميرات V2 بلون التوت وتذكرت أن الملفات الجاهزة مع نتائج المعايرة لنماذج الكاميرا المختلفة قد وصلت إلى raspicam_node على github: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/camera_info
أخذت واحدة منهم ، وقمت بعمل نسختين وحفظتها مع الأسماء left.yml و right.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] 


لليمين ، يتم استبدال اسم الكاميرا باليمين ، ويتم تسمية الملف نفسه right.yml. باقي الملف متطابق.

نظرًا لأنني لم أكن أخطط للقيام بمشروع معقد ، لم أكرر المسارات الطويلة للمؤلف مع المجلدات الفرعية وقمت فقط بوضع الملفات في جذر المجلد الرئيسي بجوار البرنامج النصي لـ python. بدأ الكود بنجاح ، وعرض رسائل الحالة في وحدة التحكم.

الصورة

بقي فقط أن نرى ما تم نشره في النهاية بواسطة كاميراتنا اليسرى واليمنى. للقيام بذلك ، أطلقت rqt_image_view. ظهرت العناصر / يسار / image_raw و / يمين / image_raw في القائمة المنسدلة. عندما حددتها ، رأيت صورًا من الكاميرات اليمنى واليسرى.

الصورة

حسنا ، لقد حصل هذا الشيء! الآن الجزء الممتع.

8. ننظر إلى خريطة الأعماق.


لعرض خريطة العمق ، لم أتوصل إلى منهجي الخاص وتناولت دليل ROS الكلاسيكي لتحديد معلمات الاستريو .
من هناك ، اكتشفت أنه سيكون من الجيد نشر كلا العقدتين في مساحة اسم محددة ، وليس في الجذر كما فعل Wezzoid. ونتيجة لذلك ، فإن خطوط نشر النموذج القديمة

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

بدأت تبدو هكذا:

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

بعد ذلك ، نطلق عقدة معالجة وضع الاستريو sterim_image_proc:

 ROS_NAMESPACE=stereo rosrun stereo_image_proc stereo_ige_proc 

حسنًا ، نريد أيضًا إلقاء نظرة على النتيجة ، لذلك نبدأ المراقبة:

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

ولتكوين معلمات خريطة العمق ، قم بتشغيل أداة التهيئة المساعدة:

 rosrun rqt_reconfigure rqt_reconfigure 

نتيجة لذلك ، نرى الصورة في بداية المقال. هنا أكبر قليلاً:

الصورة

جميع الملفات التي نشرتها على github: github.com/realizator/StereoPi-ROS-depth-map-test

9. الخطط الفورية


بعد نشري للنتيجة في مناقشة على github ، كتب روهان "رائع! أحتاج أن ألتقط جهاز StereoPi الخاص بي. " كتبنا له بالبريد وأرسلت له رسماً. آمل أنه مع وجود الأجهزة العاملة في يديه سيكون من الأسهل عليه إنهاء وتصحيح برنامج تشغيل استريو كامل لـ ROS و Raspberry.

10. ملخص


يمكن الحصول على خريطة عمق من صورة مجسمة على التوت في ROS بعدة طرق. المسار المختار للتحقق السريع ليس هو الأمثل من حيث الأداء ، ولكن يمكن استخدامه لأغراض التطبيق. جمال بساطتها والقدرة على بدء التجارب على الفور.

حسنًا ، من المضحك: بعد تلقي النتائج ، لاحظت أن Wezzoid ، الذي اقترح حله ، هو مؤلف السؤال حول نشر صورتين استريو. سأل نفسه ، قرر.

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


All Articles