精华内容
下载资源
问答
  • Halcon标定标定过程 1、制作标定板 调用函数:gen_caltab (7, 7, 0.00250, 0.4, ‘caltab_25mm.descr’, ‘caltab_25mm.ps’) 生成标定板图纸:caltab_25mm.ps 生成标定板描叙文件:caltab_25mm.descr 其中7,7为...

    Halcon标定板标定过程

    1、制作标定板

    调用函数:gen_caltab (7, 7, 0.00250, 0.4, ‘caltab_25mm.descr’, ‘caltab_25mm.ps’)
    生成标定板图纸:caltab_25mm.ps
    生成标定板描叙文件:caltab_25mm.descr
    其中7,7为生成7*7的阵列,0.00250为阵列间间距为2.5mm,0.4为直径与标记距离的比值

    2、查询相机初始参数

    StartCamPar:= [0.008,0,5.3e-006,5.3e-006,640,512,1280,1024]
    0.008:默认焦距,0:径向扭曲系数(K为负则是桶形畸变,为正是枕形畸变),5.3e-006:相机像素尺寸(Pixel Size),640:Weight/2,512:Height/2,1280:相机分辨率的宽,1024相机分辨率的高(此处是只用相机拍图能看到宽高)

    3、拍照10~20张

    架好相机,然后用标定板在相机下拍摄不同位姿图片10~20张,拍摄图片时标定板尽量覆盖整个视场(标定板要根据工作距离、视场大小定制);拍摄图片上的圆直径不得小于10个像素 。
    

    4、相机标定

    创建标定数据类型:
    create_calib_data(‘calibration_object’, 1, 1, CalibDataID)
    其中 ‘calibration_object’:相机矫正,1:一个相机,1:一个矫正对象,CalibHandle:句柄
    设置标定相机参数类型:
    set_calib_data_cam_param (CalibDataID, 0, ‘area_scan_division’, StartCamPar)

    其中,0:相机索引为1,‘area_scan_division’:面扫division模式,StartCamPar:相机初始参数

    指定所用标定板的具体描述文件:
    set_calib_data_calib_object (CalibDataID, 0, CaltabName)

    其中,0:校正对象索引,CaltabName:描述文件路径

    依次读取图片:
    寻找图片中的标定区域
    find_caltab (Image, CalPlate,CaltabName, 3, 112, 5)

    其中,3:高斯滤波核,112:标记提取的阈值,5:标定板上圆的最小直径。

    寻找标定板标志点坐标和预估外参
    find_marks_and_pose (Image, CalPlate, CaltabName, StartCamPar, 128, 10, 18, 0.9, 15, 100, RCoord, CCoord, StartPose)

    其中,128:轮廓检测的初始阈值,10:轮廓检测的初始阈值循环减少值,18:轮廓检测的最小阈值。0.9:轮廓检测的滤波系数,15:标记的轮廓的最小长度,100:标记的最大直径
    储存标定信息到标定模型
    set_calib_data_observ_points(CalibDataID, 0, 0, i, RCoord, CCoord, ‘all’, StartPose)

    其中,储存标定信息到标定模型,0:相机索引,0:标定对象的索引,‘all’:提取点与观测标定对象的标定标志的所有对应关系。

    获取相机的内外参数
    标定相机参数
    calibrate_cameras (CalibDataID, Error)

    获取相机内部参数
    get_calib_data (CalibDataID, ‘camera’, 0, ‘params’, CamParam)

    其中‘camera’:校准数据的类型,0:相机索引,‘params’:被检查数据的名称。CamParam:获得的相机内部参数

    获取相机外参数
    get_calib_data (CalibDataID, ‘calib_obj_pose’,[0,1],‘pose’,PoseCalib)

    其中,获取相机外参数‘calib_obj_pose’:校准数据的类型,[0,1]:受影响项目的索引,‘pose’:姿态。PoseCalib:获得外部参数

    展开全文
  • TCP位姿是由机器人上位机软件或API给出,因此第一步是先想办法拿到标定位姿 标定板位姿:直接跑参考文献2代码即可,我稍作了修改,代码如下: 拿到位姿之后,进一步用参考文献二中方法,标定出手眼变换...
    
    
    /// 标定板位姿解算程序
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <string>
    #include <vector>
    
    using namespace cv;
    using namespace std;
    
    /*
    @param File_Directory 为文件夹目录
    @param FileType 为需要查找的文件类型
    @param FilesName 为存放文件名的容器
    @other 目前的写法是按1.png,2.png这样来读取的,请按需要修改
    */
    void getFilesName(string &File_Directory, string &FileType, vector<string>&FilesName)
    {
        int count = 1;//初始图片索引
        while (true) {
            fstream _file;
            string pic_name = File_Directory + "/" + to_string(count) + FileType;
            _file.open(pic_name,ios::in);
            if(!_file) {
                cout<<pic_name<<"没有被创建\n";
                break;
            } else {
                cout<<pic_name<<"已经存在\n";
                FilesName.push_back(pic_name);
            }
            count++;
        }
    }
    
    void m_calibration(vector<string> &FilesName, Size board_size, Size square_size, Mat &cameraMatrix, Mat &distCoeffs, vector<Mat> &rvecsMat, vector<Mat> &tvecsMat)
    {
        ofstream fout("caliberation_result.txt");                       // 保存标定结果的文件
    
        cout << "开始提取角点………………" << endl;
        int image_count = 0;                                            // 图像数量
        Size image_size;                                                // 图像的尺寸
    
        vector<Point2f> image_points;                                   // 缓存每幅图像上检测到的角点
        vector<vector<Point2f>> image_points_seq;                       // 保存检测到的所有角点
    
        for (int i = 0;i < FilesName.size();i++)
        {
            image_count++;
    
            // 用于观察检验输出
            cout << "image_count = " << image_count << endl;
            Mat imageInput = imread(FilesName[i]);
            if (image_count == 1)  //读入第一张图片时获取图像宽高信息
            {
                image_size.width = imageInput.cols;
                image_size.height = imageInput.rows;
                cout << "image_size.width = " << image_size.width << endl;
                cout << "image_size.height = " << image_size.height << endl;
            }
    
            /* 提取角点 */
            bool ok = findChessboardCorners(imageInput, board_size, image_points, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE);
            if (0 == ok)
            {
                cout <<"第"<< image_count <<"张照片提取角点失败,请删除后,重新标定!"<<endl; //找不到角点
                imshow("失败照片", imageInput);
                waitKey(0);
            }
            else
            {
                Mat view_gray;
                cout << "imageInput.channels()=" << imageInput.channels() << endl;
                cvtColor(imageInput, view_gray, CV_RGB2GRAY);
    
                /* 亚像素精确化 */
                //find4QuadCornerSubpix(view_gray, image_points, Size(5, 5)); //对粗提取的角点进行精确化
                cv::cornerSubPix(view_gray, image_points, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 20, 0.01));
    
                image_points_seq.push_back(image_points);  //保存亚像素角点
    
                /* 在图像上显示角点位置 */
                drawChessboardCorners(view_gray, board_size, image_points, true);
    
                //imshow("Camera Calibration", view_gray);//显示图片
                //waitKey(100);//暂停0.1S
            }
        }
        cout << "角点提取完成!!!" << endl;
    
    
        /*棋盘三维信息*/
        vector<vector<Point3f>> object_points_seq;                     // 保存标定板上角点的三维坐标
    
        for (int t = 0;t < image_count;t++)
        {
            vector<Point3f> object_points;
            for (int i = 0;i < board_size.height;i++)
            {
                for (int j = 0;j < board_size.width;j++)
                {
                    Point3f realPoint;
                    /* 假设标定板放在世界坐标系中z=0的平面上 */
                    realPoint.x = i*square_size.width;
                    realPoint.y = j*square_size.height;
                    realPoint.z = 0;
                    object_points.push_back(realPoint);
                }
            }
            object_points_seq.push_back(object_points);
        }
    
        /* 运行标定函数 */
        double err_first = calibrateCamera(object_points_seq, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, CV_CALIB_FIX_K3);
        fout << "重投影误差1:" << err_first << "像素" << endl << endl;
        cout << "标定完成!!!" << endl;
    
    
        cout << "开始评价标定结果………………";
        double total_err = 0.0;            // 所有图像的平均误差的总和
        double err = 0.0;                  // 每幅图像的平均误差
        double totalErr = 0.0;
        double totalPoints = 0.0;
        vector<Point2f> image_points_pro;     // 保存重新计算得到的投影点
    
        for (int i = 0;i < image_count;i++)
        {
    
            projectPoints(object_points_seq[i], rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points_pro);   //通过得到的摄像机内外参数,对角点的空间三维坐标进行重新投影计算
    
            err = norm(Mat(image_points_seq[i]), Mat(image_points_pro), NORM_L2);
    
            totalErr += err*err;
            totalPoints += object_points_seq[i].size();
    
            err /= object_points_seq[i].size();
            //fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
            total_err += err;
        }
        fout << "重投影误差2:" << sqrt(totalErr / totalPoints) << "像素" << endl << endl;
        fout << "重投影误差3:" << total_err / image_count << "像素" << endl << endl;
    
    
        //保存定标结果
        cout << "开始保存定标结果………………" << endl;
        Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
        fout << "相机内参数矩阵:" << endl;
        fout << cameraMatrix << endl << endl;
        fout << "畸变系数:\n";
        fout << distCoeffs << endl << endl << endl;
        for (int i = 0; i < image_count; i++)
        {
            fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
            fout << rvecsMat[i] << endl;
    
            /* 将旋转向量转换为相对应的旋转矩阵 */
            Rodrigues(rvecsMat[i], rotation_matrix);
            fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
            fout << rotation_matrix << endl;
            fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
            fout << tvecsMat[i] << endl << endl;
        }
        cout << "定标结果完成保存!!!" << endl;
        fout << endl;
    }
    
    void m_undistort(vector<string> &FilesName, Size image_size, Mat &cameraMatrix, Mat &distCoeffs)
    {
    
        Mat mapx = Mat(image_size, CV_32FC1);   //X 坐标重映射参数
        Mat mapy = Mat(image_size, CV_32FC1);   //Y 坐标重映射参数
        Mat R = Mat::eye(3, 3, CV_32F);
        cout << "保存矫正图像" << endl;
        string imageFileName;                  //校正后图像的保存路径
        stringstream StrStm;
        string temp;
    
        for (int i = 0; i < FilesName.size(); i++)
        {
            Mat imageSource = imread(FilesName[i]);
    
            Mat newimage = imageSource.clone();
    
            //方法一:使用initUndistortRectifyMap和remap两个函数配合实现
            //initUndistortRectifyMap(cameraMatrix,distCoeffs,R, Mat(),image_size,CV_32FC1,mapx,mapy);
            //  remap(imageSource,newimage,mapx, mapy, INTER_LINEAR);
    
            //方法二:不需要转换矩阵的方式,使用undistort函数实现
            undistort(imageSource, newimage, cameraMatrix, distCoeffs);
    
            StrStm << i + 1;
            StrStm >> temp;
            imageFileName = "矫正后图像//" + temp + "_d.jpg";
            imwrite(imageFileName, newimage);
    
            StrStm.clear();
            imageFileName.clear();
        }
        std::cout << "保存结束" << endl;
    }
    
    int main()
    {
        string File_Directory1 = ".";   //文件夹目录1
    
        string FileType = ".png";    // 需要查找的文件类型
    
        vector<string>FilesName1;    //存放文件名的容器
    
        getFilesName(File_Directory1, FileType, FilesName1);   // 标定所用图像文件的路径
    
    
        Size board_size = Size(11, 8);                         // 标定板上每行、列的角点数
        Size square_size = Size(30, 30);                       // 实际测量得到的标定板上每个棋盘格的物理尺寸,单位mm
    
        Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));        // 摄像机内参数矩阵
        Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));          // 摄像机的5个畸变系数:k1,k2,p1,p2,k3
        vector<Mat> rvecsMat;                                          // 存放所有图像的旋转向量,每一副图像的旋转向量为一个mat
        vector<Mat> tvecsMat;                                          // 存放所有图像的平移向量,每一副图像的平移向量为一个mat
    
        m_calibration(FilesName1, board_size, square_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);
    
        //m_undistort(FilesName1, image_size, cameraMatrix, distCoeffs);
    
        return 0;
    }
    
    • 整合各个博客得到的程序(详细注释有空再加):
    //
    // Created by ch on 2020/11/25.
    //
    
    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <vector>
    #include <thread>
    #include <ui/robot_feedback.h>
    #include <ros/ros.h>
    #include <librealsense2/rs.hpp> // Include RealSense Cross Platform API
    #include "ClimberKinematics5D.h"
    #include <opencv2/core/eigen.hpp>
    #include <opencv2/opencv.hpp>   // Include OpenCV API
    #include <opencv2/calib3d.hpp>
    using namespace std;
    using namespace cv;
    
    enum CONTROL_STATUS {RECORD, WAIT, QUIT} CONTROL_FLAG;
    bool to_record = false;//交互变量,true->准备记录一组数据
    vector<double> ENCODER_DATA(5, 0);//编码器数据,用来计算TCP
    
    void control_thread() {//交互线程,输入1则记录1组数据
        bool to_quit = false;
        while (!to_quit) {
            switch (CONTROL_FLAG) {
                case CONTROL_STATUS::WAIT : {
                    int i = 0;
                    cout << "Input 1 for record, input 0 for stop recording.\n";
                    cin >> i;
                    if (i == 0) {
                        CONTROL_FLAG = CONTROL_STATUS::QUIT;
                        break;
                    } else if (i == 1) CONTROL_FLAG = CONTROL_STATUS::RECORD;
                    break;
                }
                case CONTROL_STATUS::QUIT : to_quit = true; break;
                case CONTROL_STATUS::RECORD : break;
            }
        }
    }
    void GetEncoderData(const ui::robot_feedbackConstPtr& data) {//ROS回调函数,获取编码器数据
        ENCODER_DATA[0] = data->feedbackPosData[0];
        ENCODER_DATA[1] = data->feedbackPosData[1];
        ENCODER_DATA[2] = data->feedbackPosData[2];
        ENCODER_DATA[3] = data->feedbackPosData[3];
        ENCODER_DATA[4] = data->feedbackPosData[4];
    }
    
    void ros_thread() {//ROS线程,如果不另开线程的话,main里面会有两个while(一个是realsense的while, 一个是ros的spin)
        //ros
        ros::NodeHandle n;
        ros::NodeHandle node;
        ros::Subscriber sub = node.subscribe("/low_level/joints_point_feedback", 100, GetEncoderData);
        ros::spin();
    }
    
    void m_calibration(vector<string> &FilesName, const Size& board_size, const Size& square_size, Mat &cameraMatrix, Mat &distCoeffs, vector<Mat> &rvecsMat, vector<Mat> &tvecsMat)
    {
        ofstream fout("caliberation_result.txt");                       // 保存标定结果的文件
    
        cout << "开始提取角点………………" << endl;
        int image_count = 0;                                            // 图像数量
        Size image_size;                                                // 图像的尺寸
    
        vector<Point2f> image_points;                                   // 缓存每幅图像上检测到的角点
        vector<vector<Point2f>> image_points_seq;                       // 保存检测到的所有角点
    
        for (int i = 0;i < FilesName.size();i++)
        {
            image_count++;
    
            // 用于观察检验输出
            cout << "image_count = " << image_count << endl;
            Mat imageInput = imread(FilesName[i]);
            if (image_count == 1)  //读入第一张图片时获取图像宽高信息
            {
                image_size.width = imageInput.cols;
                image_size.height = imageInput.rows;
                cout << "image_size.width = " << image_size.width << endl;
                cout << "image_size.height = " << image_size.height << endl;
            }
    
            /* 提取角点 */
            bool ok = findChessboardCorners(imageInput, board_size, image_points, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE);
            if (0 == ok)
            {
                cout <<"第"<< image_count <<"张照片提取角点失败,请删除后,重新标定!"<<endl; //找不到角点
                imshow("失败照片", imageInput);
                waitKey(0);
            }
            else
            {
                Mat view_gray;
                cout << "imageInput.channels()=" << imageInput.channels() << endl;
                cvtColor(imageInput, view_gray, COLOR_RGB2GRAY);
    
                /* 亚像素精确化 */
                //find4QuadCornerSubpix(view_gray, image_points, Size(5, 5)); //对粗提取的角点进行精确化
                cv::cornerSubPix(view_gray, image_points, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(TermCriteria::EPS+TermCriteria::COUNT, 20, 0.01));
    
                image_points_seq.push_back(image_points);  //保存亚像素角点
    
                /* 在图像上显示角点位置 */
                drawChessboardCorners(view_gray, board_size, image_points, true);
    
                imshow("Camera Calibration", view_gray);//显示图片
                waitKey(0);//暂停0.1S
            }
        }
        cout << "角点提取完成!!!" << endl;
    
    
        /*棋盘三维信息*/
        vector<vector<Point3f>> object_points_seq;                     // 保存标定板上角点的三维坐标
    
        for (int t = 0;t < image_count;t++)
        {
            vector<Point3f> object_points;
            for (int i = 0;i < board_size.height;i++)
            {
                for (int j = 0;j < board_size.width;j++)
                {
                    Point3f realPoint;
                    /* 假设标定板放在世界坐标系中z=0的平面上 */
                    realPoint.x = i*square_size.width;
                    realPoint.y = j*square_size.height;
                    realPoint.z = 0;
                    object_points.push_back(realPoint);
                }
            }
            object_points_seq.push_back(object_points);
        }
    
        /* 运行标定函数 */
        double err_first = calibrateCamera(object_points_seq, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, CALIB_FIX_K3);
        fout << "重投影误差1:" << err_first << "像素" << endl << endl;
        cout << "标定完成!!!" << endl;
    
    
        cout << "开始评价标定结果………………";
        double total_err = 0.0;            // 所有图像的平均误差的总和
        double err = 0.0;                  // 每幅图像的平均误差
        double totalErr = 0.0;
        double totalPoints = 0.0;
        vector<Point2f> image_points_pro;     // 保存重新计算得到的投影点
    
        for (int i = 0;i < image_count;i++)
        {
    
            projectPoints(object_points_seq[i], rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points_pro);   //通过得到的摄像机内外参数,对角点的空间三维坐标进行重新投影计算
    
            err = norm(Mat(image_points_seq[i]), Mat(image_points_pro), NORM_L2);
    
            totalErr += err*err;
            totalPoints += object_points_seq[i].size();
    
            err /= object_points_seq[i].size();
            //fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
            total_err += err;
        }
        fout << "重投影误差2:" << sqrt(totalErr / totalPoints) << "像素" << endl << endl;
        fout << "重投影误差3:" << total_err / image_count << "像素" << endl << endl;
    
    
        //保存定标结果
        cout << "开始保存定标结果………………" << endl;
        Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
        fout << "相机内参数矩阵:" << endl;
        fout << cameraMatrix << endl << endl;
        fout << "畸变系数:\n";
        fout << distCoeffs << endl << endl << endl;
        for (int i = 0; i < image_count; i++)
        {
            fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
            fout << rvecsMat[i] << endl;
    
            /* 将旋转向量转换为相对应的旋转矩阵 */
            Rodrigues(rvecsMat[i], rotation_matrix);
            fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
            fout << rotation_matrix << endl;
            fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
            fout << tvecsMat[i] << endl << endl;
        }
        cout << "定标结果完成保存!!!" << endl;
        fout << endl;
    }
    
    /**************************************************
    * @brief   将旋转矩阵与平移向量合成为齐次矩阵
    * @note
    * @param   Mat& R   3*3旋转矩阵
    * @param   Mat& T   3*1平移矩阵
    * @return  Mat      4*4齐次矩阵
    **************************************************/
    Mat R_T2HomogeneousMatrix(const Mat& R,const Mat& T)
    {
        Mat HomoMtr;
        Mat_<double> R1 = (Mat_<double>(4, 3) <<
                                              R.at<double>(0, 0), R.at<double>(0, 1), R.at<double>(0, 2),
                R.at<double>(1, 0), R.at<double>(1, 1), R.at<double>(1, 2),
                R.at<double>(2, 0), R.at<double>(2, 1), R.at<double>(2, 2),
                0, 0, 0);
        Mat_<double> T1 = (Mat_<double>(4, 1) <<
                                              T.at<double>(0,0),
                T.at<double>(1,0),
                T.at<double>(2,0),
                1);
        cv::hconcat(R1, T1, HomoMtr);		//矩阵拼接
        return HomoMtr;
    }
    
    /**************************************************
    * @brief    齐次矩阵分解为旋转矩阵与平移矩阵
    * @note
    * @param	const Mat& HomoMtr  4*4齐次矩阵
    * @param	Mat& R              输出旋转矩阵
    * @param	Mat& T				输出平移矩阵
    * @return
    **************************************************/
    void HomogeneousMtr2RT(Mat& HomoMtr, Mat& R, Mat& T)
    {
        //Mat R_HomoMtr = HomoMtr(Rect(0, 0, 3, 3)); //注意Rect取值
        //Mat T_HomoMtr = HomoMtr(Rect(3, 0, 1, 3));
        //R_HomoMtr.copyTo(R);
        //T_HomoMtr.copyTo(T);
        /*HomoMtr(Rect(0, 0, 3, 3)).copyTo(R);
        HomoMtr(Rect(3, 0, 1, 3)).copyTo(T);*/
        Rect R_rect(0, 0, 3, 3);
        Rect T_rect(3, 0, 1, 3);
        R = HomoMtr(R_rect);
        T = HomoMtr(T_rect);
    
    }
    
    int main(int argc, char * argv[]) try
    {
        //realsense initialization
        rs2::colorizer color_map;
        rs2::pipeline pipe;
        pipe.start();
    
        //other initialization
        ClimbotKinematics *kinematics = new ClimberKinematics5D();//运动学
        std::thread control(control_thread);
        std::thread ros_t(ros_thread);
        vector<Eigen::Matrix4d> TCP_poses;
        vector<string> pic_names;
        int count = 0;
    
        //capture pictures
        const auto window_name = "Display Image";
        namedWindow(window_name, WINDOW_AUTOSIZE);
        bool to_quit = false;
        while (waitKey(1) < 0 && getWindowProperty(window_name, WND_PROP_AUTOSIZE) >= 0 && !to_quit) {
            rs2::frameset data = pipe.wait_for_frames(); // Wait for next set of frames from the camera
            rs2::frame depth = data.get_depth_frame().apply_filter(color_map);
            rs2::frame rgb = data.get_color_frame();
    
            const int w = rgb.as<rs2::video_frame>().get_width();
            const int h = rgb.as<rs2::video_frame>().get_height();
    
            Mat image(Size(w, h), CV_8UC3, (void*)rgb.get_data(), Mat::AUTO_STEP);
            cvtColor(image, image, COLOR_BGR2RGB);
    
            //record a picture
    
            switch (CONTROL_FLAG) {
                case CONTROL_STATUS::RECORD : {
                    string num_string = to_string(count);
                    cv::imwrite("./pose_" + num_string + ".jpg" , image);
                    pic_names.emplace_back("./pose_" + num_string + ".jpg");
    
                    auto T = kinematics->GetTransformMatrix(ClimberKinematics5D::G6, ENCODER_DATA);
                    TCP_poses.push_back(T);
    
                    ofstream TCP_trasform("./TCP_pose_" + num_string + ".txt", ofstream::out);
                    Eigen::Matrix3d R = T.block(0, 0, 3, 3);
                    Eigen::Vector3d euler_angles = R.eulerAngles(2, 1, 0);
                    TCP_trasform << "Index: " << num_string << endl;
                    TCP_trasform << "Fixed X-Y-Z(x/y/z):\n" << euler_angles[0] / PI * 180 << " " << euler_angles[1] / PI * 180 << " " << euler_angles[2] / PI * 180 << endl;
                    TCP_trasform << "Position(m):\n" << T(0, 3) / 1000 << " "<< T(1, 3) / 1000 << " " << T(2, 3) / 1000 << endl << endl;
                    break;
                }
                case WAIT:
                    break;
                case QUIT:
                    to_quit = true;
                    break;
            }
    
            count++;
    
            // Update the window with new data
            imshow(window_name, image);
        }
        //camera calibration and get board pose
        Size board_size = Size(11, 8);                         // 标定板上每行、列的角点数
        Size square_size = Size(25, 25);                       // 实际测量得到的标定板上每个棋盘格的物理尺寸,单位mm
        Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));        // 摄像机内参数矩阵
        Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));          // 摄像机的5个畸变系数:k1,k2,p1,p2,k3
        vector<Mat> R_target2cam;                                          // 存放所有图像的旋转向量,每一副图像的旋转向量为一个mat
        vector<Mat> t_target2cam;                                          // 存放所有图像的平移向量,每一副图像的平移向量为一个mat
        m_calibration(pic_names, board_size, square_size, cameraMatrix, distCoeffs, R_target2cam, t_target2cam);
        //至此,第2部分完成
    
        //开始第3部分:矩阵格式转换
        vector<cv::Mat> R_gripper2base, t_gripper2base;
        for (auto& T : TCP_poses) {
            Mat cv_format_tcp_pose, cv_format_tcp_rvec, cv_format_tcp_tvec;
            eigen2cv(T, cv_format_tcp_pose);
            HomogeneousMtr2RT(cv_format_tcp_pose, cv_format_tcp_rvec, cv_format_tcp_tvec);
            R_gripper2base.emplace_back(cv_format_tcp_rvec);
            t_gripper2base.emplace_back(cv_format_tcp_tvec);
        }
        //至此,第3部分完成
    
        //开始第4部分:手眼标定程序
        cv::Mat R_cam2gripper,T_cam2gripper;
        calibrateHandEye(R_gripper2base,t_gripper2base,R_target2cam,t_target2cam,R_cam2gripper,T_cam2gripper);
    
        //第五部分:矩阵格式转换
        auto Homo_cam2gripper = R_T2HomogeneousMatrix(R_cam2gripper, T_cam2gripper);
        cout << "Homo_cam2gripper:\n" << Homo_cam2gripper << endl;
        cout << endl << "Finish.";
        return EXIT_SUCCESS;
    }
    catch (const rs2::error & e)
    {
        std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n    " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    
    
    
    展开全文
  • 环境 Ubuntu16.04 autoware版本 1.标定相机内参 在autoware/ros下, 在这里插入代码片

    环境 Ubuntu16.04 autoware1.11.1

    1.标定相机的内参

    在autoware/ros下,

    source install/setup.bash
    rosrun autoware_camera_lidar_calibrator cameracalibrator.py --square 0.04 --size 8x6 image:=cam0/image_raw 
    

    会用到待标定完成后,标定好的内参文件默认路径在/home/jinye下,后面的联合标定会用到。
    图片的信息可以由rosbag播放或者直接来自于相机的实时获取。

    2.相机和激光雷达的联合标定

    2.1通过autoware的camera_lidar_calibration.launch

    在autoware/ros下

    source install/setup.bash
    roslaunch autoware_camera_lidar_calibrator camera_lidar_calibration.launch intrinsics_file:=/home/jinye/20200619_0951_autoware_camera_calibration.yaml image_src:=/cam0/image_raw
    

    具体的操作可以看这篇博客
    https://dlonng.com/posts/autoware-calibr-1

    2.2通过Calibration ToolKit

    这个标定工具箱在我这个版本里已经移除了,可以自己编译安装

    具体的操作可以看这篇博客
    https://dlonng.com/posts/autoware-calibr-2

    3.各个版本的autoware下载链接

    https://github.com/Autoware-AI/autoware.ai/releases
    可以根据自己的需要自行下载,提供给需要的人。

    展开全文
  • 相机的装载位置 不在手上(eye-to-hand) 相机固定在一个地方,机械手的运动不会带着相机一起移动。 在手上(eye-in-hand) 相机安装在机械手上,随着机械手一起移动。...标定的过程和手眼分离系统的标

    相机的装载位置

    不在手上(eye-to-hand)

    相机固定在一个地方,机械手的运动不会带着相机一起移动。

    在手上(eye-in-hand)

    相机安装在机械手上,随着机械手一起移动。较为常用。这个实际上和eye-to-hand类似。

    可以快速有效地标定被测物体的坐标。

    这种情况的标定过程实际上和相机和机械手分离的标定方法是一样的,因为相机拍照时,机械手会运动到相机标定的时候的位置,然后相机拍照,得到目标的坐标,再控制机械手,所以简单的相机固定在末端的手眼系统很多都是采用这种方法,标定的过程和手眼分离系统的标定是可以相同对待的。

    ●基于图像的视觉控制

    ●基于位置的视觉控制

    ●结合两者的混合视觉控制

    在正式开始讲解之前,可以看一下:深入浅出地理解机器人手眼标定

    对手眼标定有一个直观的认识。

    正式开始

    本文的相机搭载方案是,hand-in-eye。移动相机,标定求解过程

    在推导过程中,我们会用到四个坐标系,分别是:

    ●基础坐标系(用base表示)

    ●机械手坐标系(用tool表示)

    ●相机坐标系(用cam表示)

    ●标定物坐标系(用cal表示)

    下面先给出示意图:

    在这里插入图片描述
    坐标系之间的转换关系说明:

    ●baseHtool:表示机械手坐标系到基础坐标系的转换关系,可以由机器人系统中得出。(已知)

    ●toolHcam:表示相机坐标系到机械手坐标系的转换关系;这个转化关系在机械手移动过程中是不变的;(未知,待求)

    ●calHcam:表示相机坐标系到标定板坐标系的转换关系(相机外參),可以由相机标定求出;(相当于已知)

    ●baseHcal:表示标定板坐标系到基础坐标系的变换,这个是最终想要得到的结果;只要机械手和标定板的相对位置不变,这个变换矩阵不发生变化。

    在这里插入图片描述
    所以:其中的A已知,X待求,B需要通过相机标定得知(张正友标定法可以求得)。

    验证结果

    在这里插入图片描述

    1. 基础坐标系(求解baseHtool)

    符合右手定则的XYZ三个坐标轴

    ●原点:机器人底座的中心点

    ●X轴正向:指向机器人的正前方

    ●Z轴正向:指向机器人的正上方

    ●Y轴正向:由右手定则确定

    在这里插入图片描述
    在这里插入图片描述
    六个自由度

    ●三个位置:x、y、z(第六轴法兰盘圆心相对于原点的偏移量)

    ●三个角:Rx、Ry、Rz(第六轴法兰盘的轴线角度,由初始姿态即竖直向上绕x轴旋转Rx度,再绕Y轴旋转Ry度,再绕Z轴旋转Rz度得到)

    ●旋转方式(机器人RPY角和Euler角 – 基本公式)(机器人学-熊有伦36-40页)

    ●绕定轴X-Y-Z旋转(判断机械臂输出四元数与代码得到的四元数是否相等得到)

    在这里插入图片描述
    一定要注意欧拉角和李代数不一样,非常容易搞混,因为他们都是3个量

    欧拉角:分别绕x、y、z轴旋转的角度,不一样的旋转次序,得到的R不一样;

    李代数:维度是3,是绕一个轴转动一定的角度。欧拉角可以理解成李代数在x、y、z轴上的分解旋转。(不一定正确,不过比较形象)

    注:不同机械臂示教器显示的法兰盘的数据格式不一样,有的是用欧拉角显示的,有的是用角轴显示的。

    1. camHcal相机到标定板

    ●注意:标定板坐标系下的坐标转换到相机坐标系下

    思路大致如下:

    ●已知双目相机的内参、畸变系数、外参(Pr=R∗Pl+t P_r=R*P_l+tP

    r=R∗P l +t),

    ●对左右相机的两张图片调用OpenCV中的findChessboardCorners函数,找到内角点(如果结果不好,继续提取亚像素点);

    ●将左右相机的像素点对应起来,得到匹配的2d点;

    ●使用空间异面直线的方法,用对应的2d点计算出以右相机为世界坐标系的3维坐标Pcam P_{cam}P cam ;(立体视觉匹配)

    ●计算出每个角点以棋盘格为世界坐标的3维坐标Pcal P_{cal}P cal;

    ●通过解方程Pcam=camHcal∗Pcal P_{cam}=camHcal*P_{cal}P cam=camHcal∗P cal 求解出外参(3d-3d:ICP,SVD奇异值分解(十四讲173页))

    张正友相机标定Opencv实现:

    在这里插入图片描述
    参数解释:

    ●第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;

    ●第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;

    ●第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector image_points_buf;

    ●第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。

    1. 求解AX=XB

    以下四篇论文对应着四种求解方法

    Tsai, Roger Y., and Reimar K. Lenz. “A new technique for fully autonomous and efficient 3D robotics hand/eye calibration.” IEEE Transactions on robotics and automation 5.3 (1989): 345-358.(博客:Tsai-Lenz的OpenCV实现)

    Horaud, Radu, and Fadi Dornaika. “Hand-eye calibration.” The international journal of robotics research 14.3 (1995): 195-210.

    Park, Frank C., and Bryan J. Martin. “Robot sensor calibration: solving AX= XB on the Euclidean group.” IEEE Transactions on Robotics and Automation10.5 (1994): 717-721.(博客:Navy的OpenCV实现)

    Daniilidis, Konstantinos. “Hand-eye calibration using dual quaternions.” The International Journal of Robotics Research 18.3 (1999): 286-298.

    网上有源代码可以下载:经典手眼标定算法C++代码

    文献3采用的是李群的理论,将AX=XB转化成最小二乘问题;

    文献4采用的时对偶四元数的知识,用对偶四元数表达旋转和平移,从而进行统一计算;

    着四种算法精度差不多,不过文献4的效果要更好点。

    具体实现文献3的算法,下面具体介绍

    对数:乘法变加法

    李群李代数

    在这里插入图片描述
    利用李群知识求解AX=XB

    在这里插入图片描述
    采用“两步法”求解上述方程,先解算旋转矩阵,再求得平移向量。

    求解旋转矩阵

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    代码:用两组数据求解方程AX=XB

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 本文是MATLAB鱼眼相机的标定过程,建立在学习普通相机的标定过程之上。转载请注明出处。请高手指教结果出现更加畸形原因。 普通相机的标定引用自:http://blog.csdn.net/jameshater/article/details/53172333 好多...
  • 相机标定是进行视觉测量和定位基础工作之一,标定参数准确与否直接关系到整个系统精度,相机标定过程标定图片数据采集过程中需要注意以下问题: 标定板拍摄张数要能覆盖整个测量空间及整个测量视场,把相机...
  • 从Halcon视频中总结手眼标定详细过程标定板描述文件准备和14张以上有标定图像和每张图像对应机器人位姿 标定板描述文件加载 读取相机内部参数 创建手眼标定模型 对手眼标定模型...
  • 今天我们的问题主要是出现在内参标定的过程中的 首先,我的参考资料主要是官方发的教程: https://github.com/code-iai/iai_kinect2/tree/master/kinect2_calibration#calibrating-the-kinect-...
  • 相机的标定之手机相机的标定

    千次阅读 2020-03-28 16:15:36
    相机的标定是 SLAM 最开始的部分,由于设备原因,这个星期只做了手机相机的标定。这篇文章主要就是介绍一下相机标定的原理以及用OpenCV中现有的函数或是Matlab做相机标定的过程
  • 机械手标定算法推导过程,便于写代码前算法理解。
  • 在视觉标定过程中有个问题? 1.标定过程标定位置要随机改变,也就说每幅图物距是在变化可是镜头是定焦 镜头,这样拍出来图像根本就不清晰,标定出来怎么能准确呢?请大神指教。。
  • OpenCV相机标定过程

    2019-11-19 14:01:50
    一、OpenCV标定的几个常用函数 findChessboardCorners() 棋盘格角点检测 bool findChessboa...
  • 用于标定的标定板制作的精度一定要高,精度低的误差会很大。 2 相机 在标定过程中相机调好后就不能再动了,稍微动一点也要重新标定。 3 清洁度检查 检查拍摄的图像有没有污点, 如果有要首先判断是在相机上还是在...
  • 主要介绍Inter D435i深度相机的IMU、相机和IMU与相机外参数标定的过程。其中,IMU使用的是realsense官方文档的教程,相机和外参数使用的是Kalibr的标定方法。 目录 1. 相机内参标定1.1 配置依赖项1.2 新建工作...
  • opencv4.0+VS2017 C++自带例程相机标定详细过程

    千次阅读 热门讨论 2019-05-29 23:32:53
    参考各位前辈的博客,我总结了标定的两种方法: (1)利用cmd+vs+自带例程hpp (2)利用自带例程hpp+vs 个人感觉第二种简单。 开始介绍之前,我先做以下为铺垫,方便两种方法,也适合像我一样的菜鸟扫盲。 ...
  • opencv4.1+VS2017 C++自带例程相机标定详细过程1打开官方OpenCV单相机标定文件夹2在VS里新建项目3编译4图片校正(单张) 1打开官方OpenCV单相机标定文件夹 按路径D:\opencv4.1.0\opencv\sources\samples\cpp\...
  • kalibr相机内参标定优化过程和原理

    千次阅读 2019-05-06 12:01:53
    如果是多相机标定,在完成内参标定的同时,也会完成具有交叉视野相机外参的的标定。初始估计步骤也会进行多相机基线距离的估计,用作后续的迭代优化。 优化过程如下: 将提取到的靶标角点按照打上时间标签,然后...
  • 最近的项目中要做关于一个EPSON机器人和两个康耐视相机之间的手眼标定,而刚好之前有接触过强大的halcon,所以打算利用halcon手眼标定例程来做,在看代码的过程中发现,halcon在标定之前会读取一个.cpd的文件,作为...
  • 标定板制作过程

    千次阅读 2018-12-06 16:47:01
    标定的作用其一就是为了求取畸变系数(因为经过镜头等成像后,或多或少都有畸变),其二是为了得到空间坐标系和图像坐标系的对应关系。 确认光学系统的性能,复原相机模型的3D空间至2D空间的一一对应关...
  • 摄像机标定过程为了进行摄像机标定,必须已知世界坐标系中足够多三维空间点坐标,找到这些点在图像中投影二维图像坐标,并建立对应关系。平面标定几大优势:非常易于操作;可以制作非常精确;可以方便用...
  • 手眼标定过程记录

    千次阅读 2018-01-19 14:11:07
    手眼标定过程记录 ============================================================================================ 以下四个变量是最重要数据 rvecs_rb2gripper, tvecs_rb2gripper rvecs_cam, tvecs_cam ...
  • ------------------------------------------------------胖子...外参就是在实际应用过程中,所需要像素与轴运动一个比例(简单来说),算出这个比例 通俗来说,就是需要将相机图像坐标与真实X,Y轴进行统一。 怎
  • 介绍了手眼标定的算法过程,并进行了严格数学推导。
  • ROS下单目相机标定过程

    万次阅读 热门讨论 2018-05-21 14:47:22
    下面简单记录一下我利用ros标定相机参数的过程,Ubuntu 16.04 ,摄像头用的罗技C920 ROSwiki有相机矫正的官方文档,有单目的也有立体相机的教程,建议直接看原文,原汁原味:链接 1、相机标定第一步,准备一张标定...
  • Camera-IMU标定过程

    万次阅读 2017-09-17 22:00:37
    Camera和IMU的标定过程问题说明VIO(Visual-Inertial Odometry)视觉惯导里程计是在视觉里程计基础上加入了IMU数据进行融合来更精确估计位姿。在VIO算法中,Camera和IMU融合都需要基于Camera和IMU坐标系进行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,764
精华内容 705
关键字:

标定的过程