视觉SLAM十四讲笔记-10-1
文章目录
10 后端
10.1 滑动窗口滤波和优化
10.1.1 实际环境下的 BA 结构
带有相机位姿和空间点的图优化称为 BA,它能够有效地求解大规模的定位和建图问题。这在 SfM 问题中十分有用。但是在 SLAM 过程中,往往需要控制 BA 的规模,保持计算的实时性。在现实条件中,必须限制后端的计算时间。比如 BA 规模不能超过 1万个路标点,迭代不超过 20 次,用时不超过 0.5 秒等等。像 SfM 这样用一周时间重建一个城市地图的算法,在 SLAM 里不见得有效。
控制计算规模的做法有很多,比如从连续的视频中抽出一部分作为关键帧,仅构造关键帧与路标点之间的 BA,于是非关键帧只用于定位,对建图没有影贡献。但是即便如此,随着时间的推移,关键帧的数量会越来越多,地图规模将不断增长。像 BA 这样的批量优化方法,计算效率会不断下降。为了避免这种情况,需要一定的手段控制后端 BA 的规模。这些方法可以是理论上的,也可以是工程上的。
例如,最简单的控制 BA 规模的思路,是仅保留当前时刻最近的 N 个关键帧,去掉时间上更早的关键帧。于是, BA 将被固定在一个时间窗口内,离开这个窗口的则被丢掉。这种方法称为滑动窗口法。这种取 N 个关键帧的具体做方法可以有一些改变,例如不见得必须取时间上最近的,而按照某些原则,取时间上靠近,空间上又可以展开的关键帧,避免相机在停止不动时,BA 的结构会缩成一团。如果再考虑深一些,像 ORB-SLAM2 中,定义一种称为==“共视图” (Covisibility graph)==
的结构。所谓共视图,就是指那些“与现在的相机存在共同观测的关键帧构成的图”。
于是在 BA 优化时,按照某些原则在共视图内取一些关键帧和路标进行优化,仅优化与当前帧有 20 个以上共视路标的关键帧,其余部分固定不变。当共视图关系能够正确构造的时候,基于共视图的优化也会在更长的时间内保持最优。
滑动窗口也好,共视图也好,其实都是对实时计算的某种工程化的这种。不过在理论上也引入了一个新问题:刚才提到只用关键帧的信息,那么对于非关键帧的信息,那么对于非关键帧的数据该如何处理尼?接下来会仔细讨论。
10.1.2 滑动窗口法
考虑一个滑动窗口。假设这个窗口有
N
N
N 个关键帧,它们的位姿表达为:
x
1
,
x
2
,
.
.
.
,
x
N
x_1,x_2,...,x_N
x1,x2,...,xN
假设它们在向量空间可以用李代数表达,那么关于这几个关键帧,能谈论些什么尼?
显然,关心这几个关键帧的位置在哪里,以及它们的不确定度如何,这对应着它们在高斯分布假设下的均值协方差。
如果这几个关键帧还对应着一个局部地图,则可以顺带着问整个局部系统的均值和方差应该是多少。设这个滑动窗口中还有 M 个路标点:
y
1
,
y
2
,
.
.
.
,
y
M
y_1,y_2,...,y_M
y1,y2,...,yM,它们与
N
N
N 个关键帧组成了局部地图。显然可以用上一章的 BA 方法处理这个滑动窗口,包括建立图优化模型,构建整体的 Hessian 矩阵,然后边缘化所有路标点来加速求解。
在边缘化时,考虑关键帧的位姿,即:
[
x
1
,
x
2
,
.
.
.
,
x
N
]
T
∼
N
(
[
μ
1
,
μ
2
,
.
.
.
,
μ
N
]
,
Σ
)
[x_1,x_2,...,x_N]^T\sim N([\mu_1, \mu_2,...,\mu_N],\Sigma)
[x1,x2,...,xN]T∼N([μ1,μ2,...,μN],Σ)
其中
μ
k
\mu_k
μk 为第
k
k
k 个关键帧的位姿均值,
Σ
\Sigma
Σ 为所有关键帧的协方差矩阵。那么显然,均值部分就是指 BA 迭代之后的结果,而
Σ
\Sigma
Σ 就是对整个 BA 的
H
H
H 矩阵进行边缘化之后的结果,即对
H
H
H 矩阵进行舒尔消元后的
S
S
S。
在滑动窗口中,当窗口结构发生改变,这些状态变量应该如何变化?这件事情可以分为两部分讨论:
1.需要在窗口中新增一个关键帧,以及它观测到的路标点。
2.需要把窗口中一个旧的关键帧删除,也可能删除它观测的路标点。
这时,滑动窗口法和传统的 BA 的区别就显现出来了。
在传统的 BA 求解中,这仅仅对应于两个不同结构的 BA,在求解上没有任何区别。但在滑动窗口的情况下,就需要讨论一些具体的细节问题了。
新增一个关键帧和路标点
考虑在上一个时刻,滑动窗口已经建立了
N
N
N 个关键帧,也已知道它们服从某个高斯分布,其均值和方差如前所述。此时,新来了一个关键帧
x
N
+
1
x_{N+1}
xN+1,那么整个问题中的变量变为
N
+
1
N+1
N+1 个关键帧和更多路标点的集合。
删除一个旧的关键帧
当考虑删除旧关键帧时,一个理论问题将显现出来。例如,要删除旧关键帧
x
1
x_1
x1,但是
x
1
x_1
x1 并不是孤立的,他会和其他帧观测到同样的路标。将
x
1
x_1
x1 边缘化之后将导致整个问题不再稀疏。
在上图这个例子中,假设
x
1
x_1
x1 看到了路标点
y
1
y_1
y1 到
y
4
y_4
y4,于是在处理之前, BA 问题的 Hesian 矩阵应该像上图中左侧一样,在
x
1
x_1
x1 行的
y
1
y_1
y1 到
y
4
y_4
y4 列存在非零块,表示
x
1
x_1
x1 看到了它们。这时考虑边缘化
x
1
x_1
x1 ,那么舒尔消元(Schur) 过程中相当于通过矩阵行和列操作消去非对角线处几个非零矩阵块,显然这将导致右下角的路标点矩阵块不再是非对角矩阵。这个过程称为边缘化中的填入(Fill in)。
在第九章中介绍了边缘化,当边缘化路标点时, Fill in 将出现左上角的位姿块中。不过,因为 BA 不要求位姿块为对角块,所以稀疏 BA 求解仍然可行。但是,当边缘化关键帧时,将破坏右下角路标点之间的对角块结构,这时 BA 就无法按照先前的稀疏方式迭代求解。这显然是个十分糟糕的问题。实际上,在早期的 EKF 滤波器后端中,人们确实保持着一个稠密的 Hessian 矩阵,这也使得 EKF 后端没法处理较大规模的滑动窗口。
不过,如果对边缘化的过程进行一些改造,也可以保持滑动窗口 BA 的稀疏性。例如,在边缘化某个旧的关键帧时,同时边缘化它观测到的路标点。这样,路标点的信息就会转换成剩下那些关键帧之间的共视信息,从而保持右下角部分的对角块结构。在某些 SLAM 框架中,边缘化策略会更复杂。例如在 OKVIS 中,会判断要边缘化的那个关键帧,它看到的路标点是否在最新的关键帧中仍可看到。如果不能就直接边缘化这个路标点;如果能,就丢弃被边缘化关键帧对这个路标点的观测,从而保持 BA 的稀疏性。
SWF中边缘化的直接解释
边缘化在概率上的意义就是指条件概率。所以,当边缘化某个关键帧,即"保持这个关键帧当前的估计值,求其他状态变量以这个关键帧为条件的条件概率"。所以,当某个关键帧被边缘化,它观测到的路标点就会产生一个“这些路标应该在哪里“的先验信息,从而影响其余部分的估计值。如果再边缘化这些路点值,那么它们的观测者将得到一个”观测它们的关键帧应该在哪里“的先验信息。
从数学上看,当边缘化某个关键帧,整个窗口中的状态变量的描述方式将从联合分布变为一个条件概率分布。以上面的例子来看,就是说:
p
(
x
1
,
…
x
4
,
y
1
,
…
y
6
)
=
p
(
x
2
,
…
,
x
4
,
y
1
,
…
y
6
∣
x
1
)
p
(
x
1
)
⏟
舍 去
p\left(\boldsymbol{x}_{1}, \ldots \boldsymbol{x}_{4}, \boldsymbol{y}_{1}, \ldots \boldsymbol{y}_{6}\right)=p\left(\boldsymbol{x}_{2}, \ldots, \boldsymbol{x}_{4}, \boldsymbol{y}_{1}, \ldots \boldsymbol{y}_{6} \mid \boldsymbol{x}_{1}\right) \underbrace{p\left(\boldsymbol{x}_{1}\right)}_{\text {舍 去 }}
p(x1,…x4,y1,…y6)=p(x2,…,x4,y1,…y6∣x1)舍 去
p(x1)
然后舍去被边缘化部分的信息。在变量被边缘化之后,在工程中就不应该再使用它。所以滑动窗口法比较适合 VO 系统,而不适合大规模建图的系统。
由于现在 g2o 和 Ceres 还未直接支持滑动窗口法中的边缘化操作,略去本节对应的实验部分。
10.2 位姿图
10.2.1 位姿图的意义
特征点在优化问题中占据了绝大部分。实际上,经过若干次观测之后,收敛的特征点位置变化很小,发散的外点则已被剔除。对收敛点再进行优化,似乎是有些费力不讨好的。因此,更倾向于在优化几次之后就把特征点固定住,只把它们看做位姿估计的约束,而不再实际地优化它们的位置估计。
沿着这个思路继续思考,会想到:是否能够完全不管路标而只管轨迹尼?完全可以构建一个只有轨迹的图优化,而位姿节点之间的边,可以由两个关键帧之间通过特征匹配之后得到的运动估计来给定初始值。不同的是,一旦初始估计完成,就不再优化那些路标点的位置,而只关心所有的相机位姿之间的联系。通过这种方式,省去了大量的特征点优化的计算,只保留了关键帧的轨迹,从而构建了所谓的位姿图(Pose Graph)。位姿图示意图如下图所示,当不再优化 BA 中的路标点,仅把它们看成对姿态节点的约束时,就得到了一个计算规模小很多的位姿图。
在 BA 中特征数量远大于位姿节点。一个关键帧往往关联了数百个关键点,而实时 BA 的最大计算规模,即利用稀疏性,在当前的主流 CPU 上一般也就是几万个点左右。这就限制了 SLAM 应用场景。所以,当机器人在更大范围的时间和空间中运动时,必须考虑一些解决方式:要么像滑动窗口法那样,丢弃一些历史数据;要么像位子图的做法一样,舍弃对路标点的优化,只保留 Pose 之间的边。此外,如果有额外测量 Pose 的传感器,那么位姿图也是一种常见的融合 Pose 测量的方法。
10.2.2 位姿图的优化
位姿图优化中的节点和边都是什么意思尼?这里的节点是相机位姿,以
T
1
T_1
T1,
T
2
T_2
T2,…,
T
n
T_n
Tn 来表达。而边,则是两个位姿节点之间相对运动的估计,该运动估计可以来自于特征点法或者直接法,也可以来自 GPS 或者 IMU 积分。无论通过哪种手段,假设估计了
T
i
T_i
Ti 和
T
j
T_j
Tj 之间的一个运动
Δ
T
i
j
\Delta T_{ij}
ΔTij。该运动可以由若干种表达方式,取比较自然的一种:
Δ
ξ
i
j
=
ξ
i
−
1
∘
ξ
j
=
l
n
(
T
i
−
1
T
j
)
∨
\Delta \xi_{ij} = \xi_i^{-1} \circ \xi_j = ln(T_i^{-1}T_j)^{\vee}
Δξij=ξi−1∘ξj=ln(Ti−1Tj)∨
或者按照李群的写法:
T
i
j
=
T
i
−
1
T
j
T_{ij} = T_{i}^{-1}T_{j}
Tij=Ti−1Tj
按照图优化的思路,实际中该等式不会精确的成立,因此设立最小二乘误差,然后和以往一样,讨论误差关于优化变量的导数。这里,把上式的
T
i
j
T_{ij}
Tij 移到等式右侧,构建误差
e
i
j
e_{ij}
eij:
e
i
j
=
Δ
ξ
i
j
l
n
(
T
i
j
−
1
T
i
−
1
T
j
)
∨
e_{ij} = \Delta \xi_{ij} ln(T_{ij}^{-1}T_i^{-1}T_j)^{\vee}
eij=Δξijln(Tij−1Ti−1Tj)∨
注意优化变量有两个:
ξ
i
\xi_i
ξi 和
ξ
j
\xi_j
ξj。因此,求
e
i
j
e_{ij}
eij 关于这两个变量的导数。按照李代数的求导方式,给
ξ
i
\xi_i
ξi 和
ξ
j
\xi_j
ξj 各一个扰动:
δ
ξ
i
\delta \xi_i
δξi 和
δ
ξ
j
\delta \xi_j
δξj。于是,误差变为:
e
^
i
j
=
ln
(
T
i
j
−
1
T
i
−
1
exp
(
(
−
δ
ξ
i
)
∧
)
exp
(
δ
ξ
j
∧
)
T
j
)
∨
\hat{\boldsymbol{e}}_{i j}=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \exp \left(\left(-\boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \exp \left(\delta \boldsymbol{\xi}_{j}^{\wedge}\right) \boldsymbol{T}_{j}\right)^{\vee}
e^ij=ln(Tij−1Ti−1exp((−δξi)∧)exp(δξj∧)Tj)∨
该式中两个扰动被夹在中间。为了利用 BCH 近似,希望将扰动项移至式子左侧或者右侧。
根据第四章课后习题 式子4.55(交换法则):
exp
(
ξ
∧
)
T
=
T
exp
(
(
Ad
(
T
−
1
)
ξ
)
∧
)
\exp \left(\boldsymbol{\xi}^{\wedge}\right) \boldsymbol{T}=\boldsymbol{T} \exp \left(\left(\operatorname{Ad}\left(\boldsymbol{T}^{-1}\right) \boldsymbol{\xi}\right)^{\wedge}\right)
exp(ξ∧)T=Texp((Ad(T−1)ξ)∧)
该式表明,通过引入一个伴随项,能够”交换“扰动项左右侧的
T
T
T。利用它,可以将扰动挪到最右,导出右乘形式的雅克比矩阵:
e
^
i
j
=
ln
(
T
i
j
−
1
T
i
−
1
exp
(
(
−
δ
ξ
i
)
∧
)
exp
(
δ
ξ
j
∧
)
T
j
)
∨
=
ln
(
T
i
j
−
1
T
i
−
1
T
j
exp
(
(
−
Ad
(
T
j
−
1
)
δ
ξ
i
)
∧
)
exp
(
(
Ad
(
T
j
−
1
)
δ
ξ
j
)
∧
)
∨
≈
ln
(
T
i
j
−
1
T
i
−
1
T
j
[
I
−
(
Ad
(
T
j
−
1
)
δ
ξ
i
)
∧
+
(
Ad
(
T
j
−
1
)
δ
ξ
j
)
∧
]
)
∨
≈
e
i
j
+
∂
e
i
j
∂
δ
ξ
i
δ
ξ
i
+
∂
e
i
j
∂
δ
ξ
j
δ
ξ
j
\begin{aligned} \hat{\boldsymbol{e}}_{i j} &=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \exp \left(\left(-\boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \exp \left(\delta \boldsymbol{\xi}_{j}^{\wedge}\right) \boldsymbol{T}_{j}\right)^{\vee} \\ &=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \boldsymbol{T}_{j} \exp \left(\left(-\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \exp \left(\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{j}\right)^{\wedge}\right)^{\vee}\right.\\ & \approx \ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \boldsymbol{T}_{j}\left[\boldsymbol{I}-\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}+\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{j}\right)^{\wedge}\right]\right)^{\vee} \\ & \approx \boldsymbol{e}_{i j}+\frac{\partial \boldsymbol{e}_{i j}}{\partial \delta \xi_{i}} \boldsymbol{\delta} \boldsymbol{\xi}_{i}+\frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{j}} \boldsymbol{\delta} \boldsymbol{\xi}_{j} \end{aligned}
e^ij=ln(Tij−1Ti−1exp((−δξi)∧)exp(δξj∧)Tj)∨=ln(Tij−1Ti−1Tjexp((−Ad(Tj−1)δξi)∧)exp((Ad(Tj−1)δξj)∧)∨≈ln(Tij−1Ti−1Tj[I−(Ad(Tj−1)δξi)∧+(Ad(Tj−1)δξj)∧])∨≈eij+∂δξi∂eijδξi+∂δξj∂eijδξj
因此,按照李代数上的求导法则,求出了误差关于两个位姿的雅克比矩阵。关于
T
i
T_i
Ti 的和关于
T
j
T_j
Tj 的:
∂
e
i
j
∂
δ
ξ
i
=
−
J
r
−
1
(
e
i
j
)
Ad
(
T
j
−
1
)
∂
e
i
j
∂
δ
ξ
j
=
J
r
−
1
(
e
i
j
)
Ad
(
T
j
−
1
)
\begin{aligned} \frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{i}} &=-\mathcal{J}_{r}^{-1}\left(\boldsymbol{e}_{i j}\right) \operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \\ \frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{j}} &=\mathcal{J}_{r}^{-1}\left(\boldsymbol{e}_{i j}\right) \operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \end{aligned}
∂δξi∂eij∂δξj∂eij=−Jr−1(eij)Ad(Tj−1)=Jr−1(eij)Ad(Tj−1)
由于 se(3) 上的雅克比矩阵
J
r
\mathcal{J}_{r}
Jr 形式过于复杂,通常取它们的近似。如果误差接近零,就可以设它们近似为
I
I
I 或者:
J
r
−
1
(
e
i
j
)
≈
I
+
1
2
[
ϕ
e
∧
ρ
e
∧
0
ϕ
e
∧
]
\mathcal{J}_{r}^{-1}\left(\boldsymbol{e}_{i j}\right) \approx \boldsymbol{I}+\frac{1}{2}\left[\begin{array}{cc} \phi_{e}^{\wedge} & \boldsymbol{\rho}_{e}^{\wedge} \\ 0 & \phi_{e}^{\wedge} \end{array}\right]
Jr−1(eij)≈I+21[ϕe∧0ρe∧ϕe∧]
理论上,即使在优化之后,由于每条边给定的观测数据并不一致,误差也不见得近似于零,所以简单地把这里的
J
r
\mathcal{J}_{r}
Jr 设置为
I
I
I 会有一定的损失。
了解雅克比求导后,剩下的部分就和普通的图优化一样了。简而言之,所有的位姿顶点和位姿—位姿边构成了一个图优化,本质上是一个最小二乘问题,优化变量为各个顶点的位姿,边来自于位姿观测约束。记
ε
\varepsilon
ε 为所有边的集合,那么总体目标函数为:
min
ξ
1
2
∑
i
,
j
∈
E
e
i
j
T
Σ
i
j
−
1
e
i
j
\min _{\boldsymbol{\xi}} \frac{1}{2} \sum_{i, j \in \mathcal{E}} \boldsymbol{e}_{i j}^{T} \boldsymbol{\Sigma}_{i j}^{-1} \boldsymbol{e}_{i j}
ξmin21i,j∈E∑eijTΣij−1eij
依然可以用高斯牛顿法、列文伯格-马夸尔特方法来求解此问题,除了用李代数表示优化位姿,别的都是相似的。根据以前的经验,可以用 Ceres 或者 g2o 进行求解,不必讨论详细过程。