浅谈单图标定、九点标定、十二点标定

一、九点标定与单图标定

所谓单图标定指的是相机拍摄一张图像得到像素当量,可以得到图像中的物理单位与像素单位的比例关系,即像素当量。
在这里插入图片描圆孔图述孔
例如通过拍摄一张圆孔图像,通过图像中圆孔的实际物理间距与像素间距可以推测出,当前视野中每个像素(pix)所代表几个毫米(mm)。仅需要一张图就可以得到详细的pix/mm像素当量。

但是,如果设备中使用机器人,因为机器人平移使用的XoY坐标系与单图标定的XoY坐标系存在偏差(例如,图像中表示的(5mm,5mm)坐标需要平移到(8mm,8mm)位置,发送给机器人平移量为Tx=3mm,Ty=3mm会导致机器人位置走错,因为坐标系不是同一个!)。

为了将图像中的XoY坐标系与机器人的XoY坐标系对应,此时需要九点标定!
棋盘格标定板

具体流程为:

  1. 在标定板上取九个点,九个点尽可能分布在视野各处,操控机器人使用探针各个探测九个点,得到九个点对应的机器人坐标。(注意,为了探测精准,需要使用棋盘格标定板,不建议使用圆孔。)
  2. 将机器人移动到原点处(自己定义,便于操作处即可)拍摄一张棋盘格图像,得到棋盘格图像的九个像素坐标。
  3. 将九个点的像素坐标与机器人坐标对应起来,构建仿射关系,之后可以随意取棋盘格角点,计算机器人坐标作为验证。

到此为止,已经通过九点标定将图像坐标的偏移与机器人的偏移一一对应。但是问题也随之来临!机器人如果需要夹爪获取工件,或者需要伴随工件旋转,此时可以发现通过九点标定完成的仿射变换存在明显误差!而且旋转角度越大,偏差越大!
原因在于机器人的旋转中心与图像旋转中心不一致!举个例子,点(1,1)绕着(0,0)逆时针旋转45°,得到的点为(0,1);但是点(1,1)绕着点(1,1)点转任意角度都是(1,1)点!所以如果旋转中心不一致,那么旋转的角度就不一致,之后按照坐标系平移的Tx,Ty自然对应不上。
为了将机器人旋转中心与图像旋转中心对应,需要进行十二点标定。

二、十二点标定

所谓十二点标定,就是在九个点标定平移当量的基础上,再使用三个点标定旋转当量。具体步骤为:

  1. 原点位置取图,如九点标定第二步,拍摄一张棋盘格图像,得到中间点(记得做标记)的像素坐标。
  2. 机器人在原点位置旋转,旋转θ度,使刚才的中间点尽可能偏移到图像边缘。
  3. 机器人在原点位置旋转,旋转-γ度,使刚才的中间点尽可能偏移到另一个图像边缘。
    通过三个点拟合圆,圆心就是旋转中心。
    之后的计算中,图像旋转转成机器人相关操作时,以旋转中心进行旋转。

三、参考到十二点标定的简易版九点标定

十二点标定的目的是将机器人旋转中心与图像中的旋转中心对应,如果操作机器人旋转中心固定在图像中某点,是否就可以避免了十二点标定转而使用九点完成呢?
答案当然是可以!
首先,在操控机器人以固定的点位在相对工作距离上移动,移动保证棋盘格某点(特征点)始终在图像视野中,并且尽可以分散在整个视野中。例如,机器人的点位为:
HTuple RobotX = new HTuple(0, 20, 20, 0, -20, -20, -20, 0, 20);
HTuple RobotY = new HTuple(0, 0, 20, 20, 20, 0, -20, -20, -20);
此时对应位置的特征点像素坐标为:
HTuple CamRows = new HTuple(1739, 2054, 2058, 1748, 1433, 1427, 1422, 1732, 2048);
HTuple CamCols = new HTuple(1641, 1635, 1947, 1954, 1963, 1645, 1332, 1327, 1321);
通过仿射变换可以得到一个仿射变换矩阵,之后,将机器人的旋转中心(TCP点)固定在图像中某点(例如棋盘格中心点),之后每次旋转量都已棋盘格中心点确定,此时,机器人与图像旋转量完全对应!
这种方法需要注意,操作机器人旋转始终要在固定的一个点,不能因为旋转而导致机器人TCP点偏移。

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV的十二点标定法是在九点标定法的基础上增加了三个,以提高标定精度。这三个分别位于棋盘格的左上角、右上角和右下角。与九点标定法类似,十二点标定法也需要使用多组不同角度的图片进行标定。下面是使用C++实现OpenCV的十二点标定法的代码: ```cpp #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 义棋盘格的行数和列数 int board_w = 11; int board_h = 8; // 义棋盘格中每个方格的大小 float square_size = 20; // 义棋盘格的三维坐标 vector<Point3f> obj; for (int i = 0; i < board_h; i++) { for (int j = 0; j < board_w; j++) { obj.push_back(Point3f(j * square_size, i * square_size, 0)); } } // 义图像中棋盘格的二维坐标 vector<vector<Point2f>> image_points; vector<Point2f> corners; // 读取多张图片进行标定 for (int i = 1; i <= 20; i++) { // 读取图片 Mat image = imread("image_" + to_string(i) + ".jpg"); // 寻找棋盘格角 bool found = findChessboardCorners(image, Size(board_w, board_h), corners); // 如果找到了角,则进行亚像素级角检测 if (found) { Mat gray; cvtColor(image, gray, COLOR_BGR2GRAY); cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1)); // 将角添加到图像坐标中 image_points.push_back(corners); // 在图像中绘制角 drawChessboardCorners(image, Size(board_w, board_h), corners, found); imshow("image", image); waitKey(0); } } // 进行标定 Mat camera_matrix, dist_coeffs; vector<Mat> rvecs, tvecs; calibrateCamera(objectPoints, image_points, Size(image.cols, image.rows), camera_matrix, dist_coeffs, rvecs, tvecs); // 输出标定结果 cout << "camera_matrix:" << endl << camera_matrix << endl; cout << "dist_coeffs:" << endl << dist_coeffs << endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值