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格式并返回**
相关推荐
文心快码BaiduComate29 分钟前
百度云与光本位签署战略合作:用AI Agent 重构芯片研发流程
前端·人工智能·架构
风象南1 小时前
Claude Code这个隐藏技能,让我告别PPT焦虑
人工智能·后端
Mintopia2 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮2 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬3 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia3 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区3 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两6 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪6 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain