Vins-Fusion之 SFM准备篇(十二)

SFM 初始化需要一对有足够运动的帧作为起点,这个帧要满足:

a.有足够的匹配特征点(>20 个)

b.有足够的视差(parallax > 30 像素)

c.能成功估计相对位姿

所以就引入一个SFM准备篇,来解决起点的问题,这就是本篇的目的。

首先核心代码如下:

cpp 复制代码
bool Estimator::relativePose(Matrix3d &relative_R, Vector3d &relative_T, int &l)
{
    for (int i = 0; i < WINDOW_SIZE; i++)
    {
        vector<pair<Vector3d, Vector3d>> corres;
        corres = f_manager.getCorresponding(i, WINDOW_SIZE);//每一帧和最新帧的匹配点对
        if (corres.size() > 20)//匹配点超过 20 个才继续
        {
            double sum_parallax = 0;
            double average_parallax;
            for (int j = 0; j < int(corres.size()); j++)
            {
                Vector2d pts_0(corres[j].first(0), corres[j].first(1));
                Vector2d pts_1(corres[j].second(0), corres[j].second(1));
                double parallax = (pts_0 - pts_1).norm();//计算两个坐标欧氏距离
                sum_parallax = sum_parallax + parallax;
            }
            average_parallax = 1.0 * sum_parallax / int(corres.size());//归一化坐标下的平均视差
            //像素视差 > 30 像素表示两帧间运动足够大,有利于三角化和位姿估计
            if(average_parallax * 460 > 30 && m_estimator.solveRelativeRT(corres, relative_R, relative_T))
            {
                l = i;
                ROS_DEBUG("average_parallax %f choose l %d and newest frame to triangulate the whole structure", average_parallax * 460, l);
                return true;//匹配到满足条件返回
            }
        }
    }
    return false;
}

关键步骤说明:

1.遍历候选帧:从第 0 帧到 WINDOW_SIZE-1,依次检查与最新帧的匹配情况

2.匹配点数量检查:corres.size() > 20

3.视差计算:

  • 归一化坐标下的视差:parallax = (pts_0 - pts_1).norm()
  • 转换为像素视差:average_parallax * 460(460 是 FOCAL_LENGTH)
  • 阈值:> 30 像素

4.相对位姿估计:调用 solveRelativeRT 估计相对旋转和平移

5.找到即返回:找到第一个满足条件的帧对就返回,不再继续

solveRelativeRT :得到最新帧相对参考帧的旋转矩阵、平移向量

cpp 复制代码
bool MotionEstimator::solveRelativeRT(const vector<pair<Vector3d, Vector3d>> &corres, Matrix3d &Rotation, Vector3d &Translation)
{
    if (corres.size() >= 15)
    {
        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 mask;
        cv::Mat E = cv::findFundamentalMat(ll, rr, cv::FM_RANSAC, 0.3 / 460, 0.99, mask);//本质矩阵
        cv::Mat cameraMatrix = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
        cv::Mat rot, trans;
        int inlier_cnt = cv::recoverPose(E, ll, rr, cameraMatrix, rot, trans, mask);//恢复两帧之间的相对旋转和平移
        //cout << "inlier_cnt " << inlier_cnt << endl;
        Eigen::Matrix3d R;
        Eigen::Vector3d T;
        for (int i = 0; i < 3; i++)
        {   
            T(i) = trans.at<double>(i, 0);
            for (int j = 0; j < 3; j++)
                R(i, j) = rot.at<double>(i, j);
        }

        Rotation = R.transpose();
        Translation = -R.transpose() * T;//想统一用「从较新帧到参考帧」的变换
        if(inlier_cnt > 12)
            return true;
        else
            return false;
    }
    return false;
}

要点:估计基础矩阵E后,再解算出R和T,转换到最新帧相对参考帧的Rotation和Translation,为后续的SFM初始化提供基础数据,这就是本篇的目的。

相关推荐
暗夜猎手-大魔王3 分钟前
转载--AI Agent 架构设计:Agent 的自我欺骗(OpenClaw、Claude Code、Hermes Agent 对比)
人工智能
智枢圈4 分钟前
Function-Calling与工具使用
人工智能
司南-70495 分钟前
Dense结构下的 大模型系统架构研究
服务器·人工智能·后端
GISer_Jing5 分钟前
AI全栈转型_TS后端学习路线
前端·人工智能·后端·学习
叼烟扛炮5 分钟前
C++第四讲:类和对象(下)
c++·算法·类和对象
漫游的渔夫5 分钟前
前端开发者做 Agent:别只会执行,用 4 类失败策略让 AI 知道怎么停
前端·人工智能·typescript
Rabitebla6 分钟前
vector 的骨架:三根指针、模板陷阱与迭代器失效的第一现场
开发语言·数据结构·c++·算法
娃娃略6 分钟前
Frame
人工智能·深度学习·机器学习
Python私教11 分钟前
Pure-Admin-Thin 深度解析:完整版和精简版到底怎么选?
vue.js·人工智能·开源
星马梦缘14 分钟前
强化学习实战8.3——用PPO打赢星际争霸【编写自定义环境GYM】
人工智能·强化学习·gymnasium·星际争霸·sc2·starcraft2·sb3