去年由奥比中光赞助的3D创新视觉赛落下帷幕,留下了令人印象深刻地一些作品(惭愧,虽然我也参加了,但是算法上面有些地方做地不好,就没有参与到后续地评奖中,希望今年获奖,哈哈哈哈哈),另外今年也幸运的当了社区的版主>.<,欢迎大家来投稿!
https://developer.orbbec.com.cn/forum.html
不过我发现论坛在长久的运营过程中其实留下了很多资料,但是缺乏系统的整理,所以我就准备整理一份开源的立体视觉资料出来,这里也希望大家给一些相关的建议。
在正式整理之前,我发现两届比赛出现了不少有用的方案,所以准备做一个系列的方案解读。一方面是为了好的方案继续发挥作用,二是为了他人的项目参考。
已完结~
那为什么会选定这样一个方案来解读呢?以下给出一封感兴趣的读者发来的邮件:
关键信息打码了
可以看出,这个项目是完整的,以及有了实用性,其次是老师想选用这个案例用于教学和指导学生实践学习。
我的文章并不简明,内部大量充斥着口语,个人想法,其看起来更像是一份工作札记,但是文风这种东西改起来不是很容易,希望读者理解。
项目缘起:
大哥在展会上面看到了这个东西
运行起来的样子
So~就自己做一个!
功能目标:基于点云的轨迹引导,即无论待引导物体以何种位姿摆放(要求该位姿在机械臂的行程范围内),视觉系统均能定位到该物体,并引导机械臂按需要的轨迹实现一定的工艺流程。
应用场景:需要轨迹引导的工业现场。
其实有人会问,那这个东西传统的二维视觉做不了吗?事实上是可以,但是它不太能满足自由度多时机器人的引导,而加入了Depth会增加这种精度。
demo的样子,kuka机器手臂一直在沿着鞋底的边缘划线
系统的硬件为相机,处理,执行器
安装的位置如图所示,右侧的相机对工作平面进行解算,求出解算边缘,而后给机械臂(这部分作者没有写,而且将处理数据输入给机械臂的示教器)
奥比中光的Zora P1开发板:板子上跑的是armbian操作系统,部署的是点云采集和点云匹配程序,点云采集采用C++编写,基于奥比中光官方提供的OpenNI2 SDK。
https://developer-orbbec-oss.oss-cn-shenzhen.
aliyuncs.com
/2021-05-26/public1/images/gather/deveban.jpg
这里是P1开发板的图床位置,可以看到是阿里云的OBS,不知道买的啥套餐
板子一角
还是很丰富的接口
板子上面使用的armbian,就是基于Debiana适配的ARM开发板~
轻量级基于Debian或Ubuntu的Linux发行版,专门用于ARM开发板
每个系统均由Armbian Build Tools进行编译,组装和优化
它具有强大的构建和软件开发工具,可以进行自定义构建
充满活力的社区
其实就是一套完整的Linux系统,但是工具链齐全~文档丰富
有着完整的文档
就是使用的这个相机
然后就是精度真的困难是不够用
在P1上面使用的是Armbian的系统,论坛里面有详细的这个安装的教程,我这里也没有机器,我就不去做了,不过我觉得是jetson或者是树莓派都可以去部署。
香橙派Armbian系统安装之认识
香橙派Armbian系统安装之烧录
现在应该写标定的东西,但是写采集的也OK,为了流程一致,写标定。
相机标定是视觉系统的基础,工业级的相机标定需要碳纤维(或者玻璃等)的工业级标定板,保证平整度和角点精度。同时需要遮住激光器,并使用红外光源,使得红外相机能采集到清晰的标定板图像。
但是,普通开发者通常不具备上述条件,面临的情况常常是没有标定板和红外光源。为此,项目使用自制标定板,即通过代码生成高分辨率的棋盘格图像,并用打印机将其打印出来,贴在平板上。但是由于没有红外光源,红外相机只能借助带激光散斑的激光器的光源来拍摄标定板图像,带来的问题是部分角点检测的误差较大。为了解决这个问题,项目采用先执行一次相机标定,保留重投影误差小的70%的点,再执行一次相机标定。这么做可以明显降低重投影误差、提高精度,使用此方法标定出的相机内外参通过深度图和彩色图的对齐来验证,确实取得了良好的效果。
为什么这个标定过程要做这么多的工作?其实我们要知道一点,我们的计算理论都是完美无瑕的,光线很直,镜头没有各种光学误差,CCD和镜头的装配也是没有误差。
但事实上,一切都“比较糟糕”,所以相机的设计处处都是对现实的妥协,幸好,我们可以将这些误差算出来,做计算方法上面的补偿。
现在用的比较多的是张正友博士的棋盘格标定法(hhhh,大佬也姓张)
也叫标定板
好,我们自己写程序实现这个标定板的生成。
这里准备了C++和Python的版本
原版是C++的,但是改写成Python的。
首先引入库,注意OpenCV的安装。后面三个参数是单个标定快的大小以及标定块的数量。
板子单位格子的高和宽
参数计算,不符合就报错
我们的图像应该有一个容器来放它们:
cv::Mat image(resolution, CV_8UC1, cv::Scalar::all(255));
搞个容器,初始化一下。后面参数Scalar 是个short型vector。指定这个能够使用指定的定制化值来初始化矩阵。
CV_
[The number of bits per item]
[Signed or Unsigned]
[Type Prefix]C[The channel number]
这是这个函数的签名。
这是核心部分(吓死我,都不敢说是算法)。
这个填充是一开始都是黑色的,一个黑色的页面,纵向的逐像素的扫描。
像素点的操作。
显示+保存,CPP看累了吗?可以看看Python~
SCALE是缩放的系数,可以控制大小,都使用元组防止篡改。
逻辑和CPP的一样,这一段
生成一张全白的照片,我上面写错了
两个步骤,先扫描纸面,然后按照逻辑填充黑点。
完事~
import cv2
import numpy as np
import sys
SCALE = 0.48
perBoardPixel = int(100 * SCALE * 2)
boardSize = (7, 10)
resolution = (int(1400 * SCALE), int(2000 * SCALE))
if __name__ == "__main__":
basisHeight = (resolution[0] - perBoardPixel * boardSize[0]) // 2
basisWidth = (resolution[1] - perBoardPixel * boardSize[1]) // 2
if basisHeight < 0 or basisWidth < 0:
print("Resolution doesn't match!")
sys.exit(0)
image = np.ones(resolution).astype(np.uint8) * 255
flag = 0
for j in range(0, boardSize[0]):
for i in range(0, boardSize[1]):
flag = (i + j) % 2
if flag == 0:
for n in range(j * perBoardPixel, (j + 1) * perBoardPixel):
for m in range(i * perBoardPixel, (i + 1) * perBoardPixel):
image[n + basisWidth, m + basisHeight] = 0
cv2.imshow("chessBoard", image)
cv2.waitKey(0)
cv2.imwrite("chessBoard.bmp", image)
设置参数,直接运行。
https://github.com/appliedengdesign/vscode-gcode-syntax
原作者链接
https://blog.csdn.net/dawudayudaxue/article/details/106339491
标定算法
https://developer.orbbec.com.cn/forum_plate_module_details.html?id=830
论坛的位置
https://github.com/3DCVdeveloper/Visual-guided-manipulator
可能出于一些别的保护原因,这个方案的源码和相关资料没有全部在Github上面上传,不过学习起来还是绰绰有余,如果有其它疑问,可以联系小助手获取。
https://docs.armbian.com/User-Guide_Basic-Troubleshooting/
Armbian文档位置
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html
OpenCV Doc
hhhh,春天来了