ROS:Raspberry Pi“低血”的深度图

图片

如果在创建机器人时使用ROS,那么您可能知道它支持使用立体摄像机。 例如,您可以构建空间的可见部分或点云的深度图。 而且我想知道在ROS中使用基于树莓派的StereoPi立体摄像机有多么容易。 早些时候,我已经确信深度图是由OpenCV完美构建的,但我从未涉及过ROS。 我决定尝试一下。 我想谈谈寻找解决方案的过程。

1. Raspberry Pi上是否有任何ROS?


首先,我决定找出是否有可能为Raspberry Pi构建ROS。 Google告诉我的第一件事是在Raspberry Pi上安装不同版本的ROS的说明列表,即该ROS Wiki页面

好吧,已经有一些起点了! 我完全记得在Raspberry上构建OpenCV需要花费多长时间(大约八个小时),所以我决定寻找现成的MicroSD卡映像以节省时间。

2.是否有带有ROS for Raspberry的现成的microSD卡映像?


事实证明,这个问题已经被多个开发团队解决。 如果您不喜欢发烧友的一次性产品,那么随着OS和ROS新版本的发布,会不断更新一些图像。

第一个选项是ROSbots团队在本地Raspbian操作系统上安装的ROS,这是带有更新的图像链接的页面: ready-to-use-image-raspbian-stretch-ros-opencv

第二个是来自ubuntu的Ubiquiti Robotics图像

好吧,第二个问题也很快就结束了。 是时候深入了解了。

3. ROS如何与Raspberry Pi相机配合使用?


ROS通常支持哪些立体摄像机? 我看了看带有立体摄像机的页面,为此声明了现成的ROS驱动程序的可用性,这是:

wiki.ros.org/传感器

有两个部分:
2.3 3D传感器(测距仪和RGB-D相机)
2.5相机
事实证明,在第一部分中,不仅列出了立体相机,还列出了TOF传感器和扫描激光雷达-通常,所有可以立即以3D形式提供信息的内容。 第二,已经有立体摄像机。 尝试查看几台立体摄像机的驱动程序并没有增加我的乐趣,因为这暗示着代码已沉浸其中。

好吧,退后一步。 ROS中的单个Raspberry Pi相机如何工作?

我在这里等着三个惊喜:

  • 事实证明,对于ROS,有一个特殊的raspicam_node节点仅用于Raspberry Pi相机
  • 各种各样的节点都位于github上,代码得到了积极维护并得到了很好的记录: github.com/UbiquityRobotics/raspicam_node
  • Rohan Agrawal节点( @Rohbotics )的作者在一家积极支持Raspberry Pi的现成图像之一的公司工作

我查看了github存储库raspicam_node并查看了问题。 大约七个月前,我在这里发现了一个名为“立体声模式”的大问题,没有答案和评论。 实际上,所有事件都在进一步发展。

4.铁杆与否?


为了不向孩子们提出孩子们的问题,我决定看一下源代码,并评估什么威胁增加立体声模式。 我对这里的系统部分更感兴趣: github.com/UbiquityRobotics/raspicam_node/tree/kinetic/src
好吧,这些家伙写的驱动程序陷入了MMAL级别。 我还记得,立体声模式下的树莓派的源代码也是开放的(可以在raspberry论坛上找到其演变过程),编写成熟的立体声驱动程序的任务是可以解决的,但是规模很大。 通过查看其他摄像机的驱动程序的描述,我意识到不仅需要发布左右图片,而且还必须提供两个摄像机的参数,将校准结果应用于每个摄像机,以及执行许多其他操作。 这吸引了长达一两个月的实验。 因此,我决定并行化该方法,即:向作者写一个有关立体声支持的问题,然后自己寻找一个更简单但可行的解决方案。

5.与作者的对话


在github上有关立体声模式的主题中,我问了作者一个问题,提到立体声自2014年以来一直受到树莓的支持,并建议,如有必要,请给他发送调试板进行实验。 让我提醒您,我仍然怀疑立体声在这种发行版中是否可以像本机Raspbian那样工作。

Rohan迅速做出了令人惊讶的回应,说他们的发行版使用树莓内核,并且一切正常。 并要求检查其组装之一。

树莓核! 万岁! 从理论上讲,应该捕获立体声图像而不用铃鼓跳舞!

使用Rohan的链接下载并部署了他们的最新图像并运行了一个简单的python脚本来捕获立体声对。 奏效了!

图片

在那之后,Rohan写道他将查看立体声模式的驱动程序代码,并提出了几个问题。 例如,我们的立体声模式产生一张粘贴的图片,我们需要将其切成两张-左右。 关于每个摄像机的校准参数的第二个问题是如何处理它。

我说过,第一步,您可以从相机独立拍摄照片。 是的,它们在拍摄时间和设置(例如亮度对比白平衡)上不会同步,但是作为第一步,这很可能会降低。

Rohan迅速推出了一个补丁 ,使您可以直接从ROS指定要从哪个相机拍摄照片。 我检查了一下-选择相机可以正常工作,这已经是一个很好的结果。

6.意外的帮助


然后,来自Wezzoid用户的评论出现在线程中。 他说,他正在使用树莓派开发板在Pi Compute 3上基于立体相机进行项目。 他的四足步行机器人跟踪了物体在太空中的位置,更改了摄像头的位置并保持了指定的距离(该项目发布在hackaday.io上)。

图片

然后,他分享了抓取图片的代码,并用python将其切成两半,然后像左右摄像机的节点一样进行共享。
在这些问题上,Python并不是一个非常快的朋友,因此他使用了320x240的低分辨率和不错的生活技巧。 如果我们捕获一个并排的立体图像(立体图像左侧的一台摄像机,右侧的第二台摄像机),则python应该将240行中的每行都切成两半。 但是,如果您制作一张上下图片(左摄像机是帧的上半部,右摄像机是下半部),则python会在一次操作中将数组切成两半。 这是由用户Wezzoid成功完成的。
另外,他将自己的python代码发布到了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相机,并记得带有不同相机型号的校准结果的现成文件进入github上的raspicam_nodegithub.com/UbiquityRobotics/raspicam_node/tree/kinetic/camera_info
我拿了其中一个,制作了两个副本,并分别以left.yml和right.yml的名称保存,并用作者脚本编写了摄像机的分辨率。 这是左镜头发生的情况:

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] 


对于右侧,将摄像机名称替换为right,并将文件本身命名为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) 

之后,我们启动stereo_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上的讨论中发表结果后,Rohan写道:“酷! 我需要去接我的StereoPi。” 我们通过邮件给他写信,我付了他费用。 我希望通过他手中的可用硬件,他可以更轻松地完成和调试用于ROS和Raspberry的成熟的立体声驱动程序。

10.总结


可以通过几种方式从ROS中的树莓上的立体图像获取深度图。 就性能而言,选择用于快速验证的路径不是最佳的,但可用于应用目的。 其简单性之美和立即启动实验的能力。

好吧,有趣的是:收到结果后,我注意到提出解决方案的韦佐德是有关出版两张立体图片的问题的作者。 他问自己,他决定。

Source: https://habr.com/ru/post/zh-CN431092/


All Articles