首页 > 其他分享 >OpenCV手眼标定

OpenCV手眼标定

时间:2023-08-19 20:34:31浏览次数:48  
标签:矩阵 标定 相机 OpenCV base 坐标系 手眼

OpenCV 手眼标定

使用 OpenCV 进行手眼标定,版本需要 4.1.0 及以上。

为了确定手眼变换,至少需要两个具有非平行旋转轴的运动。因此,至少需要 3 个不同的姿势,但强烈建议使用更多的姿势。—— OpenCV 官方文档提示

cv::calibrateHandEye()

OpenCV有关手眼标定官方文档

void cv::calibrateHandEye(
    InputArrayOfArrays R_gripper2base,
    InputArrayOfArrays t_gripper2base,
    InputArrayOfArrays R_target2cam,
    InputArrayOfArrays t_target2cam,
    OutputArray R_cam2gripper,
    OutputArray t_cam2gripper,
    HandEyeCalibrationMethod method = CALIB_HAND_EYE_TSAI
)	
  • 矩阵输入参数类型都为 std::vector<cv::Mat>,即 \(n\) 张标定图片,需要有 \(n\) 组相关的矩阵依序放入 std::vector 以传入参数
  • method:求解 \(n-1\) 个 \(A*X=X*B\) 方程组的方法
    • method类型官方文档
    • CALIB_HAND_EYE_TSAI:一种完全自主高效的3D机器人手眼标定新技术
    • CALIB_HAND_EYE_PARK:机器人传感器标定,求解欧几里德群上的 \(AX=XB\)
    • CALIB_HAND_EYE_HORAUD:手眼标定
    • CALIB_HAND_EYE_ANDREFF:在线手眼标定
    • CALIB_HAND_EYE_DANIILIDIS:使用双四元数的手眼标定

坐标系:

  • base:机械臂基地坐标系
  • gripper:机械臂末端坐标系(夹爪坐标系)
  • cam:相机坐标系
  • target:标定板坐标系(世界坐标系)

方法变量名跟眼在手上的情况关联,代表:

\[\begin{cases} {A_i} = {(^{base}_{gripper}T^{(i + 1)})^{(-1)}} \, {^{base}_{gripper}T^{(i)}} \\ {X} = {^{gripper}_{cam}T} \\ {B_i} = {^{cam}_{target}T^{(i + 1)}} \, {(^{cam}_{target}T^{(i)})^{(-1)}} \\ \end{cases} \, , \, i \in [1, n - 1] \]

对于眼在手上

眼在手上,标定求解的是 相机坐标系 相对于 机械臂末端坐标系 的变换矩阵 \({^{gripper}_{cam}T}\)。

输入:

  • R_gripper2base:机械臂末端坐标系 相对于 机械臂基底坐标系的 旋转矩阵 \({^{base}_{gripper}R}\)
  • t_gripper2base:机械臂末端坐标系 相对于 机械臂基底坐标系的 平移矩阵 \({^{base}_{gripper}t}\)
  • R_target2cam:世界坐标系 相对于 相机坐标系的 旋转矩阵 \({^{cam}_{target}R}\)
  • t_target2cam:世界坐标系 相对于 相机坐标系的 平移矩阵 \({^{cam}_{target}t}\)

输出:

  • R_cam2gripper:相机坐标系 相对于 机械臂末端坐标系的 旋转矩阵 \({^{gripper}_{cam}R}\)
  • t_cam2gripper:相机坐标系 相对于 机械臂末端坐标系的 平移矩阵 \({^{gripper}_{cam}t}\)

对于眼在手外

眼在手外,标定求解的是 相机坐标系 相对于 机械臂基底坐标系 的转换矩阵 \({^{base}_{cam}T}\)。

输入:

  • R_gripper2base为 机械臂基底坐标系 到 机械臂末端坐标系的 旋转矩阵 \({^{gripper}_{base}R}\)
  • t_gripper2base为 机械臂基底坐标系 到 机械臂末端坐标系的 平移矩阵 \({^{gripper}_{base}t}\)
  • R_target2cam为 标定板坐标系 到 相机坐标系的 旋转矩阵 \({^{cam}_{target}R}\)
  • t_target2cam为 标定板坐标系 到 相机坐标系的 平移矩阵 \({^{cam}_{target}t}\)

输出:

  • R_cam2gripper相机坐标系 到 机械臂基底坐标系的 旋转矩阵 \({^{base}_{cam}R}\)
  • t_cam2gripper相机坐标系 到 机械臂基底坐标系的 平移矩阵 \({^{base}_{cam}t}\)

由机械臂正运动学解出 机械臂基底坐标系 与 机械臂末端坐标系的 之间的变换矩阵

\({^{base}_{end}R}\)和\({^{base}_{end}T}\),可通过解算机械臂的位姿求得
对于每个位姿,通常会返回六个参数:\(\theta_x \space \theta_y \space \theta_z \space t_x \space t_y \space t_z\)
这六个参数是机械臂末端在基底坐标系下的位姿的表示
即:\({^{base}_{end}R} = R_zR_yR_x \qquad {^{base}_{end}T} = \begin{pmatrix} t_x & t_y & t_z \end{pmatrix}^T\)
\(\theta_x \space \theta_y \space \theta_z\)为欧拉角,欧拉角(旋转角)到旋转矩阵的转换,见旋转矩阵.md

solvePnP

使用OpenCV中的solvePnP,可以求解相机到标定板坐标系的旋转矩阵和平移矩阵,即\({^{board}_{camera}R}\)和\({^{board}_{camera}T}\)

solvePnP是用来求解2D-3D的位姿对应关系,在这里,图片是(2D),而标定板坐标系是(3D)
利用solvePnP函数,就可以得到图片(相机坐标系)与标定板坐标系的变换关系

bool cv::solvePnP(
    InputArray objectPoints,
    InputArray imagePoints,
    InputArray cameraMatrix,
    InputArray distCoeffs,
    OutputArray rvec,
    OutputArray tvec,
    bool useExtrinsicGuess = false,
    int flags = SOLVEPNP_ITERATIVE
)	

输入:
objectPoints为棋盘格坐标系,由棋盘格真实坐标生成,一般以棋盘格左上角顶点建立,\(Z\)轴坐标都为0

imagePoints为图片识别到的角点坐标,与objectPoints中的值一一对应
cameraMatrix为相机内参矩阵
distCoeffs为相机畸变矩阵

输出:
rvec为标定板坐标系到相机坐标系的旋转向量,可用cv::Rodrigues()变为旋转矩阵
tvec为标定板坐标系到相机坐标系的平移向量

即得:
\({^{camera}_{board}R} \qquad {^{camera}_{board}T}\)

注:
输入时,可以使用vector of vector,即vector<vector<>>,输出是vector,意味着可以一次性输入所有位姿图片,然后计算得到每张图的变换矩阵,从而进行手眼标定

注意事项

  1. 直接利用cv::calibrateCamera得到的\(R,t\),来作为标定板坐标系到相机坐标系的输入
    利用cv::calibrateCamera得到的\(R,t\),它的内参是一个新相机内参,而之后进行手眼标定得到的转换矩阵也是对于新相机内参而言的
    而在识别待抓取物体时,进行的PnP坐标结算,输入的相机内参是老内参,不是新相机内参,如果再使用对于新相机内参而言的手眼标定得到的转换矩阵,进行到机械臂基底的转换,这样就会有错误了,导致每次抓出总会有偏差
    因此,无论是手眼标定,还是PnP结算,都要使用同一个相机内参
  2. 识别标定板的角点标反了
    由于在建立棋盘格上的三维坐标系的时候,我们默认是从棋盘格左上角到右下角建立的,如果识别反了,则会有个别图片棋盘格识别的角点和输入的棋盘格三维坐标对应不上,导致标定错误
    要注意拍摄标定图片时的规范操作,不要让原本标定板最右下角的角点在原本标定板最左上角的角点的左边
  3. 手眼标定时,标定板面积过小且只在图片中心处移动
    这样会使得标定的结果更偏向于中心处准确,而图片边缘处会略微的不准
  4. 手眼标定时,机械臂末端位姿只上下前后移动,旋转角几乎不变
    这样时没法得到准确的旋转矩阵的,6个分量都需要移动,才能计算准确
  5. 手眼标定位姿太少(图片太少)
    图片太少计算结果的稳定性比较差,建议十张以上
  6. 抓取时点云坐标系不是手眼标定的相机坐标系,常见于使用RGB-D相机
    使用厂商提供的RGB相机到点云坐标系的变换矩阵,变换相机坐标系到实际点云坐标系用于抓取

标签:矩阵,标定,相机,OpenCV,base,坐标系,手眼
From: https://www.cnblogs.com/champrin/p/17643041.html

相关文章

  • OpenCV双目视觉
    OpenCV双目视觉参考资料:[双目立体视觉:标定和校正双目立体视觉:标定和校正双目立体视觉:块匹配视差图计算教你如何提高双目立体视觉系统的精度立体标定cv::stereoCalibrate()flagsCV_CALIB_FIX_INTRINSIC:K和D个矩阵是固定的。这是默认标志。如果你校准好你的相机,你可......
  • 手眼标定原理
    手眼标定原理WriteByChamprinFrom2022-11-03To2022-11-04.GUETEvolutionTeamVisualGroup参考资料:外文教程5.2-手眼标定(外参标定)要实现由图像目标点到空间实际物体上抓取点之间的坐标转换,就必须拥有相机与机械臂之间的位姿转换关系,即手眼关系,因此这种标......
  • linux环境下基于python的OpenCV 保存视频
    一概念在OpenCV中保存视频使用的是VedioWriter对象,在其中指定输出文件的名称,A创建视频写入的对象out=cv2.VideoWriter(filename,fourcc,fps,frameSize)参数含义:filename:视频保存的位置fourcc:指定视频编解码器的4字节代码fps:帧率frameSize:帧大小B 设置视频的编解......
  • 张正友的相机标定论文Flexible camera calibration by viewing a plane from unknown
    张正友的相机标定论文Flexiblecameracalibrationbyviewingaplanefromunknownorientations的翻译:翻译的pdf版本为:http://pan.baidu.com/s/1pKshsPP翻译搜索复制......
  • OpenCV CAP_PROP_FRAME_COUNT 获取视频帧数问题
    OpenCV读取视频,可以通过属性CAP_PROP_FRAME_COUNT获取视频的总帧数,但是有些视频通过该属性获取的帧数和实际遍历整个视频的帧数不一样。importcv2ascvvideo='video.mp4'cap=cv.VideoCapture(video)frames=cap.get(cv.CAP_PROP_FRAME_COUNT)#通过属性获取帧数......
  • 使用 OpenCV Python 检测和可视化两个图像之间的差异
    使用OpenCVPython检测和可视化两个图像之间的差异转自:使用OpenCVPython检测和可视化两个图像之间的差异-Glasshost如果您需要比较两个图像并确定它们之间的差异,OpenCVPython提供了一种简单有效的方法来完成此任务。本指南将向您展示如何使用OpenCVPython检测和可视......
  • OpenCV3.3深度神经网络DNN模块 实例2:GoogleNet-Caffe模型实现图像分类
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>4//使用GooglenetCaffe模型实现图像分类5usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89Stringmodel_bin_file="D:/open......
  • OpenCV3.3深度神经网络DNN模块 实例3:SSD模型实现对象检测
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;//模型尺寸为300*30010constsize......
  • OpenCV3.3深度神经网络DNN模块 实例4:SSD-MobileNet模型实时对象检测
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;10constsize_theight=300;11cons......
  • OpenCV3.3深度神经网络DNN模块 实例5:FCN模型实现图像分割
    1#include<opencv2/opencv.hpp>2#include<opencv2/dnn.hpp>3#include<iostream>45usingnamespacecv;6usingnamespacecv::dnn;7usingnamespacestd;89constsize_twidth=300;10constsize_theight=30......