返回 >>>>>> PCL-3D点云总目录
一、手眼标定的原理
图例说明:
{b}:base基座标系{g}:gripper抓手坐标系{t}:target标定板坐标系{c}:camera相机坐标系
正交矩阵的特性: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210525145320401.png)
眼在手上
eye in hand,眼在手上,相机移动 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210525145507368.JPG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RpbWVSaXZlckZvcmV2ZXI=,size_16,color_FFFFFF,t_70#pic_center)
眼在手外
eye to hand,眼在手外,相机固定 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210525145606234.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RpbWVSaXZlckZvcmV2ZXI=,size_16,color_FFFFFF,t_70)
二、手眼标定的操作
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210525145645302.JPG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RpbWVSaXZlckZvcmV2ZXI=,size_16,color_FFFFFF,t_70#pic_center)
将标定板固定在机械臂末端开启机械臂,开启摄像头在距离摄像头40、60、80cm的距离上,在摄像头可见范围内,使用各种角度各拍照15-20张照片,一共45-60张。同时保存照片以及对应拍照时机械臂位姿准备好之前标定的相机内参执行手眼标定API,得到相机在基坐标系的表达(旋转矩阵R+平移向量t)
三、自己动手实现手眼标定及验证
从文件及图片读取照片直接从摄像头拍照标定
// Created by poplar on 19-7-25.
#include
#include
#include
#include "boost/filesystem.hpp" // includes all needed Boost.Filesystem declarations
#include
#include
#include "tinyxml/tinyxml2.h"
#include
// Eigen 部分
#include
// 稠密矩阵的代数运算(逆,特征值等)
#include
// Eigen 几何模块
#include
#include
#include
#include
#include
#include
#include "utils/Rotation3DUtils.h"
using namespace boost::filesystem; // for ease of tutorial presentation;
// a namespace alias is preferred practice in real code
using namespace tinyxml2;
using namespace Eigen;
using namespace cv;
using namespace std;
using namespace rw::math;
// Eigen
// OpenCV
// RobWork
const string prefix_path = "/home/ty/Workspace/Robot/calibration-single";
const string intrinsicsPath = prefix_path + "/CaliResult/3DCameraInCailResult.xml";
const string pic_dir_path = prefix_path + "/ImageFromCamera";
const string exten = "bmp";
const string extrinsic_params = prefix_path + "/extrinsic_input_param.xml";
// const string extrinsic_params = "/home/poplar/Lesson/Cobot/Aubo/calibration-single/extrinsic_input_param_t.xml";
const string exCailFilePath = prefix_path + "/CaliResult/3DCameraExCailResult.xml";
enum Pattern {
CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID
};
void printPose(const vector &pose);
void calcChessboardCorners(const Size &boardSize, float squareSize, vector &corners,
Pattern patternType = CHESSBOARD) {
corners.resize(0);
switch (patternType) {
case CHESSBOARD:
case CIRCLES_GRID:
for (int i = 0; i < boardSize.height; i++) // 9
for (int j = 0; j < boardSize.width; j++) // 6
corners.emplace_back(float(j * squareSize),
float(i * squareSize), 0);
break;
case ASYMMETRIC_CIRCLES_GRID:
for (int i = 0; i < boardSize.height; i++)
for (int j = 0; j < boardSize.width; j++)
corners.emplace_back(float((2 * j + i % 2) * squareSize),
float(i * squareSize), 0);
break;
default:
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
}
}
/**
* 通过图片集合 填充 旋转矩阵&平移矩阵
* @param R_target2cam
* @param t_target2cam
* @param imgPaths
*/
bool fillFromImages(vector &R_target2cam, std::vector &t_target2cam, std::vector &imgPaths) {
const Size patternSize(6, 9);
const float squareSize = 20;
//! [compute-poses]
std::vector objectPoints;
// [
// [0, 0 , 0]
// [0, 20, 0]
// [0, 40, 0]
// ...
// [20, 0, 0]
// ...
// ]
calcChessboardCorners(patternSize, squareSize, objectPoints);
// 通过内参进行矫正
// 检测角点
// 计算变换矩阵(旋转矩阵+平移矩阵)
cv::FileStorage fs(intrinsicsPath, FileStorage::READ);
Mat cameraMatrix, distCoeffs;
fs["cameraMatrix"] >> cameraMatrix;
fs["distCoeffs"] >> distCoeffs;
// 遍历图片
for (const auto &path: imgPaths) {
const string &s_path = path.string();
// std::cout |