基于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
  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
手眼标定通常需要使用机器视觉库和机器人控制库,比较复杂。以下是一个基本的手眼标定代码框架,需要根据具体情况进行修改和完善。 ```python import cv2 import numpy as np import robot_control_library as rcl # 机器人控制库 # 机器人运动和图像采集函数定义 def move_robot_to_pos(pos): # 将机器人移动到指定位置 pass def capture_image(): # 采集图像 pass # 标定相关参数定义 image_points = [] # 图像坐标点集 world_points = [] # 世界坐标点集 K = np.zeros((3, 3)) # 相机内参矩阵 D = np.zeros((1, 5)) # 畸变系数 R = np.zeros((3, 3)) # 旋转矩阵 T = np.zeros((3, 1)) # 平移向量 # 手眼标定函数定义 def hand_eye_calibration(image_points, world_points): # 手眼标定相关代码 pass # 主程序 if __name__ == '__main__': # 采集图像和运动机器人,生成图像坐标点集和世界坐标点集 for i in range(num_samples): move_robot_to_pos(pos[i]) image = capture_image() image_points.append(image_corners) world_points.append(world_corners) # 手眼标定,获取相机内参、畸变系数、旋转矩阵和平移向量等参数 K, D, R, T = hand_eye_calibration(image_points, world_points) # 将相机内参、畸变系数、旋转矩阵和平移向量保存到文件中,以备后续使用 np.savetxt('K.txt', K) np.savetxt('D.txt', D) np.savetxt('R.txt', R) np.savetxt('T.txt', T) ``` 注意,以上代码仅为一个基本的手眼标定代码框架,具体实现还需要根据具体情况进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehedadaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值