Vins-Fusion之 相机—IMU在线标定(两帧间旋转估计)(十)

重点理解以下几点:

a.程序的过程

b.包含的数据计算公式(暂时未完全明白,还要理解:本质矩阵分解R/T、三角化验证)

c.怎么得到最优解?

在函数solveRelativeR(corres) 得到的是视觉两帧的相对旋转,具体代码如下:

cpp 复制代码
//从一对帧之间的归一化特征匹配点集 corres 中,估计这两帧相机之间的相对旋转矩阵
Matrix3d InitialEXRotation::solveRelativeR(const vector<pair<Vector3d, Vector3d>> &corres)
{
    if (corres.size() >= 9)
    {
        vector<cv::Point2f> ll, rr;
        for (int i = 0; i < int(corres.size()); i++)
        {
            ll.push_back(cv::Point2f(corres[i].first(0), corres[i].first(1)));
            rr.push_back(cv::Point2f(corres[i].second(0), corres[i].second(1)));
        }
        cv::Mat E = cv::findFundamentalMat(ll, rr);//估计基础矩阵E,用于后续分解得到相对旋转矩阵R1和R2
        cv::Mat_<double> R1, R2, t1, t2;
        decomposeE(E, R1, R2, t1, t2);//从 E 分解出 两个可能的旋转 R1, R2 和两个平移 t1, t2,共有 4 种姿态组合

        if (determinant(R1) + 1.0 < 1e-09)
        {
            E = -E;
            decomposeE(E, R1, R2, t1, t2);
        }
        //四种组合分别做三角化测试
        double ratio1 = max(testTriangulation(ll, rr, R1, t1), testTriangulation(ll, rr, R1, t2));
        double ratio2 = max(testTriangulation(ll, rr, R2, t1), testTriangulation(ll, rr, R2, t2));
        cv::Mat_<double> ans_R_cv = ratio1 > ratio2 ? R1 : R2;//选择三角化测试得分更高的组合作为最终的相对旋转矩阵

        Matrix3d ans_R_eigen;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                ans_R_eigen(j, i) = ans_R_cv(i, j);
        return ans_R_eigen;
    }
    return Matrix3d::Identity();
}

输入:两帧之间的特征匹配点对

输出:两帧相机之间的相对旋转矩阵 R

步骤1:提取归一化坐标

cpp 复制代码
vector<cv::Point2f> ll, rr;
for (int i = 0; i < int(corres.size()); i++)
{
    ll.push_back(cv::Point2f(corres[i].first(0),  corres[i].first(1)));
    rr.push_back(cv::Point2f(corres[i].second(0), corres[i].second(1)));
}
步骤2:估计基础矩
cpp 复制代码
cv::Mat E = cv::findFundamentalMat(ll, rr);

从两幅图像中的对应点计算基本矩阵。

cv::findFundamentalMat 是 OpenCV 中用于计算两幅图像之间基本矩阵(Fundamental Matrix)的函数。基本矩阵描述了两个未校准摄像机之间的几何关系,它在计算机视觉中用于立体视觉、运动结构恢复(Structure from Motion, SfM)、视觉里程计等任务。

  • 基础矩阵 FF(代码中变量名为 E)满足:p2TFp1=0
  • 从匹配点对估计 F
步骤3:分解基础矩阵得到候选姿态
cpp 复制代码
cv::Mat_<double> R1, R2, t1, t2;
decomposeE(E, R1, R2, t1, t2);

数学原理:

  • 从基础矩阵 FF 可分解出本质矩阵 E(归一化坐标下)
  • 本质矩阵 E=[t]×RE,其中 [t]×[t]× 是平移的反对称矩阵
  • SVD 分解 E=UΣVT 后,可构造两个旋转和两个平移方向

结果:4 种候选姿态组合

  • (R1, t1)
  • (R1, t2)
  • (R2, t1)
  • (R2, t2)
步骤4:三角化测试选择正确解
cpp 复制代码
double InitialEXRotation::testTriangulation(...)
{
    // 1. 构建两个相机的投影矩阵
    cv::Matx34f P = cv::Matx34f(1, 0, 0, 0,   // 第一帧:单位矩阵(参考帧)
                                 0, 1, 0, 0,
                                 0, 0, 1, 0);
    cv::Matx34f P1 = cv::Matx34f(R(0,0), R(0,1), R(0,2), t(0),  // 第二帧:R|t
                                  R(1,0), R(1,1), R(1,2), t(1),
                                  R(2,0), R(2,1), R(2,2), t(2));
    
    // 2. 三角化得到3D点云
    cv::triangulatePoints(P, P1, l, r, pointcloud);
    
    // 3. 统计在两相机前方(深度>0)的点数
    int front_count = 0;
    for (int i = 0; i < pointcloud.cols; i++)
    {
        // 将齐次坐标转为3D坐标
        cv::Mat_<double> p_3d_l = cv::Mat(P) * (pointcloud.col(i) / normal_factor);
        cv::Mat_<double> p_3d_r = cv::Mat(P1) * (pointcloud.col(i) / normal_factor);
        
        // 检查深度是否为正(点在相机前方)
        if (p_3d_l(2) > 0 && p_3d_r(2) > 0)
            front_count++;
    }
    
    // 4. 返回前方点的比例
    return 1.0 * front_count / pointcloud.cols;
}

原理(Cheirality Constraint):

  • 真实姿态下,大部分三角化点应在两相机前方(深度 > 0)
  • 错误姿态会导致大量点在后方(深度 < 0)
  • 选择前方点比例最高的旋转

完整流程图:

**输入: corres (特征匹配点对)

提取归一化坐标 (ll, rr)

估计基础矩阵 F (cv::findFundamentalMat)

SVD分解得到4种候选姿态:
(R1, t1), (R1, t2), (R2, t1), (R2, t2)

修正符号(确保旋转矩阵行列式=+1)

对4种组合分别做三角化测试:

  • 三角化得到3D点云
  • 统计深度>0的点数
  • 计算前方点比例

    选择比例最高的旋转矩阵

    转换为Eigen格式并返回**
相关推荐
琅琊榜首202020 分钟前
AI生成脑洞付费短篇小说:从灵感触发到内容落地
大数据·人工智能
imbackneverdie28 分钟前
近年来,我一直在用的科研工具
人工智能·自然语言处理·aigc·论文·ai写作·学术·ai工具
cici1587435 分钟前
大规模MIMO系统中Alamouti预编码的QPSK复用性能MATLAB仿真
算法·matlab·预编码算法
历程里程碑43 分钟前
滑动窗口---- 无重复字符的最长子串
java·数据结构·c++·python·算法·leetcode·django
roman_日积跬步-终至千里1 小时前
【计算机视觉-作业1】从图像到向量:kNN数据预处理完整流程
人工智能·计算机视觉
春日见1 小时前
自动驾驶规划控制决策知识点扫盲
linux·运维·服务器·人工智能·机器学习·自动驾驶
人工智能AI技术1 小时前
【Agent从入门到实践】43 接口封装:将Agent封装为API服务,供其他系统调用
人工智能·python
hjs_deeplearning1 小时前
文献阅读篇#14:自动驾驶中的基础模型:场景生成与场景分析综述(5)
人工智能·机器学习·自动驾驶
nju_spy2 小时前
离线强化学习(一)BCQ 批量限制 Q-learning
人工智能·强化学习·cvae·离线强化学习·双 q 学习·bcq·外推泛化误差