车辆上会装有各式各样的传感器,下面得顺序都是XYZ
。车辆本体一般使用前左上(XYZ
)或者右前上的顺序来定义它的坐标系,相机坐标系则普遍取右下前的顺序,激光雷达和GNSS通常采用前左上(XYZ
)得顺序。
这里定义的是坐标之间的变换关系。 R w b R_{wb} Rwb 和 t w b t_{wb} twb 都用于处理向量之间的坐标变换。而有些资料处理的是坐标轴(或者坐标基底)之间的变换关系,把旋转和平移解释成某个坐标轴从一处变换到了另一处。那样的定义方式是与这里相反
p w = R w b p b + t w b p_w=R_{wb}p_b+t_{wb} pw=Rwbpb+twb
这里相反得本质就是,基变换与坐标变换得关系(矩阵论第一讲)
对于一个向量来讲,它不会随空间基向量变化而变化,变化的只是其在不同基下的空间坐标!向量 c c c在两组空间基 ( α 1 , α 2 , … , α n ) (\alpha_1,\alpha_2,\ldots,\alpha_n) (α1,α2,…,αn)和 ( β 1 , β 2 , … , β n ) (\beta_1,\beta_2,\ldots,\beta_n) (β1,β2,…,βn)下的坐标分别为 [ a 1 a 2 . . . a n ] \begin{bmatrix}a_1\\a_2\\.\\.\\.\\a_n\end{bmatrix} a1a2...an 和 [ b 1 b 2 . . . b n ] \begin{bmatrix}b_1\\b_2\\.\\.\\.\\b_n\end{bmatrix} b1b2...bn 。通过下面这一关系,我们可以得到基变换和坐标变换公式!
c = ( α 1 , α 2 , … , α n ) [ a 1 a 2 . . . a n ] = ( β 1 , β 2 , … , β n ) [ b 1 b 2 . . . b n ] ( 1 ) c=(\alpha_1,\alpha_2,\ldots,\alpha_n)\begin{bmatrix}a_1\\a_2\\.\\.\\.\\a_n\end{bmatrix}=(\beta_1,\beta_2,\ldots,\beta_n)\begin{bmatrix}b_1\\b_2\\.\\.\\.\\b_n\end{bmatrix}\quad(1) c=(α1,α2,…,αn) a1a2...an =(β1,β2,…,βn) b1b2...bn (1)
以三维空间为例,两组空间基存在以下的变换
( β 1 β 2 β 3 ) 1 × 3 = ( α 1 α 2 α 3 ) 1 × 3 ⋅ A 3 × 3 ( 2 ) (\beta_{1}\quad\beta_{2}\quad\beta_{3})_{1\times3}=(\alpha_{1}\quad\alpha_{2}\quad\alpha_{3})_{1\times3}\cdot A_{3\times3} \quad(2) (β1β2β3)1×3=(α1α2α3)1×3⋅A3×3(2)
把基变换公式(2)带入公式(1),即可得到坐标变换公式
[ a 1 a 2 a 3 ] = A 3 × 3 [ b 1 b 2 b 3 ] ( 3 ) \begin{bmatrix}a_1\\a_2\\a_3\end{bmatrix} = A_{3\times3}\begin{bmatrix}b_1\\b_2\\b_3\end{bmatrix} \quad(3) a1a2a3 =A3×3 b1b2b3 (3)
[ b 1 b 2 b 3 ] = A 3 × 3 − 1 [ a 1 a 2 a 3 ] ( 4 ) \begin{bmatrix}b_1\\b_2\\b_3\end{bmatrix}=A_{3\times3}^{-1}\begin{bmatrix}a_1\\a_2\\a_3\end{bmatrix} \quad(4) b1b2b3 =A3×3−1 a1a2a3 (4)
从公式(2)和公式(4)可以看出坐标变换和基变换的关系,互为逆矩阵!
坐标变换:左乘
p w = R w b p b + t w b p_w=R_{wb}p_b+t_{wb} pw=Rwbpb+twb
基(坐标轴)变换:右乘转置(逆)
P w = P b R w b T + t w b P_w=P_bR_{wb}^T+t_{wb} Pw=PbRwbT+twb
旋转不影响平移,平移都是以原点来看的(不论是坐标变换,还是基变换)。
利用单位四元数 q = q 0 + q 1 i + q 2 j + q 3 k \boldsymbol{q}=q_0+q_1 \mathrm{i}+\mathrm{q}_2 \mathrm{j}+\mathrm{q}_3 \mathrm{k} q=q0+q1i+q2j+q3k表示旋转,实部表示了旋转角,虚部表示了旋转轴。下面公式可知,实部为0时,虚部刚好为转轴。,
q = [ cos θ 2 , n sin θ 2 ] T . {\mathbf{q}}=[\cos\frac{\theta}{2},n\sin\frac{\theta}{2}]^\mathrm{T}. q=[cos2θ,nsin2θ]T.
{ θ = 2 arccos q 0 [ n x , n y , n z ] T = [ q 1 , q 2 , q 3 ] T / sin θ 2 . \left\{\begin{array}{l} \theta=2 \arccos q_0 \\ {\left[n_x, n_y, n_z\right]^{\mathrm{T}}=\left[q_1, q_2, q_3\right]^{\mathrm{T}} / \sin \frac{\theta}{2}} \end{array} .\right. {θ=2arccosq0[nx,ny,nz]T=[q1,q2,q3]T/sin2θ.
不是是旋转向量,还是四元数,它们只能旋转向量,不可以改变向量的尺度!所以,对于四元数,只可使用单位四元数,对尺度无影响;对于旋转向量,转轴是单位向量,长度只能是转角!
用 ω
来表达角速度,用 ϕ
来表达旋转向量
当时间连续变化时,旋转和平移就成为了随时间变化的函数 $R(t) 和 和 和 t(t)$。平移变量本质就是一个在不断变化的量,这里忽略。
R ⊤ R = I R^\top R=I R⊤R=I
上式对时间的求导:
d d t ( R ⊤ R ) = R ˙ ⊤ R + R ⊤ R ˙ = 0 \frac{\mathrm{d}}{\mathrm{d}t}\left(R^\top R\right)=\dot{R}^\top R+R^\top\dot{R}=0 dtd(R⊤R)=R˙⊤R+R⊤R˙=0
R ⊤ R ˙ = − ( R ⊤ R ˙ ) ⊤ R^\top\dot{R}=-(R^\top\dot{R})^\top R⊤R˙=−(R⊤R˙)⊤
然后,注意到反对称矩阵经过转置运算后再取负保持不变,即 − ( R ⊤ R ˙ ) ⊤ = R ⊤ R ˙ -(R^\top\dot{R})^\top = R^\top\dot{R} −(R⊤R˙)⊤=R⊤R˙,为了简化表达形式,令 ω ∧ ∈ R 3 × 3 = R ⊤ R ˙ \omega^{\wedge}\in\mathbb{R}^{3\times3}=R^{\top}\dot{R} ω∧∈R3×3=R⊤R˙,则得泊松方程:
R ˙ = − R ( R ⊤ R ˙ ) ⊤ = R ω ∧ \dot{R}=-R(R^\top\dot{R})^\top\\ =R\omega^{\wedge} R˙=−R(R⊤R˙)⊤=Rω∧
求导后是一种特殊的微分方程,这种方程都有类似得通解,比如 y ′ = a e a x , y ( 0 ) = 1 y'=ae^{ax},y(0)=1 y′=aeax,y(0)=1,则 y = e a x y =e^{ax} y=eax。
考虑瞬时变化,那么在固定得时刻 t t t, ω \omega ω就是一个确定的数,即角速度。微分方程初解: t 0 t_0 t0 时刻旋转矩阵为 R ( t 0 ) R(t_0 ) R(t0)。类比可以得到旋转矩阵微分方程得通解:
R ( t ) = R ( t 0 ) exp ( ω ∧ ( t − t 0 ) ) \boldsymbol{R}(t)=\boldsymbol{R}(t_0)\exp(\boldsymbol{\omega}^\wedge(t-t_0)) R(t)=R(t0)exp(ω∧(t−t0))
计 Δ t = t − t 0 \Delta t=t-t_0 Δt=t−t0,则:
R ( t ) = R ( t 0 ) E x p ( ω Δ t ) \boldsymbol{R}(t)=\boldsymbol{R}(t_0)\mathrm{Exp}(\boldsymbol{\omega}\Delta t) R(t)=R(t0)Exp(ωΔt)
在 t = t 0 t = t_0 t=t0处进行泰勒展开:
R ( t 0 + Δ t ) ≈ R ( t 0 ) + R ˙ ( t 0 ) Δ t = R ( t 0 ) + R ( t 0 ) ω ∧ Δ t = R ( t 0 ) ( I + ω ∧ Δ t ) . \begin{aligned} \boldsymbol{R}(t_{0}+\Delta t)& \approx\boldsymbol{R}(t_{0})+\dot{\boldsymbol{R}}(t_{0})\Delta t \\ &=\boldsymbol{R}(t_{0})+\boldsymbol{R}(t_{0})\boldsymbol{\omega}^{\wedge}\Delta t \\ &=\boldsymbol{R}(t_{0})(\boldsymbol{I}+\boldsymbol{\omega}^{\wedge}\Delta t). \end{aligned} R(t0+Δt)≈R(t0)+R˙(t0)Δt=R(t0)+R(t0)ω∧Δt=R(t0)(I+ω∧Δt).
侧面反映了指数映射形式,注意这里时大写的Exp,所以没有加反对称符号!
Exp ( ω Δ t ) = I + ω ∧ Δ t + 1 2 ( ω ∧ Δ t ) 2 + . . . \operatorname{Exp}(\boldsymbol{\omega}\Delta t)=\boldsymbol{I}+\boldsymbol{\omega}^{\wedge}\Delta t+\frac{1}{2}(\boldsymbol{\omega}^{\wedge}\Delta t)^2+... Exp(ωΔt)=I+ω∧Δt+21(ω∧Δt)2+...
关于这里微分方程求解的补充,可以用定积分求解的方法直接求解这个微分方程的通解,这个解在IMU的运动学方程之离散旋转矩阵中也会用到。
在旋转矩阵R的求导中,利用 R ⊤ R = I R^\top R=I R⊤R=I这一性质。只能使用单位四元数表示旋转, q q ∗ = q ∗ q = 1 qq^*=q^*q=1 qq∗=q∗q=1, q q ∗ qq^* qq∗是两个四元数的模长乘积,仍然是1。非常类似旋转矩阵形式。
对时间求导:
q ∗ q ˙ + q ∗ q ˙ = 0 \dot{q^*q}+q^*\dot{q}=0 q∗q˙+q∗q˙=0
q ∗ q ˙ = − q ∗ q ˙ = − ( q ∗ q ˙ ) ∗ q^*\dot{q}=-\dot{q^*q}=-(\boldsymbol{q^*}\dot{\boldsymbol{q}})^* q∗q˙=−q∗q˙=−(q∗q˙)∗
从上面这个式子中可以得到一个信息, q ∗ q ˙ q^*\dot{q} q∗q˙是一个纯虚四元数(实部为0)!这是因为负号的存在,共轭只是虚部不一样,但实部一致,那么就只有0满足条件了。
记一个纯虚四元数 ϖ = [ 0 , ω 1 , ω 2 , ω 3 ⏟ ω ] ⊤ ∈ Q \varpi=[0,\underbrace{\omega_{1},\omega_{2},\omega_{3}}_{\omega}]^{\top}\in\mathcal{Q} ϖ=[0,ω ω1,ω2,ω3]⊤∈Q,然后令:
q ∗ q ˙ = ϖ q^*\dot{q}=\varpi q∗q˙=ϖ
通过下面推导,得到四元数关于时间的导数:
q q ∗ q ˙ = q ϖ s t : q q ∗ = q ∗ q = 1 q ˙ = q ϖ \begin{aligned}qq^*\dot{q}&=q\varpi\quad st:qq^*=q^*q=1\\\dot{q}&=q\varpi\end{aligned} qq∗q˙q˙=qϖst:qq∗=q∗q=1=qϖ
类比于 SO(3) 的情况,我们也可以讨论在 t 时刻附近的瞬时角速度、李代数、指数映射。在考虑瞬时变化时,可以认为 ϖ ϖ ϖ 为固定值,于是上述微分方程给出解为:
q ( t ) = q ( t 0 ) exp ( ϖ Δ t ) \boldsymbol{q}(t)=\boldsymbol{q}(t_0)\exp(\varpi\Delta t) q(t)=q(t0)exp(ϖΔt)
指数映射:
exp ( ϖ ) = ∑ k = 0 ∞ 1 k ! ϖ k \exp\left(\varpi\right)=\sum_{k=0}^{\infty}\frac{1}{k!}\varpi^{k} exp(ϖ)=k=0∑∞k!1ϖk
四元数可以表示为角轴,即方向与长度 ϖ = u θ \varpi=\boldsymbol{u}\theta ϖ=uθ, θ \theta θ是长度, u u u是纯虚单位四元数。纯虚四元数有以下性质:
u 2 = − 1 , u 3 = − u u^2=-1,\quad u^3=-u u2=−1,u3=−u
代入指数映射(会得到一个非常类似于欧拉公式的式子):
exp ( u θ ) = 1 + u θ − 1 2 ! θ 2 − 1 3 ! θ 3 u + 1 4 ! θ 4 + … = ( 1 − 1 2 ! θ 2 + 1 4 ! θ 4 − … ) ⏟ cos θ + ( θ − 1 3 ! θ 3 + 1 5 ! θ 5 − … ) ⏟ sin θ u = cos θ + u sin θ . \begin{aligned} \exp\left(\boldsymbol{u}\theta\right)& =1+\boldsymbol{u}\theta-\frac1{2!}\theta^{2}-\frac1{3!}\theta^{3}\boldsymbol{u}+\frac1{4!}\theta^{4}+\ldots \\ &=\underbrace{\left(1-\frac{1}{2!}\theta^2+\frac{1}{4!}\theta^4-\ldots\right)}_{\cos\theta}+\underbrace{\left(\theta-\frac{1}{3!}\theta^3+\frac{1}{5!}\theta^5-\ldots\right)}_{\sin\theta}\boldsymbol{u} \\ &=\cos\theta+u\sin\theta. \end{aligned} exp(uθ)=1+uθ−2!1θ2−3!1θ3u+4!1θ4+…=cosθ (1−2!1θ2+4!1θ4−…)+sinθ (θ−3!1θ3+5!1θ5−…)u=cosθ+usinθ.
即
exp ( ϖ ) = [ cos θ , u sin θ ] ⊤ \exp(\varpi)=[\cos\theta,u\sin\theta]^\top exp(ϖ)=[cosθ,usinθ]⊤
求模长后发现,纯虚四元数的指数映射结果是一个单位四元数,反过来想,一个单位四元数的李代数就是一个纯虚四元数!
∥ exp ( ϖ ) ∥ = cos 2 θ + sin 2 θ ∥ u ∥ 2 = 1 \|\exp(\varpi)\|=\cos^2\theta+\sin^2\theta\|\boldsymbol{u}\|^2=1 ∥exp(ϖ)∥=cos2θ+sin2θ∥u∥2=1
首先,一个单位四元数 q q q对于的李代数为一个纯虚四元数 ϖ = u θ \varpi=\boldsymbol{u}\theta ϖ=uθ,李代数通过指数映射得到的四元数为:
q = exp ( ϖ ) = [ cos θ , u sin θ ] ⊤ ( 5 ) q = \exp(\varpi)=[\cos\theta,u\sin\theta]^\top \quad(5) q=exp(ϖ)=[cosθ,usinθ]⊤(5)
其次,寻找一个旋转矩阵R对应的单位四元数的李代数!
R = E x p ( ϕ ) = E x p ( θ ′ n ) R=\mathrm{Exp}(\phi)=\mathrm{Exp}(\theta\boldsymbol{'n}) R=Exp(ϕ)=Exp(θ′n)
这时候旋转向量 ϕ \phi ϕ对应的四元数为:
q = [ cos θ ′ 2 , n sin θ ′ 2 ] ( 6 ) q=[\cos\frac{\theta'}{2},n\sin\frac{\theta'}{2}]\quad(6) q=[cos2θ′,nsin2θ′](6)
注意公式5和6中纯虚四元数 ϖ [ 0 , ω 1 , ω 2 , ω 3 ⏟ ω ] ⊤ = u θ \varpi[0,\underbrace{\omega_{1},\omega_{2},\omega_{3}}_{\omega}]^{\top} =\boldsymbol{u}\theta ϖ[0,ω ω1,ω2,ω3]⊤=uθ和 ϕ = θ ′ n \phi = \theta'n ϕ=θ′n中的角度差了一倍!旋转矩阵中的角度是纯虚四元数的2倍,即 θ ′ 2 = θ \frac{\theta'}{2} = \theta 2θ′=θ!因为旋转矩阵一半角度和其全部角度等价!
也就是说,可得:
ϖ = [ 0 , 1 2 ϕ ] ⊤ , 或 ω = 1 2 ϕ . \varpi=[0,\frac{1}{2}\phi]^\top,\quad\text{或}\omega=\frac{1}{2}\phi. ϖ=[0,21ϕ]⊤,或ω=21ϕ.
四元数表达的角速度正好是 SO(3) 李代数的一半!这与四元数在旋转一个矢量时,要乘两遍相对应。
对于一个三维瞬时角速度(或者优化函数的目标更新量) ω ∈ R 3 \omega\in\mathbb{R}^3 ω∈R3, 定义在SO(3)上的运动形式为:
R ˙ = R ω ∧ \dot{R}=R\omega^{\wedge} R˙=Rω∧
R = E x p ( ω ) = exp ( ω ∧ ) R= Exp(\omega)=\exp(\omega^{\wedge}) R=Exp(ω)=exp(ω∧)
如果这个量作为纯虚四元数的更新量(通常由优化函数求解得到),那么对应的四元数应该只更新它的一半
q ˙ = 1 2 q [ 0 , ω ] ⊤ \dot{q}=\frac{1}{2}q[0,\omega]^{\top} q˙=21q[0,ω]⊤
上面 ω \omega ω是三维,消除括号后已经变成了一个四元数:
q ˙ = 1 2 q ω \dot{q}=\frac12q\omega q˙=21qω
四元数指数映射也可以类似也写作
q = exp ( 1 2 [ 0 , ω ] ⊤ ) = Δ exp ( ω ) q=\exp(\frac{1}{2}[0,\omega]^{\top})\stackrel{\Delta}{=}\exp(\omega) q=exp(21[0,ω]⊤)=Δexp(ω)
若 ω \omega ω是一个较小量, cos ( θ 2 ) ≈ 1 , n sin θ 2 ≈ n θ 2 \cos(\frac\theta2)\approx1,n\sin\frac\theta2\approx n\frac\theta2 cos(2θ)≈1,nsin2θ≈n2θ,简化
Exp ( ω ) ≈ [ 1 , 1 2 ω ] \operatorname{Exp}(\omega)\approx[1,\frac{1}{2}\omega] Exp(ω)≈[1,21ω]
四元数更新公式:
q E x p ( ω ) ≈ q [ 1 , 1 2 ω ] q\mathrm{Exp}(\omega)\approx\boldsymbol{q}[1,\frac12\omega] qExp(ω)≈q[1,21ω]
用 ω
来表达角速度,t
是平移量,v
表示线速度
R ˙ = R ω ∧ , t ˙ = v \dot{R}=R\omega^{\wedge},\quad \dot{t}=v R˙=Rω∧,t˙=v
考虑线速度和加速度在不同坐标系之间的变换关系。这里考虑只带旋转关系的两个坐标系之间的线速度和加速度的变换关系。
考虑坐标系1和2,某个向量p在两个系下坐标为 p 1 , p 2 p_{1},p_{2} p1,p2 ,它们满足关系 p 1 = R 12 p 2 p_{1}=R_{12}p_{2} p1=R12p2
对上述关系进行求导:
p ˙ 1 = R ˙ 12 p 2 + R 12 p ˙ 2 = R 12 ω ∧ p 2 + R 12 p ˙ 2 = R 12 ( ω ∧ p 2 + p ˙ 2 ) . \begin{aligned} \dot{p}_{1}& =\dot{R}_{12}\boldsymbol{p}_2+\boldsymbol{R}_{12}\dot{\boldsymbol{p}}_2 \\ &=R_{12}\omega^{\wedge}p_{2}+R_{12}\dot{p}_{2} \\ &=\boldsymbol{R}_{12}(\boldsymbol{\omega}^\wedge\boldsymbol{p}_2+\dot{\boldsymbol{p}}_2). \end{aligned} p˙1=R˙12p2+R12p˙2=R12ω∧p2+R12p˙2=R12(ω∧p2+p˙2).
我们发现, p p p 在两个系下的速度矢量是不同的,不是同一个矢量在不同坐标系中的表达。
一般来讲,位置的导数是速度,即 p ˙ 1 = v 1 , p ˙ 2 = v 2 \dot{p}_{1}=\boldsymbol{v}_{1},\dot{p}_{2}=\boldsymbol{v}_{2} p˙1=v1,p˙2=v2,代入上式:
v 1 = R 12 ( ω ∧ p 2 + v 2 ) v_1=R_{12}(\omega^{\wedge}p_2+v_2) v1=R12(ω∧p2+v2)
速度信息对时间求导:
v ˙ 1 = R ˙ 12 ( ω ∧ p 2 + v 2 ) + R 12 ( ω ˙ ∧ p 2 + ω ∧ p ˙ 2 + v ˙ 2 ) = R 12 ( ω ∧ ω ∧ p 2 + ω ∧ v 2 + ω ˙ ∧ p 2 + ω ∧ p ˙ 2 + v ˙ 2 ) = R 12 ( v ˙ 2 + 2 ω ∧ v 2 + ω ˙ ∧ p 2 + ω ∧ ω ∧ p 2 ) \begin{gathered} \dot{v}_{1} =\dot{R}_{12}\left(\boldsymbol{\omega}^{\wedge}\boldsymbol{p}_{2}+\boldsymbol{v}_{2}\right)+\boldsymbol{R}_{12}\left(\dot{\boldsymbol{\omega}}^{\wedge}\boldsymbol{p}_{2}+\boldsymbol{\omega}^{\wedge}\dot{\boldsymbol{p}}_{2}+\dot{\boldsymbol{v}}_{2}\right) \\ =R_{12}\left(\omega^{\wedge}\omega^{\wedge}p_{2}+\omega^{\wedge}v_{2}+\dot{\omega}^{\wedge}p_{2}+\omega^{\wedge}\dot{p}_{2}+\dot{v}_{2}\right) \\ =\boldsymbol{R}_{12}\left(\dot{\boldsymbol{v}}_{2}+2\boldsymbol{\omega}^{\wedge}\boldsymbol{v}_{2}+\dot{\boldsymbol{\omega}}^{\wedge}\boldsymbol{p}_{2}+\boldsymbol{\omega}^{\wedge}\boldsymbol{\omega}^{\wedge}\boldsymbol{p}_{2}\right) \end{gathered} v˙1=R˙12(ω∧p2+v2)+R12(ω˙∧p2+ω∧p˙2+v˙2)=R12(ω∧ω∧p2+ω∧v2+ω˙∧p2+ω∧p˙2+v˙2)=R12(v˙2+2ω∧v2+ω˙∧p2+ω∧ω∧p2)
定义加速度 a 1 = v ˙ 1 , a 2 = v ˙ 2 a_{1}=\dot{v}_{1},a_{2}=\dot{v}_{2} a1=v˙1,a2=v˙2,则得到
a 1 = R 12 ( a 2 ⏟ 加速度 + 2 ω ∧ v 2 ⏟ 科氏加速度 + ω ˙ ∧ p 2 ⏟ 角加速度 + ω ∧ ω ∧ p 2 ⏟ 向心加速度 ) a_1=\boldsymbol{R}_{12}(\underbrace{a_2}_\text{加速度}+\underbrace{2\boldsymbol{\omega}^{\wedge}\boldsymbol{v}_2}_\text{科氏加速度}+\underbrace{\dot{\boldsymbol{\omega}}^{\wedge}\boldsymbol{p}_2}_\text{角加速度}+\underbrace{\boldsymbol{\omega}^{\wedge}\boldsymbol{\omega}^{\wedge}\boldsymbol{p}_2}_\text{向心加速度}) a1=R12(加速度 a2+科氏加速度 2ω∧v2+角加速度 ω˙∧p2+向心加速度 ω∧ω∧p2)
在精度不高的应用场景中,通常会选择忽略后面三项,只保留最简单的转换关系
车辆本身的速度,也就是车体坐标系原点在世界系下的速度(车体原点在车辆系下速度一直是零,没有实际意义)。这个速度是定义在世界系中的,记为 v w v_w vw 。如果左乘 R b w R_{bw} Rbw ,也可以将这个矢量转换到车辆坐标系下,记作 v b v_b vb,称其为车体系速度。
∂ L o g ( R 1 R 2 ) ∂ R 1 = lim ϕ → 0 Log ( R 1 E x p ( ϕ ) R 2 ) − Log ( R 1 R 2 ) ϕ = lim ϕ → 0 L o g ( R 1 R 2 E x p ( R 2 ⊤ ϕ ) ) − L o g ( R 1 R 2 ) ϕ = J r − 1 ( Log ( R 1 R 2 ) ) R 2 ⊤ . \begin{aligned} \frac{\partial\mathrm{Log}\left(\boldsymbol{R}_1\boldsymbol{R}_2\right)}{\partial\boldsymbol{R}_1}& =\lim_{\phi\to0}\frac{\operatorname{Log}\left(\boldsymbol{R}_1\mathrm{Exp}\left(\phi\right)\boldsymbol{R}_2\right)-\operatorname{Log}\left(\boldsymbol{R}_1\boldsymbol{R}_2\right)}\phi \\ &=\lim_{\phi\to0}\frac{\mathrm{Log}\left(\boldsymbol{R}_1\boldsymbol{R}_2\mathrm{Exp}\left(\boldsymbol{R}_2^\top\boldsymbol{\phi}\right)\right)-\mathrm{Log}\left(\boldsymbol{R}_1\boldsymbol{R}_2\right)}\phi \\ &=\boldsymbol{J}_r^{-1}(\operatorname{Log}(\boldsymbol{R}_1\boldsymbol{R}_2))\boldsymbol{R}_2^\top. \end{aligned} ∂R1∂Log(R1R2)=ϕ→0limϕLog(R1Exp(ϕ)R2)−Log(R1R2)=ϕ→0limϕLog(R1R2Exp(R2⊤ϕ))−Log(R1R2)=Jr−1(Log(R1R2))R2⊤.
其中第二行的转换使用到 S O ( 3 ) SO(3) SO(3)的伴随性质:
R ⊤ E x p ( ϕ ) R = E x p ( R ⊤ ϕ ) R^\top\mathrm{Exp}(\phi)R=\mathrm{Exp}(\boldsymbol{R}^\top\phi) R⊤Exp(ϕ)R=Exp(R⊤ϕ)
第三行使用了BCH近似公式:
L o g ( R 1 R 2 E x p ( R 2 ⊤ ϕ ) ) = L o g ( R 1 R 2 ) + J r − 1 ( R 1 R 2 ) L o g ( E x p ( R 2 ⊤ ϕ ) ) \mathrm{Log}\left(R_1R_2\mathrm{Exp}\left(R_2^\top\phi\right)\right)=\mathrm{Log}(R_1R_2)+J_r^{-1}(R_1R_2)\mathrm{Log}(\mathrm{Exp}(R_2^\top\phi)) Log(R1R2Exp(R2⊤ϕ))=Log(R1R2)+Jr−1(R1R2)Log(Exp(R2⊤ϕ))
∂ L o g ( R 1 R 2 ) ∂ R 2 = lim ϕ → 0 L o g ( R 1 R 2 E x p ( ϕ ) ) − L o g ( R 1 R 2 ) ϕ = J r − 1 ( L o g ( R 1 R 2 ) ) \frac{\partial\mathrm{Log}\left(R_1R_2\right)}{\partial R_2}=\lim_{\phi\to0}\frac{\mathrm{Log}\left(R_1R_2\mathrm{Exp}\left(\phi\right)\right)-\mathrm{Log}\left(R_1R_2\right)}{\phi}=J_r^{-1}(\mathrm{Log}(R_1R_2)) ∂R2∂Log(R1R2)=ϕ→0limϕLog(R1R2Exp(ϕ))−Log(R1R2)=Jr−1(Log(R1R2))
Log
符号,取为矢量左右扰动由状态定义和误差状态定义形式确定
定义状态:定义系统旋转状态为Globally
形式: R L G R_L^{G} RLG ,即pose
表达在Global
坐标系(平移量在Global
坐标系下)
定义误差状态:旋转的误差状态为: θ L ′ L \theta_{L^{\prime}}^L θL′L
确认状态的叠加方式:根据旋转的链式法则,叠加了扰动的旋转为:
R L ′ G = R L G R L ′ L = R L G ( I + [ θ L ′ L ] × ) R_{L^{\prime}}^G=R_L^GR_{L^{\prime}}^L=R_L^G(I+[\theta_{L^{\prime}}^L]^\times) RL′G=RLGRL′L=RLG(I+[θL′L]×)
扰动的叠加是右扰动形式
Locally
形式: R G L R_{G}^L RGL ,即pose
表达在Local
坐标系(平移量在local
坐标系下)R G L ′ = R L L ′ R G L = ( I + [ θ L L ′ ] × ) R G L = ( I − [ θ L ′ L ] × ) R G L R_G^{L^{\prime}}=R_L^{L^{\prime}}R_G^{L}=(I+[\theta_L^{L^{\prime}}]^{\times})R_G^{L}=(I-[\theta_{L^{\prime}}^{L}]^{\times})R_G^{L} RGL′=RLL′RGL=(I+[θLL′]×)RGL=(I−[θL′L]×)RGL
扰动的叠加是左扰动形式,注意上面括号里面+-
号,转换为四元数的话,+
表示了常见的Hamilton
四元数,-
表示了JPL
四元数。两个微小四元数表示的旋转是相反的。
bin/motion −−use_quaternion=true −−angular_velocity=15 #
搞清楚
body
系与世界系
1body
系:有恒定的线速度和恒定的角速度,那么就一定可以表示为圆周运动
2 世界系:body
系下速度不发生变化,但旋转到世界系就会变化。我们求的位移是世界系的位移,所以要转换速度
3pose.so3()
就相当于 R w b R_{wb} Rwb,角速度omega
就不需要转换到世界系,这里位姿的变换是右扰动(全局坐标系)。 R w b ′ = R w b R b b ′ R_{wb'} =R_{wb}R_{bb'} Rwb′=RwbRbb′
#include
#include
#include "common/eigen_types.h"
#include "common/math_utils.h"
#include "tools/ui/pangolin_window.h"
/// 本节程序演示一个正在作圆周运动的1车辆
/// 车辆的角速度与线速度可以在flags中设置
DEFINE_double(angular_velocity, 10.0, "角速度(角度)制"); // 这里都是宏变量
DEFINE_double(linear_velocity, 5.0, "车辆前进线速度 m/s");
DEFINE_bool(use_quaternion, false, "是否使用四元数计算");
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);// 这行代码用于初始化Google日志系统。它将日志输出到标准错误输出(stderr)
FLAGS_stderrthreshold = google::INFO; // 这行代码设置了在stderr中显示的日志级别。在这里,将日志级别设置为INFO,这意味着只有INFO级别及以上的日志会被输出到stderr
FLAGS_colorlogtostderr = true; // 在stderr中显示彩色日志输出
google::ParseCommandLineFlags(&argc, &argv, true); // 解析命令行参数。它会检查命令行中的标志参数(例如--flag_name=value)并根据相应的定义来设置相应的标志值
/// 可视化
sad::ui::PangolinWindow ui;
if (ui.Init() == false) {
return -1;
}
double angular_velocity_rad = FLAGS_angular_velocity * sad::math::kDEG2RAD; // 弧度制角速度
SE3 pose; // TWB表示的位姿
Vec3d omega(0, 0, angular_velocity_rad); // 角速度矢量 00z 如果z=1是角轴,这样是旋转向量,值代表角度
Vec3d v_body(FLAGS_linear_velocity, 0, 0); // 本体系速度 所以只有x方向有值
const double dt = 0.05; // 每次更新的时间
while (ui.ShouldQuit() == false) {
// 更新自身位置
Vec3d v_world = pose.so3() * v_body;
pose.translation() += v_world * dt;
// 更新自身旋转
if (FLAGS_use_quaternion) {
Quatd q = pose.unit_quaternion() * Quatd(1, 0.5 * omega[0] * dt, 0.5 * omega[1] * dt, 0.5 * omega[2] * dt);
q.normalize();
pose.so3() = SO3(q);
} else {
pose.so3() = pose.so3() * SO3::exp(omega * dt);
}
LOG(INFO) << "pose: " << pose.translation().transpose();
ui.UpdateNavState(sad::NavStated(0, pose, v_world));
usleep(dt * 1e6);
}
ui.Quit();
return 0;
}
首先要知道状态是什么?在多个传感器中,状态可以是旋转、平移、速度等等。
状态估计,就是我们要通过什么样的方式去估计状态量,得到的值是否正确,是否值得信赖。
典型的离散时间状态估计由运动方程(如IMU/Wheel
)和观测方程(cam/lidar/GNSS
)组成
{ x k = f ( x k − 1 , u k ) + w k , k = 1 , … , N z k = h ( x k ) + v k , \left.\left\{\begin{array}{l}x_k=f\left(x_{k-1},u_k\right)+\boldsymbol{w}_k,\quad k=1,\ldots,N\\z_k=\boldsymbol{h}\left(x_k\right)+\boldsymbol{v}_k,\end{array}\right.\right. {xk=f(xk−1,uk)+wk,k=1,…,Nzk=h(xk)+vk,
上面是非线性函数,将其线性化,就可以得到线性高斯系统得状态估计问题。线性系统的无偏最优估计由KF给出。
{ x k = A k x k − 1 + u k + w k z k = C k x k + v k \left.\left\{\begin{array}{l}x_k=A_k\boldsymbol{x}_{k-1}+\boldsymbol{u}_k+\boldsymbol{w}_k\\\boldsymbol{z}_k=C_k\boldsymbol{x}_k+\boldsymbol{v}_k\end{array}\right.\right. {xk=Akxk−1+uk+wkzk=Ckxk+vk
预测
x k , pred = A k x k − 1 + u k , P k , pred = A k P k − 1 A k ⊤ + R k x_{k,\text{pred}} = A _ k x _ { k - 1 }+u_k,\quad P_{k,\text{pred}} = A _ k P _ { k - 1 }A_k^\top+R_k xk,pred=Akxk−1+uk,Pk,pred=AkPk−1Ak⊤+Rk
更新
K k = P k , pred C k ⊤ ( C k P k , pred C k ⊤ + Q k ) − 1 K_k=P_{k,\text{pred}} C _ k ^ { \top }\left(C_kP_{k,\text{pred}} C _ k ^ { \top }+Q_k\right)^{-1} Kk=Pk,predCk⊤(CkPk,predCk⊤+Qk)−1
计算后验概率的分布
x k = x k , p r e d + K k ( z k − C k x k , p r e d ) , P k = ( I − K k C k ) P k , p r e d . \begin{aligned}&\boldsymbol{x}_k=\boldsymbol{x}_{k,\mathrm{pred}}+\boldsymbol{K}_k\left(\boldsymbol{z}_k-\boldsymbol{C}_k\boldsymbol{x}_{k,\mathrm{pred}}\right),\\&\boldsymbol{P}_k=\left(\boldsymbol{I}-\boldsymbol{K}_k\boldsymbol{C}_k\right)\boldsymbol{P}_{k,\mathrm{pred}}.\end{aligned} xk=xk,pred+Kk(zk−Ckxk,pred),Pk=(I−KkCk)Pk,pred.
补充:关于最后那个协方差矩阵的更新,有三种形式
1 P = ( I − K H ) P \begin{aligned}P=(I-KH)P\end{aligned} P=(I−KH)P 最常见的卡尔曼滤波的形式,数值稳定性较差,计算的协方差不能保证对称正定。
2 P = P − K ( H P H T + R ) K T P = P-K(HPH^T+R)K^T P=P−K(HPHT+R)KT: 对称形式,计算的协方差仍是对称阵。
3 P ← ( I − K H ) P ( I − K H ) T + K R K T P\leftarrow(I-KH)P(I-KH)^T+KRK^T P←(I−KH)P(I−KH)T+KRKT:对称正定形式,计算出的协方差是仍是对称正定阵
K = P H T ( H P H T + R ) − 1 ⇒ K ( H P H T + R ) = P H T P − K ( H P H T + R ) K T = P − P H T K T = P T − K H P T = ( I − K H ) P ( I − K H ) P ( I − K H ) T + K R K T = ( I − K H ) P − ( I − K H ) P H T K T + K R K T = ( I − K H ) P − P H T K T + K ( H P H T + R ) K T = ( I − K H ) P − P H T K T + P H T K T \begin{aligned} K=PH^{T}(HPH^{T}+R)^{-1}& \Rightarrow K(HPH^T+R)=PH^T \\ P-K(HPH^{T}+R)K^{T}& =P-PH^{T}K^{T} \\ &=P^{T}-KHP^{T} \\ &=(I-KH)P \\ (I-KH)P(I-KH)^T+KRK^T& =(I-KH)P-(I-KH)PH^TK^T+KRK^T \\ &=(I-KH)P-PH^TK^T+K(HPH^T+R)K^T \\ &=(I-KH)P-PH^TK^T+PH^TK^T \end{aligned} K=PHT(HPHT+R)−1P−K(HPHT+R)KT(I−KH)P(I−KH)T+KRKT⇒K(HPHT+R)=PHT=P−PHTKT=PT−KHPT=(I−KH)P=(I−KH)P−(I−KH)PHTKT+KRKT=(I−KH)P−PHTKT+K(HPHT+R)KT=(I−KH)P−PHTKT+PHTKT
假设一个高斯分布状态变量经过非线性函数后仍为高斯分布
x k , p r e d = f ( x k − 1 , u k ) , P k , p r e d = F k P k − 1 F k ⊤ + R k . x_{k,\mathrm{pred}}=f(x_{k-1},u_{k}),\quad P_{k,\mathrm{pred}}=F_{k}P_{k-1}F_{k}^{\top}+R_{k}. xk,pred=f(xk−1,uk),Pk,pred=FkPk−1Fk⊤+Rk.
K k = P k , p r e d H k ⊤ ( H k P k , p r e d H k ⊤ + Q k ) − 1 , x k = x k , pred + K k ( z k − H k x k , pred ) , P k = ( I − K k C k ) P k , p r e d . \begin{aligned} &K_{k} =P_{k,\mathrm{pred}}H_{k}^{\top}(H_{k}P_{k,\mathrm{pred}}H_{k}^{\top}+Q_{k})^{-1}, \\ &x_{k} =x_{k,\text{pred}} + K _ k ( z _ k - H _ k x _ { k ,\text{pred}} ) , \\ &P_{k} =(\boldsymbol{I}-\boldsymbol{K}_{k}\boldsymbol{C}_{k})\boldsymbol{P}_{k,\mathrm{pred}}. \end{aligned} Kk=Pk,predHk⊤(HkPk,predHk⊤+Qk)−1,xk=xk,pred+Kk(zk−Hkxk,pred),Pk=(I−KkCk)Pk,pred.
运动学方程和观测方程都可以看成一个状态变量 x
与运动学输入、观测值之间的残差,这是一种批量最小二乘
e motion = x k − f ( x k − 1 , u ) ∼ N ( 0 , R k ) , e obs = z k − h ( x k ) ∼ N ( 0 , Q k ) . \begin{aligned}e_{\text{motion}}&=x_k-f(x_{k-1},u)\sim\mathcal{N}(0,R_k),\\e_{\text{obs}}&=z_k-h(x_k)\sim\mathcal{N}(0,Q_k).\end{aligned} emotioneobs=xk−f(xk−1,u)∼N(0,Rk),=zk−h(xk)∼N(0,Qk).
对于上面提及的滤波器这种处理方法,其中的最优状态估计可以看成关于各种误差项的最小二乘问题(比较计算了协方差和预测,那么后验估计就非常类似这里的批量最小二乘)
x ∗ = arg min x ∑ k ( e k ⊤ Ω k − 1 e k ) x^*=\arg\min_x\sum_k\left(e_k^\top\Omega_k^{-1}e_k\right) x∗=argxmink∑(ek⊤Ωk−1ek)
如果是迭代最小二乘,处理方法按下面步骤。
关于滤波器与最优化的区别与联系,
它们在线性系统中会得到同样的结果 ,但在非线性系统中通常不然。主要原因有以下几个
最优化方法有迭代过程,而 EKF
则没有。
迭代过程会不断在新的线性化点 x i x_i xi上求取雅可比矩阵,而EKF
的雅可比矩阵只在预测位置上求取一次。
EKF
还会区分 x k , p r e d x_{k,pred} xk,pred ,分开处理预测过程与观测过程。而最优化方法则没有 x k , p r e d x_{k,pred} xk,pred,统一处理各处的状态变量。
如果忽略上面差异,把卡尔曼滤波当作一种优化,那么它的优化变量和误差函数是什么(图优化里面最关键的地方)
2个优化变量: x k − 1 x_{k-1} xk−1和 x k x_{k} xk,即上一个时刻与当前时刻状态。(图优化里面如滑动窗口,是一段时间内的状态;如果是全局优化,那么就是全部的状态)
3个误差函数:先验误差、运动误差、观测误差。(图优化:在纯视觉里面就是观测误差)
注意:普通的优化器没有计算协方差矩阵,需要边缘化
Marginalization
。
基础环境:Ubuntu20.04
ROS环境:Noetic
然后根据GitHub安装相应依赖
sudo apt install -y ros-noetic-pcl-ros ros-noetic-velodyne-msgs libopencv-dev libgoogle-glog-dev libeigen3-dev libsuitesparse-dev libpcl-dev libyaml-cpp-dev libbtbb-dev libgmock-dev
额外安装Pangolin、g2o库,Sophus库作者把相应的文件提取出来了,无需安装。
mkdir build # 进入到thirdparty/xxx 进行相应的编译安装
cd build
cmake ..
make -j8
这本书的程序就是一个大的工程项目,所以直接编译
mkdir build
cd build
cmake ..
make -j8
因为每一次编译都很费劲,所以可以先编译使用的章节,注释其它章节
add_subdirectory(common)
add_subdirectory(ch2)
add_subdirectory(ch3)
# add_subdirectory(ch4)
# add_subdirectory(ch5)
# add_subdirectory(ch6)
# add_subdirectory(ch7)
# add_subdirectory(ch8)
# add_subdirectory(ch9)
# add_subdirectory(ch10)
add_subdirectory(tools)
∂ R − 1 p ∂ R . \frac{\partial R^{-1}p}{\partial R}. ∂R∂R−1p.
右扰动
∂ ( R − 1 p ) ∂ φ = lim φ → 0 ( R exp ( φ ∧ ) ) − 1 p − R − 1 p φ = lim φ → 0 ( I − φ ∧ ) R − 1 p − R − 1 p φ = lim φ → 0 ( R − 1 p ) ∧ φ φ = ( R − 1 p ) ∧ \begin{aligned} \begin{aligned}\frac{\partial\left(\mathbf{R^{-1}p}\right)}{\partial\varphi}\end{aligned}& \begin{aligned}=\lim_{\boldsymbol{\varphi}\to\boldsymbol{0}}\frac{(\mathbf{R}\exp\left(\boldsymbol{\varphi}^{\wedge})\right)^{-1}\mathbf{p}-\mathbf{R^{-1}}\mathbf{p}}{\varphi}\end{aligned} \\ &=\lim_{\boldsymbol{\varphi}\to\mathbf{0}}\frac{\left(\mathbf{I}-\boldsymbol{\varphi}^{\wedge}\right)\mathbf{R}^{-1}\mathbf{p}-\mathbf{R^{-1}}\mathbf{p}}{\varphi} \\ &=\lim_{\varphi\to0}\frac{(\mathbf{R}^{-1}\mathbf{p})^{\wedge}\boldsymbol{\varphi}}\varphi=(\mathbf{R}^{-1}\mathbf{p})^\wedge \end{aligned} ∂φ∂(R−1p)=φ→0limφ(Rexp(φ∧))−1p−R−1p=φ→0limφ(I−φ∧)R−1p−R−1p=φ→0limφ(R−1p)∧φ=(R−1p)∧
左扰动:和右扰动推导一致,最后结果应该是 − R − 1 p ∧ -\mathbf{R}^{-1} \mathbf{p}^\wedge −R−1p∧
∂ R 1 R 2 − 1 ∂ R 2 . \frac{\partial R_1R_2^{-1}}{\partial R_2}. ∂R2∂R1R2−1.
右扰动
以右扰动为例,第一行到第二行求 exp ( ϕ ∧ ) − 1 = exp ( − ϕ ∧ ) ≈ I − ϕ ∧ \exp(\phi^\wedge)^{-1}=\exp(-\phi^\wedge)\approx\mathbf{I}-\phi^\wedge exp(ϕ∧)−1=exp(−ϕ∧)≈I−ϕ∧,第三行到第四行利用SO(3)
的伴随性质,第四行到第五行是对数形式的BCH近似公式。
d ln ( R 1 R 2 − 1 ) ∨ d R 2 = lim ψ → 0 ln ( R 1 ( R 2 exp ( ψ ∧ ) ) − 1 ) ∨ − ln ( R 1 R 2 − 1 ) ∨ ψ = lim ψ → 0 ln ( R 1 exp ( − ψ ∧ ) R 2 − 1 ) ∨ − ln ( R 1 R 2 − 1 ) ∨ ψ = lim ψ → 0 ln ( R 1 R 2 − 1 R 2 exp ( − ψ ∧ ) R 2 − 1 ) ∨ − ln ( R 1 R 2 − 1 ) ∨ ψ = lim ψ → 0 ln ( R 1 R 2 − 1 exp ( − R 2 ψ ∧ ) ) ∨ − ln ( R 1 R 2 − 1 ) ∨ ψ = lim ψ → 0 ln ( R 1 R 2 − 1 ) ∨ − J r − 1 R 2 ψ − ln ( R 1 R 2 − 1 ) ∨ ψ = lim ψ → 0 − J r − 1 R 2 ψ ψ = − J r − 1 ( ln ( R 1 R 2 − 1 ) ∨ ) ⋅ R 2 = − J l − 1 ( ln ( R 1 R 2 − 1 ) ∨ ) ⋅ R 1 \begin{aligned} \frac{d\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee}{d\mathbf{R}_2}& =\lim_{\psi\to0}\frac{\ln(\mathbf{R}_1(\mathbf{R}_2\exp(\psi^\wedge))^{-1})^\vee-\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee}\psi \\ &=\lim_{\psi\to0}\frac{\ln(\mathbf{R}_1\exp(-\psi^\wedge)\mathbf{R}_2^{-1})^\vee-\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee}\psi \\ &=\lim_{\psi\to0}\frac{\ln(\mathbf{R}_1\mathbf{R}_2^{-1}\mathbf{R}_2\exp(-\psi^{\wedge})\mathbf{R}_2^{-1})^\vee-\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee}\psi \\ &=\lim_{\psi\to0}\frac{\ln(\mathbf{R}_1\mathbf{R}_2^{-1}\exp(-\mathbf{R}_2\psi^{\wedge}))^{\vee}-\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^{\vee}}\psi \\ &=\lim_{\psi\to0}\frac{\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee-\mathbf{J}_r^{-1}\mathbf{R}_2\psi-\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee}\psi \\ &=\lim_{\psi\to0}\frac{-\mathbf{J}_r^{-1}\mathbf{R}_2\psi}\psi \\ &=-\mathbf{J}_r^{-1}(\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee)\cdot\mathbf{R}_2 \\ &=-\mathbf{J}_l^{-1}(\ln(\mathbf{R}_1\mathbf{R}_2^{-1})^\vee)\cdot\mathbf{R}_1 \end{aligned} dR2dln(R1R2−1)∨=ψ→0limψln(R1(R2exp(ψ∧))−1)∨−ln(R1R2−1)∨=ψ→0limψln(R1exp(−ψ∧)R2−1)∨−ln(R1R2−1)∨=ψ→0limψln(R1R2−1R2exp(−ψ∧)R2−1)∨−ln(R1R2−1)∨=ψ→0limψln(R1R2−1exp(−R2ψ∧))∨−ln(R1R2−1)∨=ψ→0limψln(R1R2−1)∨−Jr−1R2ψ−ln(R1R2−1)∨=ψ→0limψ−Jr−1R2ψ=−Jr−1(ln(R1R2−1)∨)⋅R2=−Jl−1(ln(R1R2−1)∨)⋅R1
左扰动:这个推导很简单,直接就类似于右扰动第4行,最后结果应该是 − J r − 1 ( l n ( R 1 R 2 − 1 ) ) -\boldsymbol{J}_r^{-1}(\mathrm{ln}(\boldsymbol{R}_1\boldsymbol{R}_2^{-1})) −Jr−1(ln(R1R2−1))
将2.3节的实验修改成带旋转的抛物线运动。物体一方面沿 Z
轴自转,一方面存在水平的初始线速度,又受到 −Z
方向的重力加速度影响。请设计程序并完成动画演示。
注意几点
1 绕
z
轴自转,和原来程序一致----所以旋转不发生变化2 水平初始线速度,设置为
x
方向,假设保持不变3 重力加速度–自由落体,
v(z)=-gt, x(z)=vt+0.5gt^2
。4 注意坐标系转换,搞清楚那个是
body
系,那个是世界系。比如位移是指世界系下位移,不能用body
系下速度来更新;常见-9.8
的重力加速度是世界系下量,如果要更新body
系下速度,需要转换对应的加速度!
DEFINE_double(angular_velocity, 10.0, "角速度(角度)制");
DEFINE_double(linear_velocity, 5.0, "车辆前进线速度 m/s");
DEFINE_double(gravity, -9.8, "加速度 m/s2");
DEFINE_bool(use_quaternion, false, "是否使用四元数计算");
int main(int argc, char** argv) {
....
double angular_velocity_rad = FLAGS_angular_velocity * sad::math::kDEG2RAD; // 弧度制角速度
SE3 pose; // TWB表示的位姿
Vec3d omega(0, 0, angular_velocity_rad); // 角速度矢量
Vec3d v_body(FLAGS_linear_velocity, 0, 0); // 本体系速度
const double dt = 0.05; // 每次更新的时间
const Vec3d a_w(0, 0, FLAGS_gravity);
while (ui.ShouldQuit() == false) {
// 1 v_w = R_wb * v_b;
Vec3d v_world = pose.so3() * v_body;
// 2 x_w = v_w * dt + 0.5 * a_w * dt * dt;
pose.translation() += v_world * dt + 0.5 * a_w * dt * dt;
// 3 a_b = (R_wb)^T * a_w
Vec3d a_b = pose.so3().inverse() * a_w;
// 4 v_b += a_b * dt
v_body += a_b * dt;
// 更新自身旋转
if (FLAGS_use_quaternion) {
Quatd q = pose.unit_quaternion() * Quatd(1, 0.5 * omega[0] * dt, 0.5 * omega[1] * dt, 0.5 * omega[2] * dt);
q.normalize();
pose.so3() = SO3(q);
} else {
pose.so3() = pose.so3() * SO3::exp(omega * dt);
}
LOG(INFO) << "pose: " << pose.translation().transpose();
ui.UpdateNavState(sad::NavStated(0, pose, v_world));
usleep(dt * 1e6);
}
ui.Quit();
return 0;
}
有了重力加速度,使得其不能在一个平面。右下角分别显示了世界系下的速度分量和body
系(baselink
)下的速度分量。body系下x分量不发生变换,z方向线性递减;世界系下两个速度像是随正弦函数变化。
后续补充吧