当前位置: 首页 > 编程日记 > 正文

SVO 学习笔记(深度滤波)

SVO 学习笔记(深度滤波)

  • 这篇博客
  • 论文中的深度滤波
  • 深度滤波的代码流程
    • 更新Seed对象
    • 初始化Seed对象
  • 结尾

这篇博客

这篇博客将介绍SVO论文中的Mapping部分,主要介绍深度滤波器在获取新的图像帧后,更新相应地图点深度的过程。(2020-6-18:修改“更新Seed对象”中的内容)

论文中的深度滤波

单目SLAM构建地图点是通过三角测量操作实现的。在SVO中,认为通过三角测量产生的地图点的深度值服从某种概率分布。在地图点刚构建时,这个概率分布的不确定性很大,即地图点深度值的不确定性较大(因为单目相机较难获得图中像素的真实深度)。这一点体现为下图中的浅绿色部分,即深度d在[dmin,dmax]中波动:
在这里插入图片描述
图中左边的方框是地图点的第一个观测帧(即产生该地图点的关键帧),右边是地图点的其他观测帧。将这两类帧分别记为 IrIk
 从图可以看出,降低地图点深度不确定性的方法是:针对一个由 Ir 产生的新地图点,在它的其他观测帧 Ik 上找到匹配的特征点(使用极线搜索的方法,极线为[dmin,dmax]在 Ik 上的投影线段)。以此构建 IrIk 上的一对匹配特征,并使用这对匹配特征进行三角测量,获得该地图点新的深度测量值。将这新的测量值与地图点已有的深度信息融合,更新该深度值的分布情况,使[dmin,dmax]的范围变小。
 当地图点的观测帧越多,它的深度值的波动范围就会越小。当其小到一定程度时,就可将该地图点放进地图中使用了。
 上述就是SVO中深度滤波的大致概念,下面介绍它的代码流程。

深度滤波的代码流程

在SVO中,深度滤波被定义为一个单独的类对象。它将每个新构建的地图点设定为一个Seed对象(种子对象)。Seed对象包括了待优化的地图点、地图点深度的平均值和方差等信息。
 深度滤波器通过观测帧来优化种子对象。当种子对象中深度值的方差足够小时,将该种子对应的地图点添加到地图中使用,否则继续进行优化。
 在优化过程中,普通帧和关键帧对深度滤波的作用不同:
 1、如果是普通帧,就用它来优化深度值未收敛的地图点;
 2、如果是关键帧,还需要提取帧上新的特征点,并为这些特征点产生新的种子对象。
 代码中使用 addFrameaddKeyFrame 两个函数处理不同的帧。在处理普通帧时会在addFrame函数中使用 updateSeeds 函数来更新种子对象:

更新Seed对象

//使用普通帧更新所有能够更新的Seed对象
//只更新一定数量的Seeds,不能在此消耗过多时间
void DepthFilter::updateSeeds(FramePtr frame)
{size_t n_updates=0, n_failed_matches=0, n_seeds = seeds_.size();lock_t lock(seeds_mut_);std::list<Seed>::iterator it=seeds_.begin();const double focal_length = frame->cam_->errorMultiplier2();//像素点的测量偏差double px_noise = 1.0;double px_error_angle = atan(px_noise/(2.0*focal_length))*2.0;while( it!=seeds_.end()){//判断种子更新是否需要终止if(seeds_updating_halt_)return;//不去更新那些很老的Seed(产生这些Seed的关键帧ID比较老)if((Seed::batch_counter - it->batch_id) > options_.max_n_kfs) {it = seeds_.erase(it);continue;}//检查待优化的地图点能否在当前帧上观测到SE3 T_ref_cur = it->ftr->frame->T_f_w_ * frame->T_f_w_.inverse();const Vector3d xyz_f(T_ref_cur.inverse()*(1.0/it->mu * it->ftr->f) );if(xyz_f.z() < 0.0)  {++it; // 投影点在相机成像面的后面continue;}if(!frame->cam_->isInFrame(frame->f2c(xyz_f).cast<int>())) {++it; // 投影点不再相机观测范围内continue;}// we are using inverse depth coordinates使用逆深度进行参数化float z_inv_min = it->mu + sqrt(it->sigma2);float z_inv_max = max(it->mu - sqrt(it->sigma2), 0.00000001f);double z;if(!matcher_.findEpipolarMatchDirect(//因为是逆深度,所以用分式*it->ftr->frame, *frame, *it->ftr, 1.0/it->mu, 1.0/z_inv_min, 1.0/z_inv_max, z))//z是找到匹配后,三角化计算出来该地图点在参考帧中的深度{it->b++; // increase outlier probability when no match was found++it;++n_failed_matches;continue;}// compute tau计算由一个像素测量偏差所带来的不确定度double tau = computeTau(T_ref_cur, it->ftr->f, z, px_error_angle);//计算测量值的标准差double tau_inverse = 0.5 * (1.0/max(0.0000001, z-tau) - 1.0/(z+tau));// update the estimate更新种子的概率分布。更新的是逆深度的概率分布updateSeed(1./z, tau_inverse*tau_inverse, &*it);++n_updates;if(frame->isKeyframe()){//如果当前帧是关键帧,特征点检测时,不能在这个位置上(网格内)提取特征点//因为这个位置已经找到了与地图点的匹配点,即已存在了一个特征feature_detector_->setGridOccpuancy(matcher_.px_cur_);}//如果种子收敛了,就初始化一个候选地图点,并删除掉这个种子if(sqrt(it->sigma2) < it->z_range/options_.seed_convergence_sigma2_thresh){assert(it->ftr->point == NULL); // TODO this should not happen anymore//注意是逆深度!!Vector3d xyz_world(it->ftr->frame->T_f_w_.inverse() * (it->ftr->f * (1.0/it->mu)));//创建新地图点Point* point = new Point(xyz_world, it->ftr);//将参考帧中对应特征点指向这个新的地图点it->ftr->point = point;{seed_converged_cb_(point, it->sigma2); // put in candidate list}it = seeds_.erase(it);}//种子的深度值的下限不确定,是个坏种子else if(isnan(z_inv_min)){SVO_WARN_STREAM("z_min is NaN");it = seeds_.erase(it);}else++it;}
}

在使用普通帧进行种子更新的过程中,有两个重要函数:1、updateSeed函数完成多个测量值的融合;2、computeTau函数计算观测帧中特征点的测量误差(代码中认为有一个像素坐标的测量误差)对地图点深度不确定性的影响。这两个函数是相关计算公式的实现,前者对应的公式和推导过程可以参考这篇博客;后者的公式推导可以参考《视觉SLAM十四讲》P326中的内容。(2020-6-18更新:)这里主要介绍updateSeed函数,因为computeTau和参考资料中的代码相似。

// 完成逆深度分布的融合。参数:新估计的逆深度 x 、x对应的方差 tau2 、x对应的 seed
void DepthFilter::updateSeed(const float x, const float tau2, Seed* seed)
{float norm_scale = sqrt(seed->sigma2 + tau2);if(std::isnan(norm_scale))return;// 构建以norm_scale为方差的正态分布boost::math::normal_distribution<float> nd(seed->mu, norm_scale);// 1/(s^2) = 1/(sigma2)+ 1/(tau2)float s2 = 1./(1./seed->sigma2 + 1./tau2);// m = (s^2) * (mu/sigma2 + x/tau2)float m = s2*(seed->mu/seed->sigma2 + x/tau2);// C1 = a/(a+b) * N(x|mu,sigma2 + tau2) ,pdf()概率密度:正态分布nd在x处的概率密度float C1 = seed->a/(seed->a+seed->b) * boost::math::pdf(nd, x);// C2 = b/(a+b) * U(x)float C2 = seed->b/(seed->a+seed->b) * 1./seed->z_range;// C1、C2的归一化float normalization_constant = C1 + C2;C1 /= normalization_constant;C2 /= normalization_constant;// 计算 f、e 来进行a、b的更新// f = C1*(a+1)/(a+b+1) + C2*(a)/(a+b+1)float f = C1*(seed->a+1.)/(seed->a+seed->b+1.) + C2*seed->a/(seed->a+seed->b+1.);float e = C1*(seed->a+1.)*(seed->a+2.)/((seed->a+seed->b+1.)*(seed->a+seed->b+2.))+ C2*seed->a*(seed->a+1.0f)/((seed->a+seed->b+1.0f)*(seed->a+seed->b+2.0f));// update parameters 参数更新// mu = C1*m+C2*mu;float mu_new = C1*m+C2*seed->mu;seed->sigma2 = C1*(s2 + m*m) + C2*(seed->sigma2 + seed->mu*seed->mu) - mu_new*mu_new;seed->mu = mu_new;// a 和 b 的更新seed->a = (e-f)/(f-e/f);seed->b = seed->a*(1.0f-f)/f;
}

初始化Seed对象

在处理关键帧时,会在addKeyFrame函数中调用initializeSeed函数来初始化新的Seed对象:

//当有关键帧产生时,初始化新的Seed
void DepthFilter::initializeSeeds(FramePtr frame)
{//从帧中提取特征Features new_features;feature_detector_->setExistingFeatures(frame->fts_);feature_detector_->detect(frame.get(), frame->img_pyr_,Config::triangMinCornerScore(), new_features);//为每个新的特征初始化一个Seed对象seeds_updating_halt_ = true;lock_t lock(seeds_mut_); // by locking the updateSeeds function stops++Seed::batch_counter;//初始化新的Seed对象时,使用的初始深度是当前帧中观测到的地图点的平均深度std::for_each(new_features.begin(), new_features.end(), [&](Feature* ftr){seeds_.push_back(Seed(ftr, new_keyframe_mean_depth_, new_keyframe_min_depth_));});if(options_.verbose)//显示产生了多少个新的SeedsSVO_INFO_STREAM("DepthFilter: Initialized "<<new_features.size()<<" new seeds");seeds_updating_halt_ = false;
}

结尾

感谢耐心的博友读到最后,希望这篇博客能帮助到各位。如果这篇博客的内容存在不足,希望大家指出,感谢大家的纠正。

相关文章:

寻找孪生素数(当p为素数时,p+2也为素数)

数学家希尔伯特在1900年国际数学家大会的报告上提出一个“孪生素数猜想”&#xff0c;即&#xff1a; 存在无穷多个素数p&#xff0c;使得p 2是素数。p和p2这一对差为2的素数&#xff0c;被称为“孪生素数”。 看起来&#xff0c;这个猜想是成立的&#xff0c;我们总能找到很多…

C++利用cin输入时检测回车的方法

今天做TJU的OJ &#xff0c;其中一道题是先读入一个字符串&#xff0c;再读入一个整数&#xff0c;循环往复&#xff0c;直到字符串是空&#xff0c;也就是说回车键结束循环。 但是cin对空格和回车都不敏感&#xff0c;都不影响继续读入数据&#xff0c;所以需要一种新的方式检…

使用grep过滤make的输出内容

make的输出内容其实分为两种&#xff0c;有些是到标准输出&#xff0c;有些是到标准错误&#xff0c;由于标准输出和标准错误默认都是屏幕&#xff0c;所以平时区分不出来&#xff0c; 实际上一般是error和warning信息到标准错误&#xff0c;其余的到标准输出。 如果要过滤erro…

2021年中国工业互联网安全大赛核能行业赛道writeup之机房密码

附件题&#xff1a;机房密码 题目描述&#xff1a; &#xff08;具体描述忘记了&#xff09; 经过黑客人员的不屑努力&#xff0c;在上位机上发现了登录密码的一半信息&#xff0c;剩下的一半要靠你们继续努力辣&#xff01;&#xff01;&#xff01; ZmxhZyU3Qmgwd19hX0M 附件…

ORB-SLAM2系统的实时点云地图构建

ORB-SLAM2系统的实时点云地图构建这篇博客点云地图构建的流程代码介绍点云地图构建类对象小调整获取关键帧点云地图构建与叠加在地图中设置当前相机位置点云地图到Octomap的转换地图效果结尾这篇博客 &#xff08;PS:修改于2020-9-21&#xff0c;添加了关于System和Tracking类…

使用maven导入jar包

我们都经历过自己写代码时有时就要引用一些第三方的jar包&#xff0c;这个我们都会&#xff0c;但在公司里进行团队开发时&#xff0c;是不允许我们自己导入jar包的&#xff0c;是由项目组长之类的统一导入jar包&#xff0c;我们在这里来了解一下这个过程&#xff1a; a、先创建…

Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...

Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法 www.MyException.Cn 发布于&#xff1a;2012-09-15 19:09:28 浏览&#xff1a;164次0Struts2中action接收参数的三种方法及ModelDriven和Preparable接口结合JAVA反射机制的灵活…

关于CSS的长度单位及颜色表示

长度单位 1.q 1/4mm. 2.px 计算机语言中的像素。大多数网页制作常用图片分辨率为72&#xff0c;即每英寸像素为72,1英寸等于2.54cm。那么通过换算可以得出每厘米等于28像素。 3.em 它是描述相对于应用在当前元素的字体尺寸&#xff0c;所以它也是相对长度单位。一班浏览器字体大…

2021年中国工业互联网安全大赛核能行业赛道writeup之鱿鱼游戏

目录 一、尝试 二、Writeup 附加题 鱿鱼游戏&#xff08;来自最近一部很火的韩剧&#xff09; 题目描述&#xff1a; 小王由于操作不规范&#xff0c;误将不明U盘插入到上位机中&#xff0c;导致上位机中的某些关键文件被加密&#xff0c;但攻击者在U盘中还留下了一个可执行…

视觉惯性SLAM: VI ORB-SLAM

视觉惯性SLAM: VI ORB-SLAM这篇博客视觉惯性SLAM预备知识符号说明&#xff1a;相机投影变换矩阵IMU数据更新方程IMU数据的预积分VI ORB-SLAM各环节工作方式InitializationTrackingLocalMappingLoop ClosingFull BAIMU初始化估计bgb_{g}bg​估计尺度sss和重力向量gWg_{W}gW​&am…

AEC、AGC、ANS在视音频会议中的作用?

AGC是自动增益补偿功能&#xff08;Automatic Gain Control&#xff09;&#xff0c;AGC可以自动调麦克风的收音量&#xff0c;使与会者收到一定的音量水平&#xff0c;不会因发言者与麦克风的距离改变时&#xff0c;声音有忽大忽小声的缺点。ANS是背景噪音抑制功能&#xff08…

Java中的拆箱与装箱

我们先来了解一下拆箱与装箱的概念&#xff1a; 装箱&#xff1a;将基本数据类型转换为包装类&#xff1b; 拆箱&#xff1a;将包装类转换为基本数据类型 我们来看两串代码&#xff1a; Integer b1 127;Integer b2 127;System.out.println(b1b2);//trueInteger b3 128;Inte…

WannaCry的UWP版,哈哈哈

转载于:https://www.cnblogs.com/R00R/p/6916731.html

2021年中国工业互联网安全大赛核能行业赛道writeup之数据库登录

附件题&#xff1a;数据库登录&#xff08;一道MISC、流量分析类型题目&#xff09; 题目描述&#xff1a; 具体描述已经忘记o(╯□╰)o 大概意思就是分析附件里的.pcapng包&#xff0c;找到flag。流量涉及到 MySQL 数据库了。 附件下载&#xff1a; https://download.csdn.n…

学生管理系统(用maven来导入jar包)

不废话&#xff0c;直接上 先看一下项目列表&#xff1a; 首先创建一个maven工程&#xff0c;然后导入相应的jar包&#xff0c;请参考&#xff1a;使用maven导入jar包 接着在Source Folder创建具体的项目&#xff1a; Main类&#xff08;客户端&#xff09; package com.z…

视觉惯性SLAM:VINS-Mono

视觉惯性SLAM&#xff1a;VINS-Mono这篇博客一些符号说明IV 测量数据的预处理A.视觉处理前端B.IMU预积分V. 初始化A.Vision-Only SfM in Sliding WindowB.Visual-Inertial AlignmentVI.TIGHTLY COUPLED MONOCULAR VIOA.公式介绍B.IMU误差C.视觉误差D.边缘化E.位姿优化F.以IMU采…

mysql 0x80004005 unable to connect to any of the specified mysql hosts

语言&#xff1a;c# 问题&#xff1a;偶尔会出现连不上mysql 报标题的这个错误。 解决方法&#xff1a;把server localhost 改为 127.0.0.1 或者静态IP &#xff0c;按着改暂时没出现了&#xff0c;继续观望&#xff01; 转载于:https://www.cnblogs.com/wdw31210/p/9857514…

iOS开发-自己定义重用机制给ScrollerView加入子视图

iOS开发-自己定义重用机制给ScrollerView加入子视图 事实上这个问题我非常早就想过&#xff0c;仅仅是没有通过去写程序实现&#xff0c;昨天有人提起&#xff0c;我就巧了一下 不知道大家打印郭tableview&#xff1a;cellforrow中cell初始的次数&#xff0c;也就是重用池中的c…

2021年中国工业互联网安全大赛核能行业赛道writeup之Webshell密码

附件题&#xff1a;Webshell密码 题目描述&#xff1a; 某次攻防演练中&#xff0c;抓到了一个webshell的流量&#xff0c;请分析出密码&#xff0c;flag形式&#xff1a;flag{密码} 附件下载&#xff1a; https://download.csdn.net/download/qpeity/33675356https://downlo…

关于python3与python2同时存在情况下导入pyqt失败解决记录

最近感觉tkinter功能还是比较不适合新手做出高大上的界面&#xff0c;故开始使用pyqt&#xff0c;通过pip安装好了之后&#xff0c;利用qt设计师设计好界面之后&#xff0c;cmd运行之&#xff0c;报错提示没有找到pyqt5模块&#xff0c;IDE运行能正常加载 查找资料后发现&#…

ORBSLAM-Altas:多地图SLAM

ORBSLAM-Atlas&#xff1a;多地图SLAM这篇博客ORBSLAM-Altas这个系统系统方法两类子地图新地图的构建相机位姿的可观测性子地图融合系统线程结尾这篇博客 最近ORB-SLAM3横空出世&#xff0c;马上跑去GitHub膜拜。然后在项目的相关工作中看到了ORB-SLAM3使用了一个多地图方法。这…

Android驱动学习-内部机制_回顾binder框架关键点

内部机制_回顾binder框架关键点server注册服务时, 对每个服务都提供不同的ptr/cookie,在驱动程序里对每个服务都构造一个binder_node, 它也含有ptr/cookie client使用服务前要先getService&#xff1a;会在驱动程序里对该服务构造一个binder_ref, binder_ref含有desc, node成员…

数据库--事务

我们知道数据库中的SQL语句分为DDL(数据定义语言)、DQL(数据查询语言)、DML(数据操纵语言)、DCL(数据控制语言)&#xff0c;详情请看SQL语句 当数据库的表中数据执行完添加、删除、和修改等数据操纵语言&#xff08;DML&#xff09;后&#xff0c;需要执行commit(提交)数据控制…

2021年中国工业互联网安全大赛核能行业赛道writeup之传统流量取证

附件题&#xff1a;传统流量取证 题目描述&#xff1a; 在某次攻防演练中&#xff0c;小王发现流量探针平台突然告警&#xff0c;小王第一时间下载了告警流量包&#xff0c;并进行分析&#xff1a;发现攻击队攻击在攻入内网后&#xff0c;利用了一个内网OA的一个漏洞&#xff…

ORB-SLAM3 论文笔记

ORB-SLAM3 论文笔记这篇博客ORB-SLAM3系统相机模型的抽象(Camera Model)重定位的问题图片矫正的问题视觉惯性SLAM的工作原理相关公式IMU初始化跟踪和建图系统对跟踪丢失的应对多地图的闭环检测和地图融合位置识别视觉地图融合方法视觉惯性地图的融合方法单个地图中的闭环融合结…

为什么需要 外键 呢?

生活现象&#xff1a; 不知你们是否遇到过这样的现象&#xff0c;就是你辛辛苦苦花了几十块钱注册一个会员&#xff0c;结果家里的七大姑&#xff0c;八大姨都要拿去用&#xff0c;而且完全可以用。还有就是一个淘宝账号里却可以添加好多个收获地址(里面包括收货人的姓名&#…

JavaScript闭包函数箭头函数调用与执行

一、标准的闭包函数 //一、标准的闭包函数 function A() {var i0;i;console.log(i : i);return function b() {return function c() {return i}} }var a A(); // 初始化A&#xff0c;执行A内的非function语句 ‘ i0; i‘&#xff0c;输出 I : 1 console.log(a()); // 执行fu…

jlink api sdk c# 离线数获取 标定

jlink 如何 离开 keil、IAR 监控变量呢&#xff1f; 目前 jlink的 api 可以做到&#xff0c;自己可以用C# 做一个 上位机&#xff0c;监控RAM里面的变量。而不用打开keil 调试。还可以 刷写 flash&#xff0c;可以用在产品量产的刷写上。SEGGER 的 jlink sdk并不是免费的&…

2021年中国工业互联网安全大赛核能行业赛道writeup之usb流量分析

目录 一、USB协议 二、键盘流量 三、鼠标流量 四、writeup 附件题&#xff1a;usb流量分析 题目描述&#xff1a; 具体描述忘记了o(╯□╰)o 大概意思是有个U盘插到电脑上&#xff0c;然后经过一些操作导致该电脑重启了。找到这个过程中的flag。 附件下载&#xff1a; 20…

BOS项目 第2天(BaseDao、BaseAction、用户登录、自定义strust登录拦截器)

BOS项目 第2天 今天内容安排&#xff1a; 1、根据提供的pdm文件生成sql 2、持久层和表现层设计---BaseDao、BaseAction 3、实现用户登录功能 4、jQuery EasyUI 消息提示控件 5、jQuery EasyUI menubutton菜单按钮 6、自定义struts2拦截器&#xff0c;实现用户未登录自动跳转到…