写在前面
最近看了一下SVO的论文,发现原来在N年前就已经有这么优秀的VO了,甚是惭愧,因此发奋总结了其论文中的要点,有些问题先埋着,需要看完代码进行补充。
SVO的框架结构
SVO的整体框架如下图,可以看到整个SVO的结构是由两个线程构成的,第一个线程有三个主要的操作,a. Sparse Model-based Image Alignment; b. Feature Alignment; c. Pose&Structure Refinement。第二个线程主要是map的过程,一旦有一帧被定义为keyframe,就提取其中的feature,并初始化深度滤波器,如果不是keyframe,则一直更新深度滤波器,如果某个feature的深度收敛了,则生成3D点放入Map中作为landmark
Motion Estimate Thread
该线程中主要求解当前帧的位姿(Sparse Model-based Image Alignment; Feature Alignment)和local BA(Pose&Structure Refinement)
Sparse Model-based Image Alignment
该部分类似于光流,不过求解的不是对应关系,而是相对位姿。
其优化函数为:
T
k
−
1
k
=
a
r
g
m
i
n
∬
ℜ
ρ
[
δ
I
(
T
,
u
)
]
d
u
T_{k-1}^k = argmin\iint_{\Re}{\rho[\delta I(T,u)]}du
Tk−1k=argmin∬ℜρ[δI(T,u)]du
其中:
- ρ [ ∗ ] = 0.5 ∥ ∗ ∥ 2 \rho[*]=0.5\lVert*\rVert^2 ρ[∗]=0.5∥∗∥2,可见整个过程是一个最小二乘法的问题;
- δ I ( T , u ) = I k ( π ( T π − 1 ( u , d u ) ) ) − I k − 1 ( u ) \delta I(T,u)=I_k(\pi (T\pi^{-1}(u, du)))-I_{k-1}(u) δI(T,u)=Ik(π(Tπ−1(u,du)))−Ik−1(u),这个残差对比的是图像位置上的像素值的灰度,其中 u ∈ ℜ , ℜ u\in\Re, \Re u∈ℜ,ℜ表示即可以在k-1帧图像上看到,又可以通过投影在k帧上看到
- 对于比对灰度值的算法来说,一般都会用一个patch size中的全部像素的灰度进行对比,因此下面的公式中都不仅仅使用一点像素,而是使用多点像素进行求解的,但是在这个过程中,作者为了提高计算的速度,并没有进行patch的投影,算是以量取胜吧
把上述的notation带入到优化函数中就可以得到
T
k
−
1
k
=
a
r
g
m
i
n
∑
i
∈
ℜ
1
2
∥
δ
I
(
T
k
−
1
k
,
u
i
)
∥
2
(
1
)
T_{k-1}^k = argmin\sum_{i \in \Re}{\frac{1}{2}\lVert \delta I(T_{k-1}^k,u_i)\rVert^2} \quad (1)
Tk−1k=argmini∈ℜ∑21∥δI(Tk−1k,ui)∥2(1)
对于该方法来说,因为假设的前提是帧与帧之间的motion较小,且光强不变,因此论文中该处将当前帧的位姿设为了和上一帧一样的位姿,也就是说初始的相对位姿为0,而优化函数求解的就是扰动,基于这样的思路,对
δ
I
(
T
k
−
1
k
,
u
i
)
\delta I(T_{k-1}^k,u_i)
δI(Tk−1k,ui)进行一阶展开
δ
I
(
ζ
,
u
i
)
=
I
k
(
π
(
T
k
−
1
k
∗
p
i
)
)
−
I
k
−
1
(
π
(
T
(
ζ
)
∗
p
i
)
)
(
2
)
δ
I
(
ζ
,
u
i
)
=
δ
I
(
0
,
u
i
)
+
d
δ
I
(
0
,
u
i
)
d
ζ
ζ
(
3
)
d
δ
I
(
0
,
u
i
)
d
ζ
=
−
I
k
−
1
(
a
)
d
a
×
d
a
d
b
×
d
b
d
T
(
ζ
)
×
d
T
(
ζ
)
d
ζ
N
o
t
a
t
i
o
n
:
a
=
π
(
T
(
ζ
)
∗
p
i
)
b
=
T
(
ζ
)
∗
p
i
\delta I(\zeta, ui)=I_k(\pi (T_{k-1}^k*pi))-I_{k-1}(\pi(T(\zeta)*pi)) \quad (2)\\ \delta I(\zeta, ui)=\delta I(0, ui)+\frac{d\delta I(0, ui)}{d\zeta}\zeta \quad\quad\quad\quad\quad\quad (3)\\ \frac{d\delta I(0, ui)}{d\zeta}=\frac{-I_{k-1}(a)}{da}×\frac{da}{db}×\frac{db}{dT(\zeta)}×\frac{dT(\zeta)}{d\zeta} \\ Notation: a=\pi(T(\zeta)*pi) \quad b=T(\zeta)*pi
δI(ζ,ui)=Ik(π(Tk−1k∗pi))−Ik−1(π(T(ζ)∗pi))(2)δI(ζ,ui)=δI(0,ui)+dζdδI(0,ui)ζ(3)dζdδI(0,ui)=da−Ik−1(a)×dbda×dT(ζ)db×dζdT(ζ)Notation:a=π(T(ζ)∗pi)b=T(ζ)∗pi
此处,
T
0
=
I
,
T
(
ζ
)
T_0=I, T(\zeta)
T0=I,T(ζ)是最终我们优化的变量,这里并没有采用直接的求解位姿的方法,而是采用inverse composition的方法,即
T
k
−
1
k
=
T
k
−
1
k
T
(
ζ
)
−
1
T_{k-1}^k=T_{k-1}^kT(\zeta)^{-1}
Tk−1k=Tk−1kT(ζ)−1,可以看到随着优化的进行,
T
(
ζ
)
T(\zeta)
T(ζ)越来越小,
T
k
−
1
k
T_{k-1}^k
Tk−1k从0趋近于正确的值。(这里埋一个问题,为什么不用直接的更新方法而使用这样的更新方法呢?)
对于公式(1),很明显为了求解这样的最小二乘问题,只需要使得其对于
ζ
\zeta
ζ的导数为0就可以了,因此其导数为
∑
i
∈
ℜ
δ
I
(
ζ
,
u
i
)
T
∇
δ
I
(
ζ
,
u
i
)
=
(
δ
I
(
0
,
u
i
)
+
d
δ
I
(
0
,
u
i
)
d
ζ
ζ
)
T
∗
d
δ
I
(
0
,
u
i
)
d
ζ
=
>
J
T
J
ζ
=
−
J
T
b
(
4
)
N
o
t
a
t
i
o
n
:
J
=
d
δ
I
(
0
,
u
i
)
d
ζ
b
=
δ
I
(
0
,
u
i
)
\sum_{i \in \Re} \delta I(\zeta, ui)^T\nabla \delta I(\zeta, ui)=(\delta I(0, ui)+\frac{d\delta I(0, ui)}{d\zeta}\zeta)^T*\frac{d\delta I(0, ui)}{d\zeta}=>J^TJ\zeta=-J^Tb \quad (4) \\ Notation: J = \frac{d\delta I(0, ui)}{d\zeta} \quad b=\delta I(0, ui)
i∈ℜ∑δI(ζ,ui)T∇δI(ζ,ui)=(δI(0,ui)+dζdδI(0,ui)ζ)T∗dζdδI(0,ui)=>JTJζ=−JTb(4)Notation:J=dζdδI(0,ui)b=δI(0,ui)
可以看到,直接法求解过程中整个计算量还是很小的,一个图像梯度,一个2D对3D求导,一个SE(3)的扰动模型
论文给出上图说明上述的过程
Feature Alignment
在上一步骤中,我们获得了一个比较粗略(coarse)的相对位姿,虽然粗略,但是也基本上是得到了位姿和跟踪到的点了(这些点都是上一步骤求解出位姿之后通过映射关系映射过来的),因为位姿是粗略的,所以得到的这些映射的点的位置(
u
′
u'
u′)也是粗略的,所以该步骤就是使用参考帧上的点修正一下这些粗略的点的位置,论文中称为Feature Alignment
首先是优化函数
u
i
′
=
a
r
g
m
i
n
1
2
∥
I
k
(
u
i
′
)
−
A
i
I
r
(
u
i
)
∥
2
u_i'=argmin\frac{1}{2}\lVert I_k(u_i')-A_i I_r(u_i) \rVert^2
ui′=argmin21∥Ik(ui′)−AiIr(ui)∥2
其中由于参考帧距离当前帧比较远,因此中间需要一个仿射变换把当前帧的图像状态与参考帧的图像状态变得一致。
鉴于这个的优化比较简单,导数就是一个梯度,因此这里不进行展开了。
论文给出上图说明上述的过程
Pose&Structure Refinement
经过上述的两个过程,论文描述基本上亚像素的误差平均在0.3左右,接下来的这一步中,SVO依旧进行位姿的优化,优化函数如下:
T
w
k
=
a
r
g
m
i
n
1
2
∑
i
∥
u
i
−
π
(
T
w
k
,
p
i
)
∥
2
T_w^k=argmin\frac{1}{2}\sum_i \lVert u_i-\pi(T_w^k, pi) \rVert^2
Twk=argmin21i∑∥ui−π(Twk,pi)∥2
这个部分就是一个BA,不过论文也说了,最后一步也会有一个local BA,修正位姿和3D点
论文给出上图表示上述的过程
Discussion
论文讨论了该方法中的前几个步骤,讨论了如果使用传统的LK光流法的话,时间会增加,同时较大距离的跟踪需要较大的patch以及金字塔的应用,同时因为会存在跟丢的情况,因此需要外点检测,而在SVO的方法中,因为有feature alignment的步骤,使得特征点的位置还是比较准确的,因此能保证没有外点
Mapping Thread
该线程中主要的工作就是判断当前帧是否可以作为key frame,之后进行深度滤波器的迭代以及初始化工作
下面主要说明深度滤波器的相关推导
depth-filter
深度滤波器主要是使用贝叶斯公式进行特征点深度的迭代推导,如下图所示
在深度滤波器中,论文把真实深度的分布看做一个正态分布+均匀分布的模型
p
(
x
∣
Z
,
π
)
=
π
N
(
x
∣
Z
,
τ
2
)
+
(
1
−
π
)
U
(
x
)
(
5
)
p(x|Z,\pi)=\pi N(x|Z,\tau^2)+(1-\pi)U(x) \qquad (5)
p(x∣Z,π)=πN(x∣Z,τ2)+(1−π)U(x)(5)
其中
π
\pi
π表示该次测量为有效的概率。
1. 近似表示后验分布
可以看到上面的式子其实还是很难去看本次测量到底是说与有效还是无效的,因此论文引入了一个变量
y
n
y_n
yn,这个变量是一个二值的变量,为1时表示深度满足高斯分布,为0时说明深度服从均匀分布,说明真实的深度在一个范围内都可能存在,于是上式变为
{
p
(
x
n
∣
Z
,
π
,
y
n
)
=
N
(
x
n
∣
Z
,
τ
2
)
y
n
U
(
x
n
)
1
−
y
n
p
(
y
n
∣
π
)
=
π
y
n
(
1
−
π
)
1
−
y
n
\begin{cases} p(x_n|Z,\pi,y_n)=N(x_n|Z,\tau^2)^{y_n}U(x_n)^{1-y_n} \\ p(y_n|\pi)=\pi^{y_n}(1-\pi)^{1-y_n} \end{cases}
{p(xn∣Z,π,yn)=N(xn∣Z,τ2)ynU(xn)1−ynp(yn∣π)=πyn(1−π)1−yn
设
X
=
{
x
1
,
x
2
,
.
.
.
,
x
n
}
X=\{x_1, x_2, ..., x_n\}
X={x1,x2,...,xn},
Y
=
{
y
1
,
y
2
,
.
.
.
,
y
n
}
Y=\{y_1, y_2,...,y_n\}
Y={y1,y2,...,yn},则所有变量的联合分布如下
p
(
X
,
Y
,
Z
,
π
)
=
p
(
X
,
Y
∣
Z
,
π
)
p
(
Z
,
π
)
=
p
(
X
∣
Y
,
Z
,
π
)
p
(
Y
∣
Z
,
π
)
p
(
Z
,
π
)
=
p
(
X
∣
Y
,
Z
,
π
)
p
(
Y
∣
π
)
p
(
Z
)
p
(
π
)
=
>
p
(
X
,
Y
,
Z
,
π
)
=
[
∏
n
=
1
N
p
(
x
n
∣
y
n
,
Z
,
π
)
p
(
y
n
∣
π
)
]
p
(
z
)
p
(
π
)
(
6
)
p(X, Y, Z, \pi)=p(X,Y|Z,\pi)p(Z, \pi)=p(X|Y, Z, \pi)p(Y|Z, \pi)p(Z, \pi)=p(X|Y, Z, \pi)p(Y|\pi)p(Z)p(\pi) \\ => p(X, Y, Z, \pi)=[\prod_{n=1}^{N}p(x_n|y_n, Z, \pi)p(y_n|\pi)]p(z)p(\pi) \qquad (6)
p(X,Y,Z,π)=p(X,Y∣Z,π)p(Z,π)=p(X∣Y,Z,π)p(Y∣Z,π)p(Z,π)=p(X∣Y,Z,π)p(Y∣π)p(Z)p(π)=>p(X,Y,Z,π)=[n=1∏Np(xn∣yn,Z,π)p(yn∣π)]p(z)p(π)(6)
上式中因为Z和Y,
π
\pi
π没有直接的关系,因此满足独立分布。
由于我们并不知道真实的分布
p
(
y
n
,
Z
,
π
∣
x
n
)
p(y_n,Z,\pi|x_n)
p(yn,Z,π∣xn),我们使用近似的方法对真实的分布进行近似,假如我们要近似的分布为
q
(
y
n
,
Z
,
π
)
q(y_n,Z,\pi)
q(yn,Z,π),此时论文做出如下假设
q
(
Y
,
Z
,
π
)
=
q
Y
(
Y
)
q
Z
,
π
(
Z
,
π
)
q(Y,Z,\pi)=q_Y(Y)q_{Z,\pi}(Z,\pi)
q(Y,Z,π)=qY(Y)qZ,π(Z,π),当然这里我认为并不是因为他们是相互独立的,而是通过边缘化的手段得到的。为什么要经过这样的变换呢?回到我们最初的想法,我们最终要求解的是
p
(
Z
,
π
)
p(Z,\pi)
p(Z,π),即使我们通过别的分布来近似,我们最终要求的实际上也必须是
q
(
Z
,
π
)
q(Z,\pi)
q(Z,π),所以可以看到,这个公式主要的作用其实是把Y给边缘化出去了,保留下来了我们真正需要求的东西了。
回到正题,我们做这一步是为了干什么呢?其实我们现在假设了一个分布q(),但是我们并不知道这个分布到底是什么-_-,下面我们就来求这个神秘的分布。
根据熵的原理,如果我们的近似分布q()与p()真的是相似的,那么他们的熵必然也是差不多的,有
l
n
q
(
)
=
l
n
p
(
)
lnq()=lnp()
lnq()=lnp(),所以
l
n
q
(
Z
,
π
,
Y
∣
X
)
=
l
n
P
(
Z
,
π
,
Y
∣
X
)
=
l
n
(
P
(
Z
,
π
,
Y
,
X
)
P
(
X
)
)
=
l
n
(
P
(
Z
,
π
,
Y
,
X
)
)
+
c
o
n
s
t
(
7
)
lnq(Z,\pi, Y|X)=lnP(Z,\pi,Y|X)=ln(\frac{P(Z,\pi,Y,X)}{P(X)})=ln(P(Z,\pi,Y,X))+const \quad (7)
lnq(Z,π,Y∣X)=lnP(Z,π,Y∣X)=ln(P(X)P(Z,π,Y,X))=ln(P(Z,π,Y,X))+const(7)
正如上面的推论所述,我们近似的分布q()与联合分布
p
(
Z
,
π
,
Y
,
X
)
p(Z,\pi,Y,X)
p(Z,π,Y,X)有直接的关系,此时,我们如果把公式(7)中的Y边缘化掉,那么我们就可以很清楚的得到两者之间的关系了,即有下面的公式
l
n
q
Z
,
π
(
Z
,
π
,
Y
∣
X
)
=
E
y
[
l
n
(
P
(
Z
,
π
,
Y
,
X
)
)
]
+
c
o
n
s
t
(
8
)
lnq_{Z,\pi}(Z,\pi, Y|X)=E_y[ln(P(Z,\pi,Y,X))]+const \qquad (8)
lnqZ,π(Z,π,Y∣X)=Ey[ln(P(Z,π,Y,X))]+const(8)
比较幸运的是,在公式(8)中,我们可以很容易的就看到了p()是公式(6),因此全部带入之后,就可以得到论文中的公式
随后再把ln去掉,就得到
作者认为这样的形式是符合高斯分布×Beta分布的
2. 近似后验的迭代公式
通过第一步,我们知道了我们近似的后验长什么样子,那现实的问题是,如何更新近似的后验q()呢?
这时候就要用到贝叶斯公式了(虽然上面已经用了多次贝叶斯了)
p
(
Z
,
π
∣
X
)
=
p
(
X
∣
Z
,
π
)
p
(
Z
,
π
)
=
>
q
‾
(
Z
,
π
)
=
p
(
X
∣
Z
,
π
)
q
(
Z
,
π
)
(
9
)
p(Z,\pi|X)=p(X|Z,\pi)p(Z,\pi) =>\overline q(Z,\pi)=p(X|Z,\pi)q(Z,\pi) \qquad (9)
p(Z,π∣X)=p(X∣Z,π)p(Z,π)=>q(Z,π)=p(X∣Z,π)q(Z,π)(9)
将公式(5)和高斯×Beta分布带入之后就可以得到迭代公式
q
(
Z
,
π
)
=
[
π
N
(
x
∣
Z
,
τ
2
)
+
(
1
−
π
)
U
(
x
)
]
N
(
Z
∣
μ
,
σ
2
)
B
e
t
a
(
π
∣
a
,
b
)
=
π
N
(
x
∣
Z
,
τ
2
)
N
(
Z
∣
μ
,
σ
2
)
B
e
t
a
(
π
∣
a
,
b
)
+
(
1
−
π
)
U
(
x
)
N
(
Z
∣
μ
,
σ
2
)
B
e
t
a
(
π
∣
a
,
b
)
q(Z,\pi)=[\pi N(x|Z, \tau^2)+(1-\pi)U(x)]N(Z|\mu, \sigma^2)Beta(\pi|a,b)\\=\pi N(x|Z, \tau^2)N(Z|\mu, \sigma^2)Beta(\pi|a,b)+(1-\pi)U(x)N(Z|\mu, \sigma^2)Beta(\pi|a,b)
q(Z,π)=[πN(x∣Z,τ2)+(1−π)U(x)]N(Z∣μ,σ2)Beta(π∣a,b)=πN(x∣Z,τ2)N(Z∣μ,σ2)Beta(π∣a,b)+(1−π)U(x)N(Z∣μ,σ2)Beta(π∣a,b)
推导到上面的式子之后其实并不算完成,因为Beta分布依旧是原来的参数,这时我们可以看到一种极端的情况是,如果当前的测量是一个好的测量,那么加号前面的部分就会当做后验,反之,后面就会作为后验,所以我们需要将前面的Beta(a,b)分布变为Beta(a+1,b),后面的Beta(a,b)变为Beta(a, b+1)。
根据Beta分布的性质,很容易得到转换公式:
B
e
t
a
(
a
+
1
,
b
)
=
(
a
+
b
)
a
π
B
e
t
a
(
a
,
b
)
a
n
d
B
e
t
a
(
a
,
b
+
1
)
=
(
a
+
b
)
b
(
1
−
π
)
B
e
t
a
(
a
,
b
)
Beta(a+1,b)=\frac{(a+b)}{a}\pi Beta(a,b) \quad and \quad Beta(a,b+1)=\frac{(a+b)}{b}(1-\pi)Beta(a,b)
Beta(a+1,b)=a(a+b)πBeta(a,b)andBeta(a,b+1)=b(a+b)(1−π)Beta(a,b)
带入上面公式之后再经过一个特别繁琐的化简之后,就能得到如下的迭代公式
q
(
Z
,
π
)
=
a
(
a
+
b
)
N
(
x
∣
μ
,
τ
2
+
σ
2
)
N
(
Z
∣
m
,
s
2
)
B
e
t
a
(
a
+
1
,
b
)
+
b
(
a
+
b
)
U
(
x
)
N
(
Z
∣
μ
,
σ
2
)
B
e
t
a
(
a
,
b
+
1
)
=
>
N
(
Z
∣
μ
′
,
τ
′
2
)
B
e
t
a
(
a
′
,
b
′
)
=
C
1
N
(
Z
∣
m
,
s
2
)
B
e
t
a
(
a
+
1
,
b
)
+
C
2
N
(
Z
∣
μ
,
σ
2
)
B
e
t
a
(
a
,
b
+
1
)
q(Z,\pi)=\frac{a}{(a+b)}N(x|\mu,\tau^2+\sigma^2)N(Z|m, s^2)Beta(a+1, b)+\frac{b}{(a+b)}U(x)N(Z|\mu, \sigma^2)Beta(a, b+1) \\ =>N(Z|\mu', \tau'^2)Beta(a', b')=C_1N(Z|m, s^2)Beta(a+1, b)+C_2N(Z|\mu, \sigma^2)Beta(a, b+1)
q(Z,π)=(a+b)aN(x∣μ,τ2+σ2)N(Z∣m,s2)Beta(a+1,b)+(a+b)bU(x)N(Z∣μ,σ2)Beta(a,b+1)=>N(Z∣μ′,τ′2)Beta(a′,b′)=C1N(Z∣m,s2)Beta(a+1,b)+C2N(Z∣μ,σ2)Beta(a,b+1)
其实剩下的就是艰苦卓绝的化简工作,这里我只推了一下高斯分布的,beta分布实在太麻烦了,因此直接上结论
{
1
s
2
=
1
σ
2
+
1
τ
2
m
=
s
2
(
μ
σ
2
+
x
τ
2
)
μ
′
=
C
1
m
+
C
2
μ
σ
′
2
+
τ
′
2
=
C
1
(
s
2
+
m
2
)
+
C
2
(
σ
2
+
μ
2
)
a
′
a
′
+
b
′
=
C
1
a
+
1
a
+
b
+
1
+
C
2
a
a
+
b
+
1
a
′
(
a
′
+
1
)
(
a
′
+
b
′
)
(
a
′
+
b
′
+
1
)
=
C
1
(
a
+
1
)
(
a
+
2
)
(
a
+
b
+
1
)
(
a
+
b
+
2
)
+
C
2
a
(
a
+
1
)
(
a
+
b
+
1
)
(
a
+
b
+
2
)
\begin{cases} \frac{1}{s^2}=\frac{1}{\sigma^2}+\frac{1}{\tau^2} \\ m=s^2(\frac{\mu}{\sigma^2}+\frac{x}{\tau^2}) \\ \mu'=C_1m+C_2\mu \\ \sigma'^2+\tau'^2=C_1(s^2+m^2)+C_2(\sigma^2+\mu^2) \\ \frac{a'}{a'+b'}=C_1\frac{a+1}{a+b+1}+C_2\frac{a}{a+b+1} \\ \frac{a'(a'+1)}{(a'+b')(a'+b'+1)}=C_1\frac{(a+1)(a+2)}{(a+b+1)(a+b+2)}+C_2\frac{a(a+1)}{(a+b+1)(a+b+2)} \\ \end{cases}
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧s21=σ21+τ21m=s2(σ2μ+τ2x)μ′=C1m+C2μσ′2+τ′2=C1(s2+m2)+C2(σ2+μ2)a′+b′a′=C1a+b+1a+1+C2a+b+1a(a′+b′)(a′+b′+1)a′(a′+1)=C1(a+b+1)(a+b+2)(a+1)(a+2)+C2(a+b+1)(a+b+2)a(a+1)
OK,上面的公式就是新旧状态之间的更新公式了,其中a,b是内点外点的检测次数(这个有待看代码是如何判断的),
μ
,
σ
\mu, \sigma
μ,σ是深度滤波器初始化时候的参数,而
τ
\tau
τ论文中说是根据根据测量时刻的两个相机的相对位置产生的,个人感觉是位置越远,
τ
\tau
τ越小,因为基线长。
总结
可以从论文看出来整个SVO的框架还是很清晰的,上述有一些参数或者实现具体还要在代码中查看,以后有时间看完代码之后再来进行补充~加油加油