视觉SLAM十四讲第十二讲笔记
这一讲关注回环检测。
我们知道SLAM主体(前端、后端)主要的目的在于估计相机的运动,而回环检测模块,是用于构建全局一致的轨迹和地图。举个例子:
因为前端给出相邻帧之间的估计,很难做到消除积累的误差。但是,回环检测模块,能够给出除了相邻帧之外的,一些时隔更加久远的约束:例如 x 1 − x 100 x_1 - x_{100} x1−x100之间的位姿变换。也就是说我们察觉到相机经过了同一个地方,采集到了相似的数据。而回环检测的关键,就是如何有效地检测出相机经过同一个地方这件事。 这样就能够给后端提供一个全局一致的估计。回环检测对于SLAM来说,关系到估计的轨迹和地图在长时间的正确性。另一方面,由于回环检测提供了当前数据与所有历史数据的关联,在跟踪算法丢失之后,我们还可以利用回环检测进行重定位。
方法有最朴素的方法,如对任意两张图像都做一遍特征匹配,检测是否存在回环,但这个方法计算量太大。第二个就是基于里程计的几何关系,就是当我们发现相机运动到了之前的某个位置附近的时候,检测是否有回环。但这个方法逻辑冲突,我们往往没法正确地发现“运动到了之前的某个位置附近”这件事实,回环检测也无从谈起。第三个方法,基于外观的回环检测算法中,核心问题是如何计算图像间的相似性。
图像的相似性 可以不可以直接矩阵相减,然后取某种范数呢?这个方法存在两个问题:
- 像素灰度值是一种不稳定的测量值,光照,相机曝光等等,都会有很大的影响。
- 相机视角发生变化时,即使每个物体的光度不变,它们的像素也会在图像中发生位移,造成一个很大的差异值。
怎样的函数能够更好地反映相似关系,而怎样的函数不够好呢?——从这里可以引出**感知偏差(Perceptual Aliasing)和感知变异(Perceptual Variability)**两个概念。
一、准确率和召回率
Precision = T P / ( T P + F P ) , Recall = T P / ( T P + F N ) \text{Precision} = TP/(TP+FP), \quad \text{Recall} = TP/(TP+FN) Precision=TP/(TP+FP),Recall=TP/(TP+FN)
在SLAM中,对准确率要求更高,而对召回率则相对宽容一些。
二、词袋模型
词袋,也就是Bag-of-Words(BoW),目的是用“图像上有哪几种特征”来描述一个图像。通过字典和单词,只需一个向量就可以描述整张图像了。该向量描述的是“图像是否含有某类特征”的信息,比单纯的灰度值更加稳定。又因为描述向量说的是“ 是否出现 ”,而不管它们“在哪儿出现”,所以与物体的空间位置和排列顺序无关,因此在相机发生少量运动时,只要物体仍在视野中出现,我们就仍然保证描述向量不发生变化。于是,根据描述两张图像的向量
a
,
b
∈
R
w
a,b \in \R^w
a,b∈Rw,可以计算:
s
(
a
,
b
)
=
1
−
1
W
∣
∣
a
−
b
∣
∣
1
s(a,b) = 1 - \frac{1}{W}||a-b||_1
s(a,b)=1−W1∣∣a−b∣∣1
其中范数取 L 1 L_1 L1,即各元素绝对值之和。请注意在两个向量完全一样的时候,我们將得到1;完全相反的时候(a为0的地方b为1)得到0.这样就定义了两个描述向量的相似性。
三、字典
1. K-means
简单来说,当我们有 N N N个数据,想要归成 k k k个类别,那么用K-means来做,主要有以下几个步骤:
- 随机选取 k k k个中心点: c 1 , . . . , c k c_1,...,c_k c1,...,ck;
- 对每一个样本,计算与每个中心点之间的距离,取最小的作为它的归类;
- 重新计算每个类的中心点;
- 如果每个中心点都变化很小,则算法收敛,退出;否则返回1。
K-means的做法是朴素且简单有效的,不过也存在一些问题,例如需要指定聚类数量、随机选取中心点使得每次聚类结果都不相同以及一些效率上的问题。
根据K-means,我们可以把已经提取的大量特征点聚类成一个含有 k k k个单词的字典了。但字典数据很大,需要一个数据结构来保存。这里使用一种** k k k叉树来表达字典**。它的思路很简单,类似于层次聚类,是k-means的直接扩展。假定我们有 N N N个特征点,希望构建一个深度为 d d d,每次分叉为 k k k的树,那么做法如下:
- 在根节点上,用k-means把所有样本聚成 k k k类(实际中为保证聚类均匀性会使用k-means++)。这样得到了第一层。
- 对第一层的每个节点,把属于该节点的样本再聚类成 k k k类,得到下一层。
- 以此类推,最后得到叶子层。叶子层即所谓的Words。
2. 相似度计算
有了字典之后,给定任意特征 f i f_i fi,只要在字典树中逐层查找,最后都能找到与之对应的单词 w j w_j wj。那么假设一张图像中,提取了 N N N个特征,找到这 N N N个特征对应的单词之后,我们相当于拥有了该图像在单词列表中的分布,或者直方图。考虑到,不同的单词在区分性上的重要性并不用,因此我们希望对单词的区分性或重要性加以评估,给他们不同的权值以起到更好的效果。常用的做法是TF-IDF(Term Frequency-Inverse Document Frequency)。TF部分的思想是,某单词在一个图像中经常出现,它的区分度就高。另一方面,IDF的思想是,某个单词在字典中出现的频率越低,则分类图像时区分度越高。
在词袋模型中,在建立字典时候可以考虑IDF部分。我们统计某个叶子节点
w
i
w_i
wi中的特征数量相对于所有特征数量的比例,作为IDF部分。假设所有特征数量为
n
n
n,
w
i
w_i
wi数量为
n
i
n_i
ni,那么该单词的IDF为:
I
D
F
i
=
l
o
g
n
n
i
IDF_i = log\frac{n}{n_i}
IDFi=lognin
另一方面,TF部分则是指某个特征在单个图像中出现的频率。假设图像A中,单词
w
i
w_i
wi出现了
n
i
n_i
ni次,而一共出现的单词次数为
n
n
n,那么TF为:
T
F
i
=
n
i
n
TF_i = \frac{n_i}{n}
TFi=nni
于是
w
i
w_i
wi的权重等于TF乘IDF之积:
η
i
=
T
F
i
×
I
D
F
i
\eta_i = TF_i \times IDF_i
ηi=TFi×IDFi
考虑权重以后,对于某个图像A,它的特征点可对应到许多个单词,组成它的Bag-of-Words:
A
=
{
(
w
1
,
η
1
)
,
(
w
2
,
η
2
)
,
.
.
.
,
(
w
N
,
η
N
)
}
≜
v
A
A= \{(w_1, \eta_1),(w_2,\eta_2),...,(w_N,\eta_N)\} \triangleq v_A
A={(w1,η1),(w2,η2),...,(wN,ηN)}≜vA
由于相似的特征可能落到同一类中,因此实际的 v A v_A vA中会存在大量的零。无论如何,通过词袋,我们用单个向量 v A v_A vA描述了一个图像A。这个向量 v A v_A vA是一个稀疏的向量,它的非零部分指示出图像 A A A中含有哪些单词,而这些部分的值为 T F − I D F TF-IDF TF−IDF的值。
接下来的问题是:给定 v A v_A vA和 v B v_B vB,如何计算它们的差异呢?
s ( v A − v B ) = 2 ∑ i = 1 N ∣ v A i ∣ + ∣ v B i ∣ − ∣ v A i − v B i ∣ s(v_A- v_B) = 2\sum_{i=1}^N |v_{A_i}| + |v_{B_i}| - |v_{A_i}-v_{B_i}| s(vA−vB)=2i=1∑N∣vAi∣+∣vBi∣−∣vAi−vBi∣
对任意两个图像,我们都能给出一个相似性评分,但是只利用这个分值的绝对大小,并不一定有很好的帮助。 考虑到这种情况,我们会取一个先验相似度
s
(
v
t
,
v
t
−
Δ
t
)
s(v_t,v_{t-\Delta t})
s(vt,vt−Δt),它表示某时刻关键帧图像与上一时刻的关键帧的相似性。然后,其他的分值都参照这个值进行归一化:
s
(
v
t
,
v
t
j
)
′
=
s
(
v
t
,
v
t
j
)
/
s
(
v
t
,
v
t
−
Δ
t
)
s(v_t, v_{t_j})'=s(v_t,v_{t_j})/s(v_t,v_{t-\Delta t})
s(vt,vtj)′=s(vt,vtj)/s(vt,vt−Δt)
站在这个角度上,我们说:如果当前帧与之前某关键帧的相似度,超过当前帧与上一个关键帧相似度的3倍,就认为可能存在回环。
四、关键帧的处理
如果关键帧选得太近,那么导致两个关键帧之间的相似性过高,相比之下不容易检测出历史数据中的回环。所以从实践上说,用于回环检测的帧最好是稀疏一些,彼此之间不太相同,又能涵盖整个环境。另一方面,如果成功检测到了回环,比如说出现在第1帧和第n帧。那么很可能第n+1帧,n+2帧都会和第1帧构成回环。但是,确认第1帧和第n帧之间存在回环,对轨迹优化是有帮助的,但再接下去的第n+1帧,n+2帧都会和第1帧构成回环,产生的帮助就没那么大了,因为我们已经用之前的信息消除了累计误差,更多的回环并不会带来更多的信息。所以,我们会把“相近”的回环聚成一类,使算法不要反复地检测同一类的回环。
五、 检测之后的验证
词袋的回环检测算法完全依赖于外观而没有利用任何的几何信息,这导致外观相似的图像容易被当成回环。并且,由于词袋不在乎单词顺序,只在意单词有无的表达方式,更容易引发感知偏差。所以,在回环检测之后,我们通常还会有一个验证步骤。验证的方法有很多。其一是设立回环的缓存机制,认为单次检测到的回环并不足以构成良好的约束,而在一段时间中一直检测到的回环,才认为是正确的回环。这可以看成时间上的一致性检测。另一方法是空间上的一致性检测,即是对回环检测到的两个帧进行特征匹配,估计相机的运动。然后,再把运动放到之前的Pose Graph中,检查与之前的估计是否有很大的出入。总之,验证部分通常是必须的。