【SLAM】VINS-MONO解析——初始化(代码部分)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

6.2 代码解析

在这里插入图片描述这部分代码在estimator::processImage()最后面。初始化部分的代码虽然生命周期比较短,但是,代码量巨大!主要分成2部分,第一部分是纯视觉SfM优化滑窗内的位姿,然后在融合IMU信息,按照理论部分优化各个状态量。

    if (solver_flag == INITIAL)//进行初始化
    {
   
   
        if (frame_count == WINDOW_SIZE)
        {
   
   
            bool result = false;
            if( ESTIMATE_EXTRINSIC != 2 && (header.stamp.toSec() - initial_timestamp) > 0.1)
            {
   
   //确保有足够的frame参与初始化,有外参,且当前帧时间戳大于初始化时间戳+0.1秒
               result = initialStructure();//执行视觉惯性联合初始化
               initial_timestamp = header.stamp.toSec();//更新初始化时间戳
            }
            if(result)//初始化成功则进行一次非线性优化
            {
   
   
                solver_flag = NON_LINEAR;//进行非线性优化
                solveOdometry();//执行非线性优化具体函数solveOdometry()
                slideWindow();
                f_manager.removeFailures();
                ROS_INFO("Initialization finish!");
                last_R = Rs[WINDOW_SIZE];//得到当前帧与第一帧的位姿
                last_P = Ps[WINDOW_SIZE];
                last_R0 = Rs[0];
                last_P0 = Ps[0];                
            }
            else
                slideWindow();//不成功则进行滑窗操作
        }
        else
       //TODO 再看看这个值是怎么变的
            ;//图像帧数量+1
    }



6.3 initialStructure()

这是一个相当大的函数,而且多层套娃。而且原理上讲的初始化,包括纯视觉SfM,视觉和IMU的松耦合,都是在这个部分里面。
在这里插入图片描述

6.3.1 确保IMU有足够的excitation(可选)

这一部分的思想就是通过计算滑窗内所有帧的线加速度的标准差,判断IMU是否有充分运动激励,以判断是否进行初始化。

一上来就出现all_image_frame这个数据结构,见5.2-2,它包含了滑窗内所有帧的视觉和IMU信息,它是一个hash,以时间戳为索引。

(1)第一次循环,求出滑窗内的平均线加速度

Vector3d sum_g;
for (frame_it = all_image_frame.begin(), frame_it++; frame_it != all_image_frame.end(); frame_it++)
{
   
   
    double dt = frame_it->second.pre_integration->sum_dt;//time for bk to bk+1
    Vector3d tmp_g = frame_it->second.pre_integration->delta_v / dt;
    sum_g += tmp_g;
}
Vector3d aver_g;
aver_g = sum_g * 1.0 / ((int)all_image_frame.size() - 1);


(2)第二次循环,求出滑窗内的线加速度的标准差

double var = 0;
for (frame_it = all_image_frame.begin(), frame_it++; frame_it != all_image_frame.end(); frame_it++)
{
   
   
    double dt = frame_it->second.pre_integration->sum_dt;
    Vector3d tmp_g = frame_it->second.pre_integration->delta_v / dt;
    var += (tmp_g - aver_g).transpose() * (tmp_g - aver_g);//计算加速度的方差
    //cout << "frame g " << tmp_g.transpose() << endl;
}
var = sqrt(var / ((int)all_image_frame.size() - 1));//计算加速度的标准差
//ROS_WARN("IMU variation %f!", var);
if(var < 0.25)
{
   
   
    ROS_INFO("IMU excitation not enouth!");
    //return false;
}
   


6.3.2 将f_manager中的所有feature在所有帧的归一化坐标保存到vector sfm_f中(辅助)

(1)一上来就定义了几个容器,分别是:

Quaterniond Q[frame_count + 1];
Vector3d T[frame_count + 1];
map<int, Vector3d> sfm_tracked_points;
vector<SFMFeature> sfm_f;

这块有2个需要注意的地方,
就是为什么容量是frame_count + 1?因为滑窗的容量是10,再加上当前最新帧,所以需要储存11帧的值!
然后出现了一个新的数据结构,我们看一下:

数据结构: vector<SFMFeature> sfm_f
它定义在initial/initial_sfm.h中,
    struct SFMFeature
{
   
   
    bool state;//状态(是否被三角化)
    int id;
    vector<pair<int,Vector2d>> observation;//所有观测到该特征点的 图像帧ID 和 特征点在这个图像帧的归一化坐标
    double position[3];//在帧l下的空间坐标
    double depth;//深度
};   


可以发现,它存放着一个特征点的所有信息。容器定义完了,接下来就是往容器里放数据。

(2) 往容器里放数据

for (auto &it_per_id : f_manager.feature)//对于滑窗中出现的 所有特征点
{
   
   
    int imu_j = it_per_id.start_frame - 1;//从start_frame开始帧编号
    SFMFeature tmp_feature;
    tmp_feature.state = false;//状态(是否被三角化)
    tmp_feature.id = it_per_id.feature_id;//特征点id
    for (auto &it_per_frame : it_per_id.feature_per_frame)//对于当前特征点 在每一帧的坐标
    {
   
   
        imu_j++;//帧编号+1
        Vector3d pts_j = it_per_frame.point;//当前特征在编号为imu_j++的帧的归一化坐标
        tmp_feature.observation.push_back(make_pair(imu_j, Eigen::Vector2d{
   
   pts_j.x(), pts_j.y()}));//把当前特征在当前帧的坐标和当前帧的编号配上对
    }//tmp_feature.observation里面存放着的一直是同一个特征,每一个pair是这个特征在 不同帧号 中的 归一化坐标
    sfm_f.push_back(tmp_feature);//sfm_f里面存放着是不同特征
} 

在这里,为什么要多此一举构造一个sfm_f而不是直接使用f_manager呢?
我的理解,是因为f_manager的信息量大于SfM所需的信息量(f_manager包含了大量的像素信息),而且不同的数据结构是为了不同的业务服务的,所以在这里作者专门为SfM设计了一个全新的数据结构sfm_f,专业化服务。

6.3.3 在滑窗(0-9)中找到第一个满足要求的帧(第l帧),它与最新一帧(frame_count=10)有足够的共视点和平行度,并求出这两帧之间的相对位置变化关系
(1)定义容器

Matrix3d relative_R; 
Vector3d relative_T;
int l; //滑窗中满足与最新帧视差关系的那一帧的帧号

(2)两帧之间的视差判断,并得到两帧之间的相对位姿变化关系

if (!relativePose(relative_R, relative_T, l))
{
   
   //这里的第L帧是从第一帧开始到滑动窗口中第一个满足与当前帧的平均视差足够大的帧l,会作为 参考帧 到下面的全局sfm使用,得到的Rt为当前帧到第l帧的坐标系变换Rt
    ROS_INFO("Not enough features or parallax; Move device around");
    return false;
}

这里,又出现了个新的函数relativePose(),这个函数也是6.3.3的主要功能,进去看一下:

首先,搞清楚这个函数是要干什么事情?
a.计算滑窗内的每一帧(0-9)与最新一帧(10)之间的视差,直到找出第一个满足要求的帧,作为我们的第l帧;

 Estimator::relativePose(Matrix3d &relative_R, Vector3d &relative_T, int &l)
{
   
                                      //output array R,t
    // 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);//寻找第i帧到窗口最后一帧(当前帧)的对应特征点归一化坐标
        if (corres.size() > 20)                             //归一化坐标point(x,y,不需要z)
        {
   
   
            double sum_parallax = 0;
            double average_parallax;//计算平均视差
            for (int j = 0; j < int(corres.size()); j++)
            {
   
   //第j个对应点在第i帧和最后一帧的(x,y)
                Vector2d pts_0(corres[j].first(0), corres[j].first(1));//改成3,4呢(对应像素坐标,1-3是归一化xyz坐标)
                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


getCorresponding()的作用就是找到当前帧与最新一帧所有特征点在对应2帧下分别的归一化坐标,并配对,以供求出相对位姿时使用。

b.计算l帧与最新一帧的相对位姿关系

            if(average_parallax * 460 > 30 && m_estimator.solveRelativeRT(corres, relative_R, relative_T))
            {
   
    //solveRelativeRT()通过基础矩阵计算当前帧与第l帧之间的R和T,并判断内点数目是否足够
                l = i; //同时返回窗口最后一帧(当前帧)到第l帧(参考帧)的relative_R,relative_T
                ROS_DEBUG("average_parallax %f choose l %d and newest frame to triangulate the whole structure", average_parallax * 460, l);
                return true;
//一旦这一帧与当前帧视差足够大了,那么就不再继续找下去了(再找只会和当前帧的视差越来越小)
            }
            

这里最核心的公式就是m_estimator.solveRelativeRT(),这部分非常地关键。这里面代码很简单,就是把对应的点传进入,然后套cv的公式,但是求出来的R和T是谁转向谁的比较容易迷糊。
根据对以前学习内容和回忆和对后面公式的阅读,这个relative_R和relative_T是把最新一帧旋转到第l帧的旋转平移!

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;            //因为这里的ll,rr是归一化坐标,所以得到的是本质矩阵
        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
SLAMVINS-MONO解析——vins_estimator流程 5.vins_estimator 基本上VINS里面绝大部分功能都在这个package下面,包括IMU数据的处理(前端)初始化(我觉得可能属于是前端),滑动窗口(后端),非线性优化(后端),关键帧的选取(部分内容)(前端)。我第一次看的时候,总是抱有一个疑问,就是为什么把这么多内容全都放在这一个node里面。为了回答这个问题,那么首先先搞清楚vins_estimator里面分别具体都是什么,为什... 阅读详情

相关推荐

求助!!!vins-mono实时运行及录制包,报错Not enough features or parallaxMove device around(已解决)

进行标定,将imu信息、相机信息、平移旋转矩阵修改之后,运行报错“Not enough features or parallax

qq_53229439的博客 1674

VIO:VINS-FUSION解毒填坑(一)

函数用于在所有帧中找到一个相对位姿较好的帧对,以此作为SFM初始化的基础时,函数返回 false,则说明。vins-fusion原理、代码和跑通解毒。设置检查点,检查变量。

weixin_38582851的博客 294

VINS-Mono+Fusion源码解析系列(十三):纯视觉SFM

假设滑窗中一共有11帧,首先需要选取一个枢纽帧,利用枢纽帧和最后一帧通过求出这两帧之间的相对位姿。对枢纽帧的要求是:一方面要求枢纽帧离最后一帧尽可能远,因为离最后一帧比较近时,二者之间的平移比较小,使得三角化精度较差,甚至会无法进行对极约束(平移为零的情况)。另一方面,要求枢纽帧与最后一帧之间不能距离太远,否则会使得二者之间关联的特征点比较少,导致求解出的位姿可信度不高,并且无法三角化出尽可能多的点。因此,在保证匹配点足够多的情况下,枢纽帧离最后一帧应该尽可能远,如下图中选取第4帧作为枢纽帧。

LDST_CSDN的博客 992

运行VINS-Mono报错: IMU excitation not enouth!

问题描述 闲来无事,跑个VINS-Mono玩玩,按照github给的步骤,一步步进行,没有报错,编译完美结束。但是在运行的时候: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191031094510400.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aH...

weixin_42580307的博客 3727

男人8题之Tony's TourPKU1739基于连通性状态压缩的动态规划

http://acm.pku.edu.cn/JudgeOnline/problem?id=1739<span style="font-family:宋体;mso-ascii-font-family:Calibri;mso-ascii-theme-font:minor-latin;mso-fareast-font-family:宋体;mso-fareast-theme-font:minor-

陆科杰(C/C++) 6086

SLAMVINS-MONO解析——对vins-mono的一点小改动

vins-mono刷了三遍,手写vio刷了两遍,从一开始完全看不懂是啥,再到自己写总结写攻略,真的有很多感慨。学习开源代码的目的其实就是是为了自己能够去开发一个vio,一个slam系统。 slam最难的就是工程化了,公式很多人推导了一遍又一遍,道理大家都懂,但是转化为c++项目就是另一码事了。 vins系列真的是神作,对沈老师和对秦神真的是佩服。在这里我对vins-mono进行了一下小小的提升和修...

iwanderu的博客 7051

SLAMVINS-MONO解析——综述

目前网上有很多分析文章,但是都只是一些比较基础的原理分析,而且很多量,虽然有推倒,但是往往没有讲清楚这些量是什么,为什么要有这些量,这些量是从哪来的,也没有刷通整个代码,或者太简练了,对新手不友好。这一次我计划从原理/代码一步步刷通整个VINS,并且说清楚代码中遇到的每一个数据结构对应的作用是什么。 github官方上开源了VINS-monoVINS-Fusion和VINS-mobile,还有另...

iwanderu的博客 6万+

SLAMVINS-MONO解析——基于vins-mono的双目slam系统开发

这个系统是基于香港科技大学飞行机器人组的开源框架VINS-Mono开发的,原开源框架是针对单目SLAM。本双目SLAM系统是在原单目开源框架基础上的二次深度开发,外部接口与原框架一致。这个项目是我的研究课题项目,非商业用途,感谢HKUST的沈老师课题组提供的开源框架。该项目的github地址请点击这里。 1.需要的基本配置 Ubuntu 16.04 ROS Kinetic Ceres Solver...

iwanderu的博客 4331

SLAMVINS-MONO解析——初始化(理论部分)

6.初始化 第一个问题,为什么要初始化? 对于单目系统而言, (1)视觉系统只能获得二维信息,损失了一维信息(深度),所以需要动一下,也就是三角化才能重新获得损失的深度信息; (2)但是,这个三角化恢复的深度信息,是个“伪深度”,它的尺度是随机的,不是真实的,所以就需要IMU来标定这个尺度; (3)要想让IMU标定这个尺度,IMU也需要动一下,得到PVQ的P; (4)另外,IMU存在bias,视觉...

iwanderu的博客 2万+

港科大VINS-MONO入门(一):框架入门及源码解析

一、VINS介绍 VINS-Mono是HKUST的Shen Shaojie团队开源的一套Visual-Inertial融合定位算法. 介绍见(https://github.com/HKUST-Aerial-Robotics/VINS-Mono), 论文里面也有. 此外,他们还开源了ios版本(https://github.com/HKUST-Aerial-Robotics/VINS-Mobil...

wbzhang233的博客 2万+

VINS技术路线与代码详解

VINS技术路线 写在前面:本文整和自己的思路,希望对学习VINS或者VIO的同学有所帮助,如果你觉得文章写的对你的理解有一点帮助,可以推荐给周围的小伙伴们,当然,如果你有任何问题想要交流,欢迎随时探讨。话不多说,下面上正文。 VINS代码地址:https://github.com/HKUST-Aerial-Robotics/VINS-Mono 参考文档:1.VINS-Mono A R...

wangshuailpp的博客 7万+

多种视觉SLAM方案对比

SLAM研究中,我们通常需要在各数据集上测试各个方案的性能情况。如下主要对表1中6个视觉SLAM方案进行相关测试,测试其在不同场景、要求下的精度和鲁棒性,并给出最终方案选型。 【由于时间、场地等原因,多次引用大神们测试结果,谢谢前人的分享!】 ** 开源SLAM方案选型: ** ** 数据集: ** 一、ORB-SLAM2 检测流程:Kinect生成地图【地图主要可见的有关键帧(包括相机的p...

hly453的博客 2万+

SLAMVINS-MONO解析——前端

3. feature_tracker feature_tracker是vins的前端,它的目录在src/ feature_tracker下,功能主要是获取摄像头的图像帧,并按照事先设定的频率,把cur帧上满足要求的特征点以sensor_msg::PointCloudPtr的格式发布出去,以便RVIZ和vins——estimator接收。 这个package下面主要包括4个功能: feature_t...

iwanderu的博客 2万+

VINS-MONO概述

VINS-Mono是HKUST的Shen Shaojie团队开源的一套Visual-Inertial融合定位算法,https://github.com/HKUST-Aerial-Robotics/VINS-Mono,是用紧耦合方法实现的,通过单目+IMU恢复出尺度,效果很好,接下来对会该算法框架逐步解析,以便二次开发。 那么为什么要进行视觉与IMU的融合呢,自己总结的主要有以下几点: 视觉与IMU的融合可以借助IMU较高的采样频率,进而提高系统的输出频率。 视觉与IMU的融合可以提高视觉的鲁棒性,如视觉S

xiaojinger_123的博客 1万+

SLAMVINS-MONO解析——IMU预积分

4.IMU预积分 IMU预积分主要干了2件事,第一个是IMU预积分获得α、β、γ值,另一个是误差传递函数的获取。本部分的流程图如下图所示。 4.1 IMU预积分 4.1.1 连续形式 第一个问题,为什么要IMU积分? IMU获取的是加速度和角速度,通过对IMU测量量的积分操作,能够获得机器人的位姿信息。 IMU测量值包括加速度计得到的线加速度和陀螺仪得到的角速度。 其中 t下标表示在IMU坐标...

iwanderu的博客 2万+

SLAMVINS-MONO解析——回环检测和重定位

部分内容涉及到的代码部分在pose_graph文件夹下,少部分vins_estimator里。这部分最主要的作用就是求出漂移位姿矫正矩阵r_drift和t_drift。这么大一个工程,这么多行代码,保存这么多帧特征点和描述子,就是为了找出这6个自由度的变量。

iwanderu的博客 1万+

超详细:VINS-Mono论文中文记录

VINS-Mono论文笔记题目0. 摘要1. 背景简介2.相关工作介绍2.1 融合方式2.2 摄像头数据处理2.3 imu数据处理2.4 初始化2.5里程计3. VINS-Mono系统总览4 视觉和IMU测量的预处理步骤4.1 视觉部分4.2 imu部分4.3 偏置纠正5 鲁棒的初始化过程5.1 视觉重构5.2 视觉惯性联合6 紧耦合的单目VIO系统6.1 公式6.2 imu残差6.3 视觉残差6.4 边缘化残差6.5 针对相机实时帧率的纯运动视觉惯性状态估计器6.6 imu向前传播以得到imu频率的状态估

hu_hao的博客 1万+

VINS-Mono论文学习与代码解读——目录与参考

港科大的VINS-Mono作为目前state of the art的开源VIO项目,是研究视觉与IMU融合的必读算法。本人将在学习之余逐步整理自己对算法的论文和实现的相关解读,欢迎大家一起讨论。

Manii 5万+

VINS-MonoORB-SLAM2代码阅读笔记汇总

VINS-Mono代码是港科大沈老师组开放出来的非常优秀的开源SLAM项目,融合了视觉和IMU,是SLAM学习研究中一个很好的项目。这里列出本人学习该项目代码过程中的笔记,后续有新的理解会不断更新。代码学习过程中也借鉴参考了许多网上的其他资料来帮助自己理解,非常感谢有这么多人分享自己的学习经验。 VINS-Mono代码阅读笔记(一):从Euroc数据集的运行开始 VINS-Mono代码...

6380
上一篇: 【SLAM】VINS-MONO解析——初始化(理论部分)
下一篇: 【SLAM】VINS-MONO解析——后端优化(理论部分)
iwander。
博客等级 码龄7年 1430粉丝 67原创
评论 23
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值