关于PnP测距部分的代码

    /** pnp测距 */
    Eigen::Vector3d pnpSolve(cv::Point2f *p, int type,Eigen::Matrix<double, 3, 1>& R);         // 测距函数
    Eigen::Vector3d pnpSolve_buff(cv::Point2f *p, int type);    // 测距函数(buff)

我们有两个函数,这个区别装甲板和能量机关的两个函数,里面的计算是一样的

可以合并在一起,但是因为能量机关和装甲板的代码不是同一个人写的,为了保证稳定,区别出来保证不会影响自己的代码

装甲板的测距自然是测量到装甲板中心的距离

而能量机关的测量就是扇叶的中心到车的距离

下面是代码:

Eigen::Vector3d AngleSolve::pnpSolve(cv::Point2f *p, int type,Eigen::Matrix<double, 3, 1>& R)
{
    std::vector<cv::Point3d> ps;        // 世界坐标系
    std::vector<cv::Point2f> pu;        // 像素坐标系
    double w;                           // 宽度
    double h;                           // 高度
    //=======================宽高选择=======================
    if(type == SMALL)                   // 小装甲板
    {
        w = small_w;
        h = small_h;
//        std::cout << "small" << std::endl;
    }
    else if(type == BIG)                // 大装甲板
    {
//        w = big_w;
//        h = big_h;
        w = small_w;
        h = small_h;
//        std::cout << "BIG" << std::endl;
    }

    tv = Eigen::Vector3d();                 //平移向量
    rv = Eigen::Matrix<double,3,3>();       //旋转矩阵
    //=======================坐标系========================
    /** 世界坐标系 */
    ps = {
            {-w / 2 , -h / 2, 0.},      //左上
            { w / 2 , -h / 2, 0.},      //右上
            { w / 2 ,  h / 2, 0.},      //右下
            {-w / 2 ,  h / 2, 0.}       //左下
    };
    /** 像素坐标系 */
    pu.push_back(p[3]);                          //左上
    pu.push_back(p[2]);                          //右上
    pu.push_back(p[1]);                          //右下
    pu.push_back(p[0]);                          //左下

    cv::Mat rvec;
    cv::Mat tvec;

    // pnp测距
    cv::solvePnP(ps, pu, F_MAT, C_MAT, rvec, tvec/*, SOLVEPNP_IPPE*/);

    Eigen::Matrix<double, 3, 1> R_v;
    cv::cv2eigen(rvec,R_v);
    R = R_v;

    cv::Mat rv_mat;
    cv::Rodrigues(rvec,rv_mat);     // 将旋转向量转为旋转矩阵
    cv::cv2eigen(rv_mat,rv);
    cv::cv2eigen(tvec, tv);

#ifdef SHOW_MEASURE_RRECT
    cv::Mat pnp_check = _src.clone();
    std::cout<<"rv"<<rv<<std::endl;

    //=========================坐标系转换==============================

    cv::Point2f m_lu,m_ld,m_ru,m_rd;             //定义4点坐标
    Eigen::Vector3d imuPoint;                    //世界坐标系
    Eigen::Vector3d armorPoint;                  //相机坐标系

    imuPoint = {-w / 2 , -h / 2, 0.};   //世界坐标系左上
    armorPoint = rv*imuPoint + tv;
    m_lu = cam2pixel(armorPoint);

    imuPoint = {-w / 2 , h / 2, 0.};    //世界坐标系左下
    armorPoint = rv*imuPoint + tv;
    m_ld = cam2pixel(armorPoint);

    imuPoint = {w / 2 , -h / 2, 0.};    //世界坐标系左下
    armorPoint = rv*imuPoint + tv;
    m_ru = cam2pixel(armorPoint);

    imuPoint = {w / 2 , h / 2, 0.};     //世界坐标系左下
    armorPoint = rv*imuPoint + tv;
    m_rd = cam2pixel(armorPoint);

    /** 将坐标用圆形表示 */
    circle(pnp_check,m_lu,3,cv::Scalar(0,255,0),-1);
    circle(pnp_check,m_ld,3,cv::Scalar(255,255,0),-1);
    circle(pnp_check,m_ru,3,cv::Scalar(0,0,255),-1);
    circle(pnp_check,m_rd,3,cv::Scalar(0,255,255),-1);

    /** 将坐标用线连接 */
    line(pnp_check,m_lu,m_ld,cv::Scalar(0,0,0),2);
    line(pnp_check,m_ld,m_rd,cv::Scalar(255,0,0),2);
    line(pnp_check,m_rd,m_ru,cv::Scalar(255,0,255),2);
    line(pnp_check,m_ru,m_lu,cv::Scalar(255,255,0),2);

    /** 输出4点坐标和 */
    std::cout<<"m_lu:"<<m_lu<<std::endl;
    std::cout<<"m_ld:"<<m_ld<<std::endl;
    std::cout<<"m_ru:"<<m_ru<<std::endl;
    std::cout<<"m_rd:"<<m_rd<<std::endl;
    std::cout<<"tvec:"<<cam2pixel(tv)<<std::endl;

    imshow("pnp_check",pnp_check);
#endif
    return tv;
}

SHOW_MEASURE_RRECT 是一个测试点,这里我就不展开讲了,自己观看

我们比赛中分为大、小装甲板,这里是都当小装甲板进行计算了,这样对大装甲板的计算会有一定的误差,虽然不会太大

为什么不区分呢,因为大小装甲板的区别一直是个难题,两者在低分辨率、低曝光下,太过难于区别,小装甲板一旦识别为大装甲板

对测距是一个致命的错误,为什么你可以自己想一想同样的大小,小变大会怎么样,相反,大装甲板识别成小装甲板的影响反而没有很大,当然你可以对装甲板ID为1的特判为大装甲板,这里纯属我后面忘记改了而已

接下来进入正题,这里small_w、small_h是小装甲板的实际大小,这里的实际高宽指的是灯条的高和两个灯条的中心距离

你可以自己量量看


我们的装甲板角点顺序是从右下角开始,逆时针计算,这里可以看到有一个世界坐标系和像素坐标系,两者要一一对应,不然会计算错误

如图: 凑合看看

这里装甲板的中心就是(0,0)也就是3D坐标系的中心,

    ps = {
            {-w / 2 , -h / 2, 0.},      //左上
            { w / 2 , -h / 2, 0.},      //右上
            { w / 2 ,  h / 2, 0.},      //右下
            {-w / 2 ,  h / 2, 0.}       //左下
    };
    /** 像素坐标系 */
    pu.push_back(p[3]);                          //左上
    pu.push_back(p[2]);                          //右上
    pu.push_back(p[1]);                          //右下
    pu.push_back(p[0]);                          //左下

坐标3对应的世界坐标就是(-w / 2 , -h / 2, 0.) 对应x,y,z

所有的像素坐标和3D坐标相对应

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

需要的参数就是,像素坐标、世界坐标、相机内参矩阵、相机畸变矩阵

相应得到的就是旋转向量、平移向量,tvec就是我们的世界坐标系

results matching ""

    No results matching ""