基于OpenCV-Python的机器人手眼标定和重投影误差(九点标定法)-附代码

基于OpenCV-Python的机器人手眼标定和重投影(九点标定法)

前言:

前一篇转载,写了九点标定的原理。

这一篇,就是记录一下,如何标定,以及计算标定参数和重投影精度。
我好像没有在中文社区,搜到“九点标定”和“重投影”的相关关键词。
但在张正友标定法中,是有一个重投影误差的概念的。
即将算出来的变换矩阵M,代入变换公式中,计算出新的目标坐标,和原坐标的的误差。
用公式表达即:

new_target_pos = origin_pos*M
e = MSE(new_target_pos, target_pos)

直观上理解,如果我们的标定参数,覆盖了所有的样本,通过最小二乘法拟合,那么拿到的误差应该就会比较小。
所以我借用深度学习常用的方法,划分了训练集和测试集。
并且测试了不同训练样本对结果的影响。
结果非常amazing啊!

  1. 其实3点,这个函数也是能用的,但是至少要有3个点,因为1个点两个等式,有6个未知量。
    数据集中可能存在一些异常点,异常点对结果的影响非常大!
  2. 只要找到合适的数据点,其实优化不优化,结果都比较好。
  3. 如果存在误差特别大的点,那就用多一点的数据,效果也不错。
  4. 重投影误差0.4mm或者0.4像素。

实验流程:

Eye-to-Hand Calibration:
摄像机固定,与机器人基坐标系相对位置不变。且机器人末端在固定平面移动,即只需要求一个单应性矩阵的变换关系就行。

实验流程如下:

  1. 手眼系统场景搭建:相机固定,机械臂带动针尖在固定平面移动。
  2. 标定样本采集。包括摄像机图像采集,以及对应的机器人关节构型采集。–calibration_data_collected_main.py
  3. 图像处理提取标定针尖,并计算针尖在机器人坐标系下坐标。记录好每个位置点的 针尖像素坐标、针尖世界坐标、末端坐标
  4. 计算标定参数矩阵M–calibration_class.py
  5. 计算重投影误差avg_e–calibration_class.py

标定实验的主要环境配置和使用到的工具有:

  • 操作系统:Windows 7 64bit

  • 图像处理工具:OpenCV-Python 3.4.* 如果安装不上的话,版本是4.* 以上,用estimated2DAffine好像也行,没测试过。

  • 机器人和摄像机:新松SCR5七自由度协作机械臂,海康工业相机MV-CA013-21UC

其中calibration_class.py可以单独使用。只要有独立存在的标定点集即可。

那就上代码?

代码:

先挂我的github链接吧,这个代码有点多。
https://github.com/kaixindelele/Eye-to-Hand-Calibration
如果有帮助的话,记得点个star哈~
核心代码:

计算转换矩阵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

猜你喜欢

转载自blog.csdn.net/hehedadaq/article/details/113486527