【视觉SLAM十四讲】第12讲 回环检测

2023-05-16

12.1 回环检测概述

前面已经介绍过了前端和后端,前端用于特征点的提取以及轨迹、地图的初始值,而后端负责对这部分数据进行优化。考虑到误差的存在,每一个时刻存在的误差会不断累积,从而产生累积误差的问题,使得长期的估计结果不可靠,或者说无法得到一个全局一致的轨迹和地图。
在这里插入图片描述
虽然后端能够估计最大后验误差,但所谓“好模型架不住烂数据”,只有相邻关键帧数据时,我们能做的事情并不很多,也无从消除累积误差。但是,回环检测模块,能够给出除了相邻帧之外的,一些时隔更加久远的约束,比如说第一帧和第一百帧之间的位姿变换。这个约束主要是因为相机能够察觉相机经过了同一个地方,而且采集到了相似的数据。
回环检测的关键就是如何有效地检测出相机经过了同一个地方这件事。如果检测到了回环,就可以为后端的优化提供更多的有效数据,从而得到一个更好的优化,特别是一个全局一致的估计,这样我们就可以把偏差根据检测到的回环点,“拉正”到一个合适的位置。

回环系统对于SLAM系统意义重大,它关系到我们估计的轨迹和地图在长时间下的正确性。一些情况下,我们将仅有前端和局部后端的系统称为VO,而将带有回环检测和全局后端的称为SLAM。

一般提到回环检测,最简单的思路就是将任意的两张图片都进行一遍特征匹配,根据正确匹配的数量来确定哪两个图像之间存在关联。这种思路的问题在于检测的数量实在是太大,对于实时系统来说不是很实用。另一种思路是随机抽取历史数据进行回环检测,这种做法能够维持常数时间的运算量,但是这种盲目试探方法在帧数增长时,抽到回环的几率又大幅下降,使得检测效率不高。

在实际情境下,大多数的回环检测都是依据下面的两个思路:基于里程计的几何关系和基于外观。
基于里程计是指当我们发现当前相机运动到了之前的某个位置附近时,检测它们有没有回环关系。这自然是一种直观的想法,但是由于累积误差的存在,我们往往没法正确地发现“运动到了之前的某个位置附近”这件事实,回环检测也无从谈起。也就是说,理想情况下,当我们根据里程计检测到运动回原点附近的时候,自然会出现回环,但是因为累积误差,里程计本身的结果就不一定准确,里程计回到原点,真实情况下可并不是这样,自然无法用其校正回环。
基于外观则与前端和后端的估计都无关,仅仅根据两张图像的相似性来确定回环检测关系,这种方式能够有效地在不同的场景下工作,所以已经成为了SLAM中的主流做法。

在基于外观的方式下,核心问题是如何计算图像之间的相似性。一般的思路是利用相似性评分,当评分大于一定值的时候认为产生了回环。但是我们并不能直接使用灰度值,也就是将两个图像的灰度值相减然后套个外壳。
在这里插入图片描述
这种方式的问题有两个。首先,灰度值不是一种稳定的测量值,它会受到环境光和相机曝光的影响。另一方面,当相机视角发生少量变化时,即使每个物体的光度不变,它们的像素也会在图像中发生位移,造成一个很大的差异值。

所以我们说,这个函数s不能很好的反映图像间的相似关系。这里牵涉到一个“好”和“不好”的定义问题。这里可以引出感知偏差和感知变异两个概念。根据回环检测的结果,我们可以产生下面四种结果:
在这里插入图片描述
这其实就是机器学习里面分类那部分的四种结果,只不过这里换了个说法。借用医学上的说法,将假阳性称为感知偏差,将假阴性称为感知变异。我们希望TP和TN要尽可能高,在此基础上引入了准确率和召回率:
在这里插入图片描述
从公式字面意义上来看,准确率描述的是,算法提取的所有回环中,确实是真实回环的概率,换句话说就是认为是真的判断只能怪有多少是正确的判断。而召回率则是说,在所有真实回环中,被正确检测出来的概率,也就是真实情况为真的个例中有多少被正确地判断出来了。
这两个统计量本身是两个矛盾。准确率高可以表现为只筛选“把握”高的个例,这意味着只有一小部分把握很高的个例才会被认为产生了回环,所以有大量把握不大但是真的是回环的帧被落下了,自然会导致召回率增高。反过来,召回率高可以通过将所有帧都看做回环来实现,这种情况下,召回率能达到100%,但是正确率会很低,因为将大量不是回环的帧认为是回环。
为了评价算法的好坏,我们会测试它在各种配置下的P和R值,然后做出一条Precision-Recall曲线。当用召回率为横轴,用准确率为纵轴时,我们会关心整条曲线偏向右上方的程度、100%准确率下的召回率,或者50%召回率时候的准确率,作为评价算法的指标。
在SLAM中,我们对准确率要求更高,所以在选择回环检测算法的时候,更加倾向于将参数设置地更加严格,或者在检测之后加上回环验证的步骤。

12.2 词袋模型

两张图像直接相减的方法不好,我们完全可以借鉴VO的特征点方法。一种很类似的方法就这样出现了,即词袋模型。

词袋的目的是利用图上有哪几个特征来描述一个图像。比如说这个图上有一辆车一条狗,另一张图上有一匹马和一台电脑等。具体来说,我们将车、狗、马、电脑这种概念称为单词,许多单词组成了字典,这样可以根据一张图上有什么没有什么,将图像用一个向量来表示,通过向量和字典的比较,来表示相似程度。
在这里插入图片描述
上图就是对一张图的简单描述,字典中一共有w1w2w3三个特征,对于图片A来说,它只有w1和w2两个特征。
利用这种向量,来表示图像是否含有某类特征,比直接使用灰度值要准确的多。又因为描述向量说的是“是否出现”,而不管它们“在哪
儿出现”,所以与物体的空间位置和排列顺序无关,因此在相机发生少量运动时,只要物体仍在视野中出现,我们就仍然保证描述向量不发生变化。
基于这种向量的表示,我们可以描述相似性:
在这里插入图片描述
其中范数取 L1 范数,即各元素绝对值之和。请注意在两个向量完全一样时,我们将得到1;完全相反时(a为0的地方b为1)得到0。这样就定义了两个描述向量的相似性,也就定义了图像之间的相似程度。

12.3 字典

根据前面的介绍,字典是单词的集合,一个单词表示一个概念,但是一个单词并不能与一个单独的特征点划等号,它本身不是从图像上提取出来的,而是一类特征的组合,所以字典的生成就类似一个聚类问题。
假设我们对大量的图像提取了N个特征点,我们想用这些特征点,去构建一个大小为k的字典,我们的方法就是用典型的聚类算法Kmeans算法。
在这里插入图片描述
利用这种方法,我们提取到了字典需要的k个单词,现在问题是我们入耳利用图像的特征点,去查找字典中需要的单词。
首先,我们需要明确字典的大小,显然字典必须要够大,这是基于通用性的考虑,我们会使用一个较大规模的字典,去保证当前使用环境中的特征在字典中都有出现,不能出现“查无此词”的现象。所以查字典的过程就是一个大问题。对于这个问题,我们一般使用树结构去优化:
在这里插入图片描述
最简单的方法是用k叉树去表示:
在这里插入图片描述
最终我们仍在叶子层构建了单词,而树结构中的中间节点仅供快速查找时使用。这样一个k分支,深度为d的树,可以容纳k^d个单词。另一方面,在查找某个给定特征对应的单词时,只需将它与每个中间结点的聚类中心比较(一共d次),即可找到最后的单词,保证了对数级别的查找效率。

12.4 相似度计算

有了字典,我们现在对于任何一个给定的特征,就可以在字典树里面查找,从而得到一个对应的单词。这种方法的缺点在于,我们对所有的单词都是一视同仁的,而对于一些重要性不同的特征,这种一视同仁是及其不公平的。
对于这个问题,我们借鉴文本检索中的TF-IDF思想。TF部分的思想是,某单词在一个图像中经常出现,它的区分度就高。另一方面,IDF的思想是,某单词在字典中出现的频率越低,则分类图像时区分度越高。
在词袋模型中,在建立字典时可以考虑 IDF 部分。我们统计某个叶子节点wi中的特征数量相对于所有特征数量的比例,作为IDF部分。假设所有特征数量为n,wi数量为ni,那么该单词的IDF为:
在这里插入图片描述
另一方面,TF部分则是指某个特征在单个图像中出现的频率。假设图像A中,单词wi出现了ni次,而一共出现的单词次数为n,那么TF为:
在这里插入图片描述
于是wi的权重等于TF乘IDF之积:
在这里插入图片描述
考虑权重以后,对于某个图像A,它的特征点可对应到许多个单词,组成它的词袋就变成下面的形式:
在这里插入图片描述
这种情况下,差异的计算也会发生变化:
在这里插入图片描述

12.5 实验分析与评述

在SLAM中,我们经常会怀疑是不是字典选择太小了。具体来说,当字典规模增加时,无关图像的相似性明显变小了。而相似的图像,虽然分值也略微下降,但相对于其他图像的评分,却变得更为显著了。这说明增加字典训练样本是有益的。

对于一些相似度本身很大的场景,比如楼道,利用相似性评分的分值的绝对大小,并不会有很大的帮助,这时我们一般采用一个先验相似度,去表示某时刻关键帧图像与上一个时刻的关键帧的相似性,然后利用这个值去对剩下的分值做一个归一化:
在这里插入图片描述
站在这个角度上,我们说:如果当前帧与之前某关键帧的相似度,超过当前帧与上一个关键帧相似度的3倍,就认为可能存在回环。这个步骤避免了引入绝对的相似性阈值,使得算法能够适应更多的环境。

对于回环检测使用的帧,从实践上说,用于回环检测的帧最好是稀疏一些,彼此之间不太相同,又能涵盖整个环境。但是对于已经检测到回环的情况,假设是第1帧和第n帧,这两帧检测到了回环,对于优化是有帮助的,但接下去的n+1和n+2都会产生回环,产生的帮助就没那么大了,甚至有些多余,这种情况下,我们会把“相近”的回环聚成一类,使算法不要反复地检测同一类的回环。

词袋的回环检测算法完全依赖于外观而没有利用任何的几何信息,这导致外观相似的图像容易被当成回环。并且,由于词袋不在乎单词顺序,只在意单词有无的表达方式,更容易引发感知偏差。验证的方法有很多。其一是设立回环的缓存机制,认为单次检测到的回环并不足以构成良好的约束,而在一段时间中一直检测到的回环,才认为是正确的回环。这可以看成时间上的一致性检测。另一方法是空间上的一致性检测,即是对回环检测到的两个帧进行特征匹配,估计相机的运动。然后,再把运动放到之前的位姿图中,检查与之前的估计是否有很大的出入。总之,验证部分通常是必须的,但如何实现却有很多种方法。

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

【视觉SLAM十四讲】第12讲 回环检测 的相关文章

  • tensorflow-gpu1.14 + Win10 + CUDA10.0 + CUDNN7.5.0 + Python3.6 + VS2015安装

    最近学习深度学习 xff0c 在配置环境中的过程中遇到很多问题 xff0c 在这进行总结 xff0c 希望对大家有帮助 一 整个软件安装配置过程 xff0c 很多博客写的很详细 xff0c 附上链接 xff1b https blog csd
  • VS2015下配置海康威视SDK

    1网络摄像头可以在官网下载到SDK开发包 xff0c 进入海康威视官网 xff0c 选择何时的版本 xff0c 点击下载 https www hikvision com cn download 61 html 下载完成进行解压 解压完成 x
  • 如何提升串口响应速度

    最近负责编写公司的工厂模式指令集 xff0c 碰到了一些代码之外的问题 xff0c 困扰了我很久 因为综测那边对串口响应速度的要求很高 xff0c 要求从上位机下发指令开始到上位机接收到完整回复 xff0c 整个过程的响应速度要达到几十个m
  • rv1126 SDK简单编译

    rv1126 SDK简单编译 在工程的根目录下执行命令 source envsetup sh 会出现很多选项 xff0c 选择 rockchip rv1126 rv1109 spi nand 这个选项 xff0c 输入93 我的FLASH是
  • socket套接字编程之UDP协议封装

    1 UDP 协议特点 xff1a 传输层协议 无连接 不可靠传输 面向数据报 2 封装之前先将清楚几个要点 xff1a 2 1网络字节序 xff1a 注意设备的大小端 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出 接收主机把
  • C语言 使用调用函数的方法,将两个字符串连接起来

    因本人才疏学浅 xff0c 见识浅薄 xff0c 有不当之处望指正 xff0c 谢谢 xff01 这次用调用函数的方法 xff0c 连接两个字符串 在被调函数中可以说明形参数组的大小 xff0c 也可以不说明形参数组的大小 例如cat st
  • STL容器特点对比

    1 分类 序列式容器 xff08 sequential container xff09 vector list array deque forward list关联式容器 xff08 associative container xff09
  • 【RT-Thread】PIN 设备源码分析

    目录 1 获取引脚编号2 设置引脚模式3 设置引脚电平4 绑定 PIN 中断回调函数5 使能引脚中断6 总结7 PIN 设备使用示例 关于 RT Thread 的 PIN 设备驱动应用层面的介绍可以直接参考 RT Thread 的官网 xf
  • 基于VSCode的Linux内核调试环境搭建

    1 安装开发工具 span class token function sudo span span class token function apt span span class token function install span b
  • STM32定时器周期任务函数编写

    在STM32中我们对定时器的周期任务有一定的需求 xff0c 但在使用过程中 xff0c 需要将一些任务写到中断中 xff0c 中断函数会看上去比较复杂 xff0c 并且会有好多标志位 以流水灯为例 xff0c 周期为1s 介绍一下我自己的
  • stm32寄存器封装

    文章目录 前言 一 版本一 二 版本二 三 版本三 前言 本文记录的是用stm32开发的时候 一些底层的寄存器封装 固件库是如何帮我们完成这些工作的 一 版本一 代码如下 示例 span class token comment 外设基地址
  • 使用TI的MSP430实现一个单片机与上位机的数传系统。(西安电子科技大学综合应用开发实验)

    题目要求 xff1a 目标 xff1a 智能控制系统 利用单片机 xff08 开发平台任选 xff09 设计并编程实现一个单片机与上位机的数传系统 要求 xff1a 对单片机和PC 手机 单片机之间的通信进行设计 如果大作业没有设计通信部分
  • NVIDIA Jetson Xavier NX 控制GPIO

    NVIDIA Jetson Xavier NX 控制GPIO 文章目录 NVIDIA Jetson Xavier NX 控制GPIO前言一 简介二 代码实例1 gpio h2 gpio cpp 三 拓展 前言 在linux系统中以文件io的
  • NVIDIA Jetson Xavier NX禁用上电自启,使用按键开关机

    NVIDIA Jetson Xavier NX禁用上电自启 xff0c 使用按键开关机 文章目录 NVIDIA Jetson Xavier NX禁用上电自启 xff0c 使用按键开关机前言一 原理二 拓展 前言 NX默认上电自启 xff0c
  • Linux系统设置共享文件夹

    Linux系统设置共享文件夹 文章目录 Linux系统设置共享文件夹一 设置原理二 设置步骤1 安装samba2 创建 设置共享文件夹 三 测试 一 设置原理 基于Ubuntu16 04 xff0c 采用在线安装samba库的方式设置共享文
  • Linux:复位USB设备

    Linux xff1a 复位USB设备 文章目录 Linux xff1a 复位USB设备前言一 基本原理二 代码实例总结 前言 在Ubuntu16 04下开发SDR设备数据处理程序时 xff0c msi sdr设备有时运行几个小时后就会出现
  • Ubuntu Terminal终端默认常用快捷键总结

    Ubuntu Terminal终端默认常用快捷键总结 Ubuntu Terminal终端快捷键默认设置如下 xff0c 不同的发行版本可能有所出入 xff0c 以下快捷键在Ubuntu18 04LTS下可用 1 文件 快捷键说 明Ctrl
  • 基于c++ boost库实现进程管理

    基于c 43 43 boost库实现进程管理 1 前言 基于c 43 43 boost库与Terminator终端 xff0c 实现启动进程 进程运行状态监听 自动重启进程 杀死进程 设置进程环境变量等基础功能 2 原理 启动 杀死进程基于
  • 【RT-Thread】UART 设备源码分析

    官网介绍 I O 设备模型框架如下图 xff1a 但看到官网写道 设备驱动层是一组驱使硬件设备工作的程序 xff0c 实现访问硬件设备的功能 它负责创建和注册 I O 设备 xff0c 对于操作逻辑简单的设备 xff0c 可以不经过设备驱动
  • 基于c++ boost实现阻塞式ping指定IP

    基于c 43 43 boost实现阻塞式ping指定IP 1 前言 在实际业务场景中 xff0c 可能需要阻塞式检测目标IP连通性 xff0c 本程序基于c 43 43 boost库实现了一个简易的阻塞式ping指定IP例子 2 原理 在循

随机推荐

  • ROS_PACKAGE_PATH相关问题

    在ROS进行跨文件调用功能包时遇到报错 Resource not found The following package was not found span class token keyword in span span class t
  • 基于python批量调整图像大小

    前言 在写论文的时候常常因为截图的尺寸大小不一样 xff0c 导致图片排版很难受 xff0c 在word中又不会批量修改 xff0c 用下面的代码可以批量处理修改成一样的尺寸哦 xff01 代码如下 xff1a 在本文中 xff0c 我将向
  • PX4:【启动流程】

  • 线程池和多线程的区别

    线程池的概念 线程池大类总共分为4种 fixThreadPool 正规线程 xff08 传统线程池 xff09 cacheThreadPool 缓存线程池singleThreadPoll 单线程线程池 xff08 单例线程池 xff09 S
  • C++ libcurl Digest Auth

    C 43 43 libcurl Digest Auth postman操作如下 xff1a 附认证原理如下 xff1a MD5 md5 span class token punctuation span string HA1 span cl
  • 如何安装postman(超简单)

    方法一 xff1a xff08 官网下载 xff09 1 打开https www crx4chrome com crx 1058 2 稍微往下微微一拉就出现 Download crx file from Crx4Chrome gt 的选择
  • 转 curl 参数大全

    curl是一个非常实用的 用来与服务器之间传输数据的工具 xff1b 支持的协议包括 DICT FILE FTP FTPS GOPHER HTTP HTTPS IMAP IMAPS LDAP LDAPS POP3 POP3S RTMP RT
  • DataX mysql同步到mysql

    使用Datax web 创建同步任务 准备工作 创建数据源 配置数据库相关信息 创建执行器 配置执行器执行地址相关信息 1 构建reade 1 1 SQL语句 xff08 querySql xff09 在json文件中此部分配置就是 que
  • MQTT协议简介

    目录 MQTT协议简介一 MQTT协议特点1 1 发布和订阅1 2 QoS Quality of Service levels 二 MQTT数据包结构2 1 MQTT固定头2 2 MQTT可变长 Variable header 2 3 消息
  • vins位姿图优化

    我们 的滑动窗口和边缘化方案限制了计算的复杂性 xff0c 但也给系统带来了累积漂移 更确切地说 xff0c 漂移发生在全局三维位置 x y z 和围绕重力方向的旋转 yaw 为了消除漂移 xff0c 提出了一种与单目VIO无缝集成的紧耦合
  • 基于STM32开发板实现温湿度传感数据采集

    一 实验要求 本实验将选用STM32F407ZGT6开发板进行项目开发 xff0c 选用的传感器为DHT11温湿度传感器 传感器将采集到的数据传输到STM32 xff08 MCU xff09 主控进行数据处理 xff0c 最后通过串口打印出
  • 相机成像模型、内参矩阵、外参矩阵

    相机针孔成像模型 基本的小孔成像过程 xff1a X坐标系是针孔所在坐标系 xff0c Y坐标系为成像平面坐标系 xff0c P为空间一点 xff0c 小孔成像使得P点在图像平面上呈现了一个倒立的像 xff0c 俯视图如下 xff1a 由三
  • 韦东山学习笔记——UART(串口)的使用

    基于jz2440的串口使用 搬砖的文章概述UART的发送和接收串口之间的数据传输UART的用途串口的数据帧参数说明起始位数据位奇偶校验位停止位波特率 怎么发送一字节数据 xff0c 比如 A UART的优缺点优点缺点 UART相关配置寄存器
  • C++源文件的编译流程简介

    概述 C 43 43 C源文件 xff0c 包含 c h cpp hpp等格式的文件 xff0c 经过预处理 编译 汇编 链接后 xff0c 形成可执行文件 xff0c 也就是 exe文件 以一个简单项目MyProject为例 xff0c
  • 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系

    使用 sudo apt get install 安装软件时 xff0c 出现错误 无法修正错误 xff0c 因为您要求某些软件包保持现状 xff0c 就是它们破坏了软件包间的依赖关系 错误的主要原因是 xff0c 系统中已经安装了被依赖的包
  • kalibr工具的编译与安装

    安装 kalibr提供了两种安装使用的方法 一 直接使用打包好的程序 下载地址 xff0c 选择CDE packages下载 xff08 需要访问Google xff09 使用注意事项 xff1a 只有64位系统可以使用 二 源码编译 安装
  • TCP/IP协议栈协议头

    目录 OSI与TCP IP模型UDP发送数据过程以太网协议头IP协议头UDP协议头UDP包 OSI与TCP IP模型 UDP发送数据过程 以太网协议头 把上面的以太网头用一个结构体表示如下 xff1a span class token ma
  • VLAN标签

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是VLAN标签 我们知道 xff0c 以太网交换机根据MAC地址表来转发数据帧 MAC地址表中包含了端口和端口所连接终端主机MAC地址的
  • fastjson的一些用法

    一般情况下 xff0c 在进行redis集群写入时 xff0c 使用jedisCluster set key value value为String类型 xff0c 那么就用到了fastjson进行序列化 以下是一些要点 xff1a 1 序列
  • 【视觉SLAM十四讲】第12讲 回环检测

    12 1 回环检测概述 前面已经介绍过了前端和后端 xff0c 前端用于特征点的提取以及轨迹 地图的初始值 xff0c 而后端负责对这部分数据进行优化 考虑到误差的存在 xff0c 每一个时刻存在的误差会不断累积 xff0c 从而产生累积误