前言:
这一篇,就是记录一下,如何标定,以及计算标定参数和重投影精度。
我好像没有在中文社区,搜到“九点标定”和“重投影”的相关关键词。
但在张正友标定法中,是有一个重投影误差的概念的。
即将算出来的变换矩阵M,代入变换公式中,计算出新的目标坐标,和原坐标的的误差。
用公式表达即:new_target_pos = origin_pos*M
e = MSE(new_target_pos, target_pos)
直观上理解,如果我们的标定参数,覆盖了所有的样本,通过最小二乘法拟合,那么拿到的误差应该就会比较小。
所以我借用深度学习常用的方法,划分了训练集和测试集。
并且测试了不同训练样本对结果的影响。
结果非常amazing啊!其实3点,这个函数也是能用的,但是至少要有3个点,因为1个点两个等式,有6个未知量。
数据集中可能存在一些异常点,异常点对结果的影响非常大!
只要找到合适的数据点,其实优化不优化,结果都比较好。
如果存在误差特别大的点,那就用多一点的数据,效果也不错。
实验流程:Eye-to-Hand Calibration:
摄像机固定,与机器人基坐标系相对位置不变。且机器人末端在固定平面移动,即只需要求一个单应性矩阵的变换关系就行。
实验流程如下:手眼系统场景搭建:相机固定,机械臂带动针尖在固定平面移动。
标定样本采集。包括摄像机图像采集,以及对应的机器人关节构型采集。--calibration_data_collected_main.py
图像处理提取标定针尖,并计算针尖在机器人坐标系下坐标。记录好每个位置点的 针尖像素坐标、针尖世界坐标、末端坐标
计算标定参数矩阵M--calibration_class.py
计算重投影误差avg_e--calibration_class.py
标定实验的主要环境配置和使用到的工具有:操作系统:Windows 7 64bit
图像处理工具:OpenCV-Python 3.4.* 如果安装不上的话,版本是4.* 以上,用estimated2DAffine好像也行,没测试过。
机器人和摄像机:新松SCR5七自由度协作机械臂,海康工业相机MV-CA013-21UC
其中calibration_class.py可以单独使用。只要有独立存在的标定点集即可。
那就上代码?
代码:
计算转换矩阵m
def get_m(self,
origin_points_set,
target_points_set):
# 确保两个点集的数量级不要差距过大,否则会输出None,看到这个输出,我直接好家伙。
# 明明回家前,还能输出一个好的转换矩阵,为什么一回家就报错?我错哪儿了...
m = cv2.estimateRigidTransform(origin_points_set,
target_points_set,
fullAffine=True)
return m
重投影误差计算:
def reproject(self,
origin_points_set,
target_points_set,
m):
error_list = []
for index in range(len(origin_points_set)):
p_origin = list(origin_points_set[index])
p_origin.append(1)
p_origin = np.array(p_origin)
p_tar = target_points_set[index]
new_tar = np.dot(m, p_origin)
error = np.linalg.norm(new_tar-p_tar[:2])
error_list.append(error)
print("avg_e:", np.mean(np.array(error_list)))
return np.mean(np.array(error_list))
重投影误差-训练样本数测试:
数据解析:
横轴为训练样本数,从9开始,到29结束,总样本量为37;
纵轴为误差大小,单位为毫米;
标题解释:xy为针尖坐标,uv为针尖像素像素坐标,xy2uv的意思是通过九点标定,计算出uv=mxy中的m,然后将m代入测试样本,算出新的uv_pred_test=mxy_test,计算误差e=mse(uv_pred, uv_origin_test)
如果是mt的话,则是,通过uv=m*xy计算出了m之后,用类似于下面的线性方程组:
t_rx= (A * t_px) + B * t_py + C);
t_ry= (D * t_px) + E * t_py+ F);
解出xy=mt*uv中的mt。再计算重投影误差。
结果在前言已经分析了。
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
总结:
记录这么多,其实核心内容也没什么创新的。
至于九点标定的重投影误差,我看到19年的ICRA竟然有一篇文章在用。
但是人家是eye-in-hand结构,两个机械臂,结构和原理都比我这个复杂多了...
我这个小项目花了我接近一周的时间,时间都花在哪儿了呢?
回顾了一下:
一个是对整个标定过程的怀疑,我从来没有标定过这种手眼结构,也没找到现成的工具包。
对整个信息流都没有搞明白,所以刚开始甚至都不知道采集哪些数据。
其次就是我们这个没有标定板,特征点的检测,只好利用自己写的针尖检测程序,这个程序我也调了很久,最终利用场景信息,写了一个优化的比较好的检测模块,可以在0.04秒一帧的速度下,实现2个像素以内的误差。
接着就是这个垃圾OpenCV的版本问题,4以上的版本没有cv2.estimateRigidTransform。我只能找3.4的版本。
最后就是浮点数精度的问题,因为像素坐标的取值范围是0-1280,而初始的针尖坐标的取值范围是0-0.8,差三个数量级,直接把点集带入函数,不同的电脑硬件不同,返回值竟然不一样。我在台式机可以拿到一个值非常小的的矩阵m,但是在我的笔记本中,同样的代码和数据,返回值竟然是None?我当时都怀疑我是不是笔记本坏掉了。
做一个测试:
在谷歌的colab上测试,最大精度是float128
测试脚本非常简单:
import numpy as np
print(np.finfo(np.longdouble))
out:Machine parameters for float128
---------------------------------------------------------------
precision = 18 resolution = 1e-18
machep = -63 eps = 1.084202172485504434e-19
negep = -64 epsneg = 5.42101086242752217e-20
minexp = -16382 tiny = 3.3621031431120935063e-4932
maxexp = 16384 max = 1.189731495357231765e+4932
nexp = 15 min = -max
而我的笔记本最大精度则是float64,有意思,这个坑一定要记住,说不定什么时候会被这玩意给坑了。
Machine parameters for float64
---------------------------------------------------------------
precision = 15 resolution = 1.0000000000000001e-15
machep = -52 eps = 2.2204460492503131e-16
negep = -53 epsneg = 1.1102230246251565e-16
minexp = -1022 tiny = 2.2250738585072014e-308
maxexp = 1024 max = 1.7976931348623157e+308
nexp = 11 min = -max
说了这么多,希望对大家有所帮助~