主要代码:
g2o/types/sba/types_six_dof_expmap.h
g2o/types/sba/types_six_dof_expmap.cpp
基本结构
SE3Quat
包括四元数和vector3
相机位姿顶点
VertexSE3Expmap::VertexSE3Expmap() : BaseVertex<6, SE3Quat>() {
}
路标3D点顶点
VertexSBAPointXYZ::VertexSBAPointXYZ() : BaseVertex<3, Vector3>(){
}
位姿–位姿 边
EdgeSE3Expmap::EdgeSE3Expmap() :
BaseBinaryEdge<6, SE3Quat, VertexSE3Expmap, VertexSE3Expmap>() {
}
线性化:
void EdgeSE3Expmap::linearizeOplus() {
VertexSE3Expmap * vi = static_cast<VertexSE3Expmap *>(_vertices[0]);
SE3Quat Ti(vi->estimate());
VertexSE3Expmap * vj = static_cast<VertexSE3Expmap *>(_vertices[1]);
SE3Quat Tj(vj->estimate());
const SE3Quat & Tij = _measurement;
SE3Quat invTij = Tij.inverse();
SE3Quat invTj_Tij = Tj.inverse()*Tij;
SE3Quat infTi_invTij = Ti.inverse()*invTij;
_jacobianOplusXi = invTj_Tij.adj();
_jacobianOplusXj = -infTi_invTij.adj();
}
误差模型:
void computeError() {
const VertexSE3Expmap* v1 = static_cast<const VertexSE3Expmap*>(_vertices[0]);
const VertexSE3Expmap* v2 = static_cast<const VertexSE3Expmap*>(_vertices[1]);
SE3Quat C(_measurement);
SE3Quat error_= v2->estimate().inverse()*C*v1->estimate();
_error = error_.log();
}
推导:
注:1.Twc camera to world,但是g2o里面setEstimate用的是Tcw,setMeasurement用的是Tji。ORB中的例子:
g2o::VertexSE3Expmap * vSE3 = new g2o::VertexSE3Expmap();
vSE3->setEstimate(Converter::toSE3Quat(pFrame->mTcw));
2.markdown 里\^ 打不出skew符号 用s代替,有时也可以直接用
e
x
p
(
ξ
)
exp(\xi)
exp(ξ)代表指数映射。
3.lie伴随性质
e
x
p
(
(
A
d
j
(
T
)
ξ
)
s
)
=
T
e
x
p
(
ξ
s
)
T
−
1
exp((Adj(T)\xi)^s ) = Texp(\xi^s)T^{-1}
exp((Adj(T)ξ)s)=Texp(ξs)T−1其中
A
d
j
(
T
)
=
[
R
t
S
R
0
R
]
Adj(T) = \begin{bmatrix} R & t^SR \\ 0 & R \end{bmatrix}
Adj(T)=[R0tSRR]
变体:
e
x
p
(
(
ξ
s
)
)
T
=
T
e
x
p
(
(
A
d
j
(
T
−
1
)
ξ
)
s
)
exp( (\xi^ s) )T = T exp(( Adj(T ^ {-1})\xi)^s )
exp((ξs))T=Texp((Adj(T−1)ξ)s)这个公式在求导中很有用,可以用来替换左乘右乘的位置
在Lie Groups for 2D and 3D Transformations》中示例两个SE3乘积的求导:
C
=
C
1
C
0
C = C_1C_0
C=C1C0
∂
C
∂
C
O
=
∂
C
∂
δ
[
C
1
e
x
p
(
δ
)
C
0
]
=
∂
C
∂
δ
[
e
x
p
(
A
d
j
(
C
1
)
δ
)
C
1
C
0
]
=
A
d
j
(
C
1
)
\frac{\partial C}{\partial C_O}=\frac{\partial C}{\partial \delta}[C_1 exp(\delta) C_0] =\frac{\partial C}{\partial \delta}[exp(Adj(C_1) \delta) C_1 C_0]=Adj(C_1)
∂CO∂C=∂δ∂C[C1exp(δ)C0]=∂δ∂C[exp(Adj(C1)δ)C1C0]=Adj(C1)
开始求雅克比:
在g2o的边EdgeSE3Expmap中,两个顶点如上所述setEstimate()分别是Tiw, Tjw,根据代码中的误差模型可以看到误差:
T
e
r
r
=
T
j
w
−
1
T
j
i
T
i
w
T_{err} = T_{jw}^{-1} T_{ji} T_{iw}
Terr=Tjw−1TjiTiw
其中
T
j
i
T_{ji}
Tji是测量值,通过setMeasurement赋值。ORB中Sim3边的使用方式如下
const g2o::Sim3 Sjw = vScw[nIDj];
const g2o::Sim3 Sji = Sjw * Swi;
g2o::EdgeSim3* e = new g2o::EdgeSim3();
e->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(nIDj)));
e->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(nIDi)));
e->setMeasurement(Sji);
再看线性化模型,里面的中间变量Tij应该是反了?是Tji的话才能推误差模型。
优化变量有两个:
ξ
i
ξ
j
\xi_i \xi_j
ξiξj,所以
e
i
j
e_{ij}
eij要分别对两个变量求导,分别对应代码中的_jacobianOplusXi
和_jacobianOplusXj
以对位姿j推导为例,对Tj左乘一个小扰动:
T
e
r
r
=
T
j
w
−
1
T
j
i
T
i
w
T
e
r
r
(
δ
ξ
j
)
=
(
e
x
p
(
δ
ξ
j
)
s
T
j
w
)
−
1
T
j
i
T
i
w
=
T
j
w
−
1
e
x
p
(
−
δ
ξ
j
)
T
j
i
T
i
w
T_{err} = T_{jw}^{-1} T_{ji} T_{iw} T_{err}(\delta\xi_{j}) = ( exp(\delta\xi_{j})^s T_{jw} )^{-1} T_{ji} T_{iw} = T_{jw}^{-1} exp(-\delta\xi_{j})T_{ji} T_{iw}
Terr=Tjw−1TjiTiwTerr(δξj)=(exp(δξj)sTjw)−1TjiTiw=Tjw−1exp(−δξj)TjiTiw
T
e
r
r
=
T
j
w
−
1
T
j
i
T
i
w
e
x
p
(
(
−
a
d
j
(
T
j
i
T
i
w
)
−
1
∗
δ
ξ
j
)
s
)
=
e
x
p
(
(
ξ
e
r
r
)
s
)
e
x
p
(
(
−
a
d
j
(
T
j
i
T
i
w
)
−
1
∗
δ
ξ
j
)
s
)
T_{err} = T_{jw}^{-1} T_{ji} T_{iw} exp( (-adj_{(T_{ji} T_{iw})^{-1}} * \delta\xi_j) ^s) = exp((\xi_{err})^s ) exp( (-adj_{(T_{ji} T_{iw})^{-1}} * \delta\xi_j) ^s)
Terr=Tjw−1TjiTiwexp((−adj(TjiTiw)−1∗δξj)s)=exp((ξerr)s)exp((−adj(TjiTiw)−1∗δξj)s)
T
e
r
r
=
e
x
p
(
(
ξ
e
r
r
−
J
−
1
a
d
j
(
T
j
i
T
i
w
)
−
1
∗
δ
ξ
j
)
s
)
J
−
1
≈
I
T_{err} = exp( (\xi_{err} - J^{-1}adj_{(T_{ji} T_{iw})^{-1}} * \delta\xi_j) ^s) J^{-1}\approx I
Terr=exp((ξerr−J−1adj(TjiTiw)−1∗δξj)s)J−1≈I
因此误差对位姿j的雅克比为:
∂
ξ
e
r
r
∂
δ
ξ
j
=
−
a
d
j
(
T
j
i
T
i
w
)
−
1
\frac{\partial \xi_{err}}{\partial {\delta \xi_{j}} } =-adj_{(T_{ji} T_{iw})^{-1}}
∂δξj∂ξerr=−adj(TjiTiw)−1
对应代码里的:
_jacobianOplusXj = -infTi_invTij.adj();