VINS --- 初始化 视觉SFM 部分

brief

把多年前个人网站的资料迁移一下,满满的回忆
在这里插入图片描述

(初始化过程中 )光流跟踪(2d-2d点对的匹配关系) -> 对极约束(分解本质矩阵得到两帧之间的RT)-> 再进行 三角化,得到3d坐标 ->进行PnP求解其他帧的位姿->然后利用新帧的位姿又可以进行三角化,再进行PnP

(初始化后的视觉跟踪,与IMU融合过程 ) 光流跟踪(2d-2d点对的匹配关系) -> 再进行 三角化,得到3d坐标 -> 然后使用IMU的积分位姿作为初始值,进行非线性优化求解(Ceres)

(对于没有使用IMU的 tracking ,如双目视觉里程计) 光流跟踪 ->三角化 -> 使用 PnP 构建本质矩阵分解得到初始位姿或者使用重投影方式求解位姿,作为初始值 -> 最后再是基于滑动窗口的位姿优化。

1. 选择参考帧,并计算出当前帧与参考帧之间的位姿

要求这一帧和最后一帧既有足够的共视 > 20对点,也要有足够的视差 (平均视差大于 average_parallax * 460 > 30)
relativePose(relative_R, relative_T, l)

/**
 * @brief 寻找滑窗内一个帧作为枢纽帧,要求和最后一帧既有足够的共视也要有足够的视差
 *        这样其他帧都对齐到这个枢纽帧上
 *        得到T_l_last
 * @param[in] relative_R 
 * @param[in] relative_T 
 * @param[in] l 
 * @return true 
 * @return false 
 */

bool Estimator::relativePose(Matrix3d &relative_R, Vector3d &relative_T, int &l)
{
    // find previous frame which contians enough correspondance and parallex with newest frame
    // 优先从最前面开始
    for (int i = 0; i < WINDOW_SIZE; i++)
    {
        vector<pair<Vector3d, Vector3d>> corres;
        corres = f_manager.getCorresponding(i, WINDOW_SIZE);
        // 要求共视的特征点足够多
        if (corres.size() > 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());
            // 有足够的视差在通过本质矩阵恢复第i帧和最后一帧之间的 R t T_i_last
            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;
}

findFundamentalMat 得到基础矩阵,再分别得到位姿 (R和带尺度的t)


/**
 * @brief 根据两帧匹配对求解R和带尺度的t
 * 
 * @param[in] corres 
 * @param[in] Rotation 
 * @param[in] Translation 
 * @return true 
 * @return false 
 */

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;
        // 调用opencv接口求解E矩阵
        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;
        // 用opencv接口恢复R和t
        int inlier_cnt = cv::recoverPose(E, ll, rr, cameraMatrix, rot, trans, mask);
        //cout << "inlier_cnt " << inlier_cnt << endl;

        Eigen::Matrix3d R;
        Eigen::Vector3d T;
        // cv -> eigen
        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);
        }
        // opencv得到的是T21,这里换成T12
        Rotation = R.transpose();
        Translation = -R.transpose() * T;
        if(inlier_cnt > 12)
            return true;
        else
            return false;
    }
    return false;
}

2.进行sfm的求解所有帧的位姿与3d坐标点

根据已有的枢纽帧/参考帧和最后一帧的位姿变换,得到各帧位姿和3d点坐标,最后通过ceres进行优化

  • 枢纽帧/参考帧设置为单位帧,也可以理解为世界系原点
q[l].w() = 1;
q[l].x() = 0;
q[l].y() = 0;
q[l].z() = 0;
T[l].setZero();

// 求得最后一帧的位姿
q[frame_num - 1] = q[l] * Quaterniond(relative_R);
T[frame_num - 1] = relative_T;
  • 使用 PnP 与 三角化 窗口中其他关键帧与特征点坐标 ,都表示为 Tcw
    得到了所有关键帧的位姿,遍历没有被三角化的特征点,进行三角化
  • ceres BA 求解
    【固定参考帧位姿(世界坐标系原点),固定最后一帧的(当前帧)的平移,也就是固定这个尺度信息,这两帧之间只优化旋转量】
// 由于是单目视觉slam,有七个自由度不可观,因此,fix一些参数块避免在零空间漂移
// fix设置的世界坐标系第l帧的位姿,同时fix最后一帧的位移用来fix尺度
if (i == l)
{
    problem.SetParameterBlockConstant(c_rotation[i]);
}
if (i == l || i == frame_num - 1)
{
    problem.SetParameterBlockConstant(c_translation[i]);
}
  • 最后求解所有的帧,除关键帧外的其他帧 solvePnP()求解

优化之后 : 由于尺度未恢复,因此平移暂时不转到imu系 (得到尺度后再更新)

 // Twc -> Twi
        // 由于尺度未恢复,因此平移暂时不转到imu系
        frame_it->second.R = R_pnp * RIC[0].transpose();
        frame_it->second.T = T_pnp;

3. 使用所有关键帧的位姿,即可以与IMU进行对齐

再求解 bg ,g ,s ,V[10]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值