VINS-Mono关键知识点总结——前端详解

2023-05-16

VINS-Mono关键知识点总结——前端详解

  • VINS-Mono关键知识点总结——前端详解
    • 1. VINS-Mono的前端流程概述
    • 2. setMask() 函数的作用
    • 3. rejectWithF() 函数的作用
    • 4. addPoints() 函数和 updataID() 函数的作用

VINS-Mono关键知识点总结——前端详解

VINS-Mono的前端看上去思路是比较简单的,但是如果仔细阅读源码的话还是能看出许多非常有技术性的东西的,下面我就把一些我觉得很有意思的细节码出来,分享一下

1. VINS-Mono的前端流程概述

VINS-Mono的前端整个封装成了一个ROS节点
其订阅的topic是:

  1. 相机或者数据集发来的图片

其发布topic是:

  1. 由pub_img发布的"feature",发布的是当前帧的特征点,特征点分装成了sensor_msgs::PointCloudPtr类型,里面包括了每个特征点的(1)归一化平面上的坐标(2)归一化平面上的速度(3)图像平面上的坐标,这三者中归一化平面坐标是在SFM和后端用到,而归一化平面上的速度以及图像平面上的坐标仅仅在进行时间偏差估计时会用到。
  2. 由pub_match发布的“feature_img”,发布的是用来调试的带特征点的灰度图,图上特征点的颜色越红代表此特征点被跟踪的帧数越多,越蓝表示越少。

在图片的回调函数中会先对时间进行控制,然后会进入 readImage() 函数 进行光流追踪、特征点提取、特征点提取等步骤


2. setMask() 函数的作用

setMask() 函数主要是和特征点提取有关的,当跟踪的特征点数量没有达到设定的值时就会对图片进行特征点提取,提取采取的算法是OpenCV的Harris角点提取函数

cv::goodFeaturesToTrack(forw_img, n_pts, MAX_CNT - forw_pts.size(), 0.01, MIN_DIST, mask);

函数的最后一个参数mask是是设置角点提取的区域,实际上是和输入forw_img尺寸相同的一个Mat数据类型,而这个mask就在这里就是通过setMask()函数生成的,它的作用是使得提取的特征点分布尽可能均匀,setMask() 函数源码如下:

void FeatureTracker::setMask()
{
    if(FISHEYE)
        mask = fisheye_mask.clone();
    else
        mask = cv::Mat(ROW, COL, CV_8UC1, cv::Scalar(255));
    
    //算法会更加倾向于保留跟踪时间长的特征点
    vector<pair<int, pair<cv::Point2f, int>>> cnt_pts_id;

    for (unsigned int i = 0; i < forw_pts.size(); i++)
        cnt_pts_id.push_back(make_pair(track_cnt[i], make_pair(forw_pts[i], ids[i])));

    //对光流跟踪到的特征点forw_pts,按照被跟踪到的次数cnt从大到小排序
    sort(cnt_pts_id.begin(), cnt_pts_id.end(), [](const pair<int, pair<cv::Point2f, int>> &a, const pair<int, pair<cv::Point2f, int>> &b)
    {
       return a.first > b.first;
    });

    //清空cnt,pts,id并重新存入
    forw_pts.clear();
    ids.clear();
    track_cnt.clear();

    for (auto &it : cnt_pts_id)
    {
        if (mask.at<uchar>(it.second.first) == 255)
        {
            //当前特征点位置对应的mask值为255,则保留当前特征点,将对应的特征点位置pts,id,被追踪次数cnt分别存入
            forw_pts.push_back(it.second.first);
            ids.push_back(it.second.second);
            track_cnt.push_back(it.first);

            //在mask中将当前特征点周围半径为MIN_DIST的区域设置为0,后面不再选取该区域内的点(使跟踪点不集中在一个区域上)
            cv::circle(mask, it.second.first, MIN_DIST, 0, -1);
        }
    }
}

其大概流程是会根据光流法跟踪下一帧图片forw_img获得的forw_pts的被跟踪的时间进行排序,然后按照被跟踪时间从长到段的顺序以半径MIN_DIST在一张白图上画黑圈,如果画出来的mask大概长下面这个样子(有点密集恐惧症了…):
在这里插入图片描述
将这个mask图输入到goodFeaturesToTrack函数中,函数就只会在白色区域,也就是没有跟踪时长特别长的地区提取特征点,因此会使得我们提取的关键点相对较为均匀,如下图所示:

在这里插入图片描述
如果你将setMask函数中排序的部分注释掉,特征点的分布就会变成如下图所示结果:
在这里插入图片描述
我个人觉得上面这种做法应该是有利有弊的,对于纹理特征丰富的环境,这种操作使得特征点分布更加均匀是有利于系统鲁棒性的,在ORB SLAM2和SVO等经典框架中,前端都有相应的均匀分布的操作,但是在上面图片中这种纹理特征稀疏的环境下,这种做法就不一定好用了,因为强行使得特征点分布到一些本来就没什么特征的区域,这样可能(因为这里我还没有做实验验证)会使得系统精度下降。


3. rejectWithF() 函数的作用

rejectWithF() 函数的源码如下,其实比较简单:

void FeatureTracker::rejectWithF()
{
    if (forw_pts.size() >= 8)
    {
        ROS_DEBUG("FM ransac begins");//才哟ing的是ransac的方法
        TicToc t_f;

        vector<cv::Point2f> un_cur_pts(cur_pts.size()), un_forw_pts(forw_pts.size());
        for (unsigned int i = 0; i < cur_pts.size(); i++)
        {

            Eigen::Vector3d tmp_p;
            //根据不同的相机模型将二维坐标转换到三维坐标
            m_camera->liftProjective(Eigen::Vector2d(cur_pts[i].x, cur_pts[i].y), tmp_p);
            //转换为归一化像素坐标
            tmp_p.x() = FOCAL_LENGTH * tmp_p.x() / tmp_p.z() + COL / 2.0;
            tmp_p.y() = FOCAL_LENGTH * tmp_p.y() / tmp_p.z() + ROW / 2.0;
            un_cur_pts[i] = cv::Point2f(tmp_p.x(), tmp_p.y());

            m_camera->liftProjective(Eigen::Vector2d(forw_pts[i].x, forw_pts[i].y), tmp_p);
            tmp_p.x() = FOCAL_LENGTH * tmp_p.x() / tmp_p.z() + COL / 2.0;
            tmp_p.y() = FOCAL_LENGTH * tmp_p.y() / tmp_p.z() + ROW / 2.0;
            un_forw_pts[i] = cv::Point2f(tmp_p.x(), tmp_p.y());
        }

        vector<uchar> status;
        //调用cv::findFundamentalMat对un_cur_pts和un_forw_pts计算F矩阵
        cv::findFundamentalMat(un_cur_pts, un_forw_pts, cv::FM_RANSAC, F_THRESHOLD, 0.99, status);
        int size_a = cur_pts.size();
        reduceVector(prev_pts, status);
        reduceVector(cur_pts, status);
        reduceVector(forw_pts, status);
        reduceVector(cur_un_pts, status);
        reduceVector(ids, status);
        reduceVector(track_cnt, status);
        ROS_DEBUG("FM ransac: %d -> %lu: %f", size_a, forw_pts.size(), 1.0 * forw_pts.size() / size_a);
        ROS_DEBUG("FM ransac costs: %fms", t_f.toc());
    }
}

这里主要是操作是将图像平面上的特征点,投影到归一化平面上然后消除畸变,这一步是通过liftProjective函数实现的,cameral_model功能包通过工厂模式生成了多种相机模型,不同的相机模型的liftProjective函数里面算法不同,但是好像这些相机模型对应的畸变消除算法和《视觉SLAM十四讲》中讲的最基本的那种 k 2 , k 4 , k 6 , p 1 , p 2 k_2, k_4, k_6, p_1,p_2 k2,k4,k6,p1,p2五个参数的都不同,具体的相机模型我还没有时间去研究,这里我还有两个问题:

  1. 为什么FOCAL_LENGTH在程序中设置成了460这个数值?
  2. 为什么要将特征带点转化到归一化平面上的像素坐标?

排除这两个问题之外,其他其实也就很简单了,通过findFundamentalMat函数自带的ransac消除外点的功能在光流法之后以及提取新的特征点之前消除光流法更踪错误的点,维持系统精度。


4. addPoints() 函数和 updataID() 函数的作用

addPoints() 函数代码如下:

void FeatureTracker::addPoints()
{
    for (auto &p : n_pts)
    {
        forw_pts.push_back(p);
        ids.push_back(-1);//新提取的特征点id初始化为-1
        track_cnt.push_back(1);//新提取的特征点被跟踪的次数初始化为1
    }
}

updataID() 函数代码如下:

bool FeatureTracker::updateID(unsigned int i)
{
    if (i < ids.size())
    {
        if (ids[i] == -1)
            ids[i] = n_id++;
        return true;
    }
    else
        return false;
}

这两个函数都很短啊,addPoints() 函数主要是将goodFeaturesToTrack() 函数中提取的新的特征点n_pts加入到下一帧的关键点forw_pts中,注意这里将新添加的特征点的id都设为了-1, 而updataID() 函数在这之后将id从n_id开始赋值,而n_id是一个全局变量,从第一个特征点为1开始来一个特征点加一,因此是一个一直增长且不重复的整数,而我们特征点对应的id如果答应出来可以发现是一个乱序且不连续的数列,为什么呢?这可以结合到我们上面讲的setMask() 函数和 rejectWithF() 函数,因为他们分别对这个数列进行了排序和删除~

VINS的前端其实很简单的,其精华主要是在后端的优化的边缘化,前端就总结到这里啦,欢迎指正。

此外,对其他SLAM算法感兴趣的同学可以看考我的博客SLAM算法总结——经典SLAM算法框架总结

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VINS-Mono关键知识点总结——前端详解 的相关文章

  • Vmware安装Ubuntu16.0.4出现Assuming drive cache: write through和1920X1080分辨率调节问题

    Vmware安装Ubuntu16 0 4出现Assuming drive cache write through和1920X1080分辨率调节问题 注 xff1a 用Vmare安装Ubuntu16几乎都会遇到下面两个问题 xff0c 跟着我
  • ubuntu QT出现qt creater能编译但qmake编译后不能完全运行

    注释 xff1a 最开始用QT 编译程序 xff0c 但是最后需要将用命令行来编译 xff0c 在使用qmake过程中出现编译能通过但是运行程序也能跑起来 xff0c 但是就感觉卡在哪里了 xff0c 每次需要在QT编译器中重新编译才能运行
  • VScode 代码行数统计

    VScode 代码行数统计 1 安装vs code counter 2 按住按Ctrl 43 Shift 43 P 在命令输入 xff1a Count lines in workspace并回车 3 自动生成 VSCodeCounter x
  • 个人主页(包含项目经历、实习经历、竞赛经历)

    个人主页 注 xff1a 个人主页中主要包含项目经历 实习经历 竞赛经历等方面的照片及文字说明 项目经历 一 基于声源目标识别与定位的微型平台设计 图1 图2 图3 图1 3声源目标识别与定位平台 二 基于STM32的六足仿生机器人设计 图
  • UBUNTU编译运行RTKLIB

    RTKLIB在ubuntu下没有图形用户界面 xff08 GUI xff09 xff01 RTKLIB作为一个功能强大的工具 xff0c 我一直以为它在ubuntu下和win10下是一个样子 xff0c 所以想着用ubuntu下的编译器去编
  • 关于makefile与CMakeList的理解

    为了编译某一工程中的众多 c文件搞出来一个makefile可以批量编译 xff1b Linux下用Cmake也即CMakeLists txt文件 作者写的很清晰易懂
  • c++版本详表

    1 语言标准 版本代目版本号年份第一版c 43 43 981998第二版c 43 43 032003第三版c 43 43 112011第四版c 43 43 142014第五版c 43 43 172017第六版待定待定 2 visual st
  • ffmpeg常用命令

    1 rtsp into mp4 ffmpeg exe i 34 rtsp username password 64 192 168 1 183 554 34 f mp4 r 25 an 640x480 ss 600 t 600 s 34 t
  • http包详解

    位于应用层的http协议 什么也不多说了 xff0c 马上放个图看看 请求方法 xff0c 主要是 xff1a get xff1a 获取 post xff1a 提交 请求头 xff1a User Agent xff1a 产生请求的浏览器类型
  • linux下编译C++项目

    include CheckCXXCompilerFlag CHECK CXX COMPILER FLAG std 61 c 43 43 11 COMPILER SUPPORTS CXX11 CHECK CXX COMPILER FLAG s
  • www总结

    一 www服务器 1 什么是www xff1a www是world wide web的缩写 xff0c 也就是全球信息广播的意思 通常说的上网就是使用www来查询用户所需要的信息 www可以结合文字 图形 影像以及声音等多媒体 xff0c
  • matlab基础练习作业(1)

    要期末考试了 xff0c 整理一下平时的题 作业 xff11 1 写出完成下列操作的命令 1 将矩阵A第2 5行中第1 3 5列元素赋给矩阵B B 61 A 2 5 1 3 5 或者B 61 A 2 3 4 5 1 3 5 2 删除矩阵A的
  • PHP中使用cURL实现进行Post请求 ssl 双向认证

    span class token comment 64 param string url 接收数据的api 64 param string vars 提交的数据 64 param int second 要求程序必须在 second 秒内完成
  • 大端字节序与小端字节序 (网络字节序与主机字节序)

    大端字节序与小端字节序 1 简介2 判断机器字节序 xff08 C C 43 43 xff09 3 主机字节序与网络字节序的转换4 IP地址与网络字节序的转换 1 简介 字节序分为大端字节序 xff08 big endian xff09 和
  • Noetic安装ros_canopen

    建立workspace mkdir span class token operator span p canopen span class token operator span scr cd canopen catkin make 下载n
  • ROS学习初始化ws下catkin_make必须有src生成devel和build

    安装虚拟机开启intel vt 创建工作空间 first ws src catkin make在ws下生成devel和build 创建package 在src下添加package catkin create pkg hello ros ro
  • Pyhon : 爬虫Requests高级用法--证书

    客户端证书 你也可以指定一个本地证书用作客户端证书 xff0c 可以是单个文件 xff08 包含密钥和证书 xff09 或一个包含两个文件路径的元组 xff1a requests get https kennethreitz org cer
  • 【px4】运行mavsdk中的offboard example

    运行MAVSDK中的offboard例子时无人机不执行 想控制无人机前后左右移动 xff0c 在按照官方教程实现offboard 插件的时候 发现用action插件能正常起飞和降落 但是一旦执行到offboard的插件代码的时候就会自动降落
  • MDF does not reside in a read-only database or filegroup

    错误信息 The file 34 MDF 34 is compressed but does not reside in a read only database or filegroup The file must be decompre
  • Python复习笔记4——文件和目录操作

    一 文件读写 1 1 读文件 xff08 1 xff09 打开文件 使用Python内置的open 函数 xff0c 传入文件名和标示符 xff0c 打开一个文件对象 xff1a span class token operator gt g

随机推荐

  • Python复习笔记5——常用模块

    一 内建模块 1 1 datetime日期时间 xff08 1 xff09 导入模块 span class token keyword from span datetime span class token keyword import s
  • COLMAP+OpenMVS实现物体三维重建mesh模型

    一 环境准备 1 COLMAP 进入COLMAP官网 xff0c 打开下载页面 xff0c 下载COLMAP程序 2 OpenMVS OpenMVS可以自己从源码下载进行编译 如果觉得编译openmvs可能要费点时间 xff0c 那也可以直
  • 解决ubuntu火狐无法上网问题

    之前安装Ubuntu时是可以上网的 xff0c 呆了一个月没怎么用突然不能搜索了 xff0c 记录一下 xff0c 已解决 只需一条命令 打开终端 xff0c 输入 sudo apt get install firefox 问题解决啦 xf
  • C语言基本数据类型int, short int, long int, long long int, unsigned int, signed int等解析

    一 普通int类型 int类型是有符号整型 xff0c 即int类型的值必须是整数 xff0c 可以是正整数 xff0c 负整数 xff0c 零 int类型取值范围因计算机系统而异 早起的16位IBM PC兼容机使用16位来存储一个int值
  • Docker容器内更新apt-get 国内加速

    Docker容器内更新源apt get的方法 由于不使用国内镜像网速缓慢 xff0c 所以使用国内镜像加速就很必要了 xff0c 但是经过博主测试大部分apt get加速都是针对Ubuntu 的 xff0c 根本解决不了Docker 容器内
  • STM32F4 422串口通信

    STM32F429 422串口通信 422串口的硬件原理图如下 xff1a 422串口和232串口不同的是引脚的电平控制 xff0c 还有DE是发送使能 xff0c 置高即可 xff1a RE是接收使能 xff0c 置低即可 422接出来的
  • Java基础学习记录

    一 基础DOS操作 dir xff1a directory 列出当前目录内容 md xff1a make directory 创建目录 rd xff1a remove directory 删除目录 cd xff1a change direc
  • 6、杂项:rtk原理简要说明

    目录 x1f345 点击这里查看所有博文 随着自己工作的进行 xff0c 接触到的技术栈也越来越多 给我一个很直观的感受就是 xff0c 某一项技术 经验在刚开始接触的时候都记得很清楚 往往过了几个月都会忘记的差不多了 xff0c 只有经常
  • 树莓派控制直流电机

    古月金真 1 直流电机 直流电机是一种将直流电能转换为机械能的装置 xff0c 被广泛的用于驱动各种设备 xff0c 如电风扇 遥控小车 电动车窗等 xff0c 在控制设备运转领域中 xff0c 它是不可或缺的一种设备 小型直流电机的的电压
  • SVN创建指定版本号的分支

    SVN创建分支下面三个选择的具体含义 xff1a 最新版本 xff08 HEAD revision in therepository xff09 直接从版本库中最新创建 xff0c 由于不需要从你的工作拷贝中传输数据 xff0c 创建过程会
  • 图像降噪算法——图像降噪算法总结

    图像降噪算法 图像降噪算法总结 图像降噪算法 图像降噪算法总结 图像降噪算法 图像降噪算法总结 前面这段时间我看了几篇和图像降噪相关的Review xff0c 给我的感觉就是SLAM这一研究领域像是一片汪洋 xff0c 而图像降噪领域则是另
  • 图像降噪算法——图像噪声模型

    图像降噪算法 图像噪声模型 图像降噪算法 图像噪声模型1 图像噪声建模2 C 43 43 代码实现3 结论 图像降噪算法 图像噪声模型 1 图像噪声建模 首先 xff0c 我们要区分图像传感器噪声和图像噪声 xff0c 图像传感器噪声我在博
  • 图像降噪算法——维纳滤波

    图像降噪算法 维纳滤波 图像降噪算法 维纳滤波1 基本原理2 C 43 43 代码实现3 结论 图像降噪算法 维纳滤波 维纳滤波是在频域中处理图像的一种算法 xff0c 是一种非常经典的图像增强算法 xff0c 不仅可以进行图像降噪 xff
  • 图像降噪算法——Variance Stabilizing Transform / Generalization Anscombe Transform算法

    图像降噪算法 Variance Stabilizing Transform Generalization Anscombe Transform算法 图像降噪算法 Variance Stabilizing Transform Generali
  • 图像降噪算法——DnCNN / FFDNet / CBDNet / RIDNet / PMRID / SID

    图像降噪算法 DnCNN FFDNet CBDNet RIDNet PMRID SID 图像降噪算法 DnCNN FFDNet CBDNet RIDNet PMRID SID1 基本原理1 1 DnCNN1 2 FFDNet1 3 CBDN
  • SLAM算法总结——经典SLAM算法框架总结

    SLAM算法总结 经典SLAM算法框架总结 SLAM算法总结 经典SLAM算法框架总结 SLAM算法总结 经典SLAM算法框架总结 从研究生接触SLAM算法到现在也有两三年了 xff0c 期间学习了很多经典的SLAM算法框架并写了一些相关的
  • 计算机视觉算法——基于Anchor Free的目标检测网络总结

    计算机视觉算法 基于Anchor Free的目标检测网络总结 计算机视觉算法 基于Anchor Free的目标检测网络总结1 CornerNet1 1 关键知识点 网络结构及特点1 2 关键知识点 正负样本匹配1 3 关键知识点 损失计算
  • 计算机视觉算法——基于Transformer的语义分割(SETR / Segmenter / SegFormer)

    计算机视觉算法 基于Transformer的语义分割 xff08 SETR Segmenter SegFormer xff09 1 SETR1 1 网络结构及特点1 1 1 Decoder 1 2 实验 2 Segmenter2 1 网络结
  • 计算机视觉算法——基于深度学习的高精地图算法(HDMapNet / VectorMapNet / MapTR / VectorNet)

    计算机视觉算法 基于深度学习的高精地图算法 xff08 HDMapNet VectorMapNet MapTR VectorNet xff09 计算机视觉算法 基于深度学习的高精地图算法 xff08 HDMapNet VectorMapNe
  • VINS-Mono关键知识点总结——前端详解

    VINS Mono关键知识点总结 前端详解 VINS Mono关键知识点总结 前端详解1 VINS Mono的前端流程概述2 setMask 函数的作用3 rejectWithF 函数的作用4 addPoints 函数和 updataID