本文将介绍Mahony姿态解算算法,不涉及磁力计部分,程序源代码见:https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/

姿态的表示方法

欧拉角

物体姿态最直观的表示方法就是欧拉角,一个坐标系相对于另一个坐标系的转动,可以通过围绕三个正交轴的转动来表示,这三个转动角被称为一组欧拉角。常用的欧拉角组是横滚角( r o l l roll roll ϕ \phi ϕ)、俯仰角( p i t c h pitch pitch θ \theta θ)、偏航角( y a w yaw yaw ψ \psi ψ),分别表示绕导航坐标系中 X X X Y Y Y Z Z Z轴的旋转,导航坐标系满足右手坐标系,食指方向为 X X X轴正方向,中指为 Y Y Y轴正方向,大拇指为 Z Z Z轴正方向,这三个旋转角对应的旋转矩阵如下:
R ( ϕ ) = [ 1 0 0 0 c o s ϕ s i n ϕ 0 − s i n ϕ c o s ϕ ] R ( θ ) = [ c o s θ 0 − s i n θ 0 1 0 s i n θ 0 c o s θ ] R ( ψ ) = [ c o s ψ s i n ψ 0 − s i n ψ c o s ψ 0 0 0 1 ] \begin{aligned} R(\phi)&=\begin{bmatrix}1&0&0\\ 0&cos\phi&sin\phi\\0&-sin\phi&cos\phi\end{bmatrix}\\ R(\theta)&=\begin{bmatrix}cos\theta&0&-sin\theta\\ 0&1&0\\sin\theta&0&cos\theta\end{bmatrix}\\ R(\psi)&=\begin{bmatrix}cos\psi&sin\psi&0\\-sin\psi&cos\psi&0\\0&0&1\end{bmatrix} \end{aligned} R(ϕ)R(θ)R(ψ)=1000cosϕsinϕ0sinϕcosϕ=cosθ0sinθ010sinθ0cosθ=cosψsinψ0sinψcosψ0001
由于矩阵乘法不满足交换律,所以旋转顺序不同,得到的姿态也不同,一般按照" Z Y X ZYX ZYX"的顺序进行旋转,得到:
R = R ( ψ ) R ( θ ) R ( ϕ ) = [ c o s θ c o s φ − c o s ϕ s i n ψ + s i n ϕ s i n θ c o s ψ s i n ϕ s i n ψ + c o s ϕ s i n θ c o s ψ c o s θ s i n ψ c o s ϕ c o s ψ + s i n ϕ s i n θ s i n ψ − s i n ϕ c o s ψ + c o s ϕ s i n θ s i n ψ − s i n θ s i n ϕ c o s θ c o s ϕ c o s θ ] \begin{aligned} R&=R(\psi)R(\theta)R(\phi)\\ &=\begin{bmatrix} cos\theta cos\varphi&-cos\phi sin\psi+sin\phi sin\theta cos\psi&sin\phi sin\psi +cos\phi sin\theta cos\psi\\ cos\theta sin\psi&cos\phi cos\psi+sin\phi sin\theta sin\psi&-sin\phi cos\psi+cos\phi sin\theta sin\psi\\ -sin\theta&sin\phi cos\theta &cos\phi cos\theta \end{bmatrix} \end{aligned} R=R(ψ)R(θ)R(ϕ)=cosθcosφcosθsinψsinθcosϕsinψ+sinϕsinθcosψcosϕcosψ+sinϕsinθsinψsinϕcosθsinϕsinψ+cosϕsinθcosψsinϕcosψ+cosϕsinθsinψcosϕcosθ
虽然欧拉角的表示方式很直观,但是由于万向节死锁的存在,一般不用欧拉角来计算姿态。

四元数

四元数(Quaternion)是一种能表示三维物体转动的四维超复数。一般的复数可表示为 a + i b a+ib a+ib,而四元数包含一个实部,三个虚部,用 q 0 + q 1 i + q 2 j + q 3 k q_0+q_1i+q_2j+q_3k q0+q1i+q2j+q3k表示,用向量表示为:
q = [ q 0 q 1 q 2 q 3 ] [ 1 i j k ] q=\begin{bmatrix}q_0&q_1&q_2&q_3\end{bmatrix}\begin{bmatrix}1\\i\\j\\k\end{bmatrix} q=[q0q1q2q3]1ijk
假设坐标系 A A A,绕该坐标系中的一个轴 r ^ = [ r x , r y , r z ] \hat r=[r_x,r_y,r_z] r^=[rx,ry,rz]进行旋转,旋转角度为 θ \theta θ,得到坐标系 B B B,如下图:

image-20210819171816213

我们可以用如下四元数来表示从 A A A系到 B B B系的旋转,也即 A A A系绕 r ^ \hat r r^轴的正方向(右手准则)转动角度 θ \theta θ后与 B B B系重合。注:使用四元数来表示刚体旋转,必须使四元数的模等于1
A B q = [ q 0 q 1 q 2 q 3 ] = [ c o s θ 2 r x s i n θ 2 r y s i n θ 2 r z s i n θ 2 ] _A^Bq =\begin{bmatrix}q_0&q_1&q_2&q_3\end{bmatrix} =\begin{bmatrix}cos\frac{\theta}{2}&r_xsin\frac{\theta}{2}&r_ysin\frac{\theta}{2}&r_zsin\frac{\theta}{2}\end{bmatrix} ABq=[q0q1q2q3]=[cos2θrxsin2θrysin2θrzsin2θ]
相反的旋转可用 q B A q_B^A qBA表示:
B A q = A B q − 1 = A B q ∗ ∣ ∣ A B q ∣ ∣ _B^Aq={_A^Bq^{-1}}=\frac{_A^Bq^*}{||{_A^Bq}||} BAq=ABq1=ABqABq
其中 q ∗ q^* q表示共轭, ∣ ∣ q ∣ ∣ ||q|| q表示四元数的模,用于表示姿态的四元数模长固定为1,所以:
B A q = A B q ∗ = [ q 0 − q 1 − q 2 − q 3 ] _B^Aq={_A^Bq^*}=\begin{bmatrix}q_0&-q_1&-q_2&-q_3\end{bmatrix} BAq=ABq=[q0q1q2q3]
也即相反的旋转可以用四元数的共轭表示。

使用 ⊗ \otimes 来表示四元数乘法,对于四元数 a a a b b b,四元数乘积为:
a ⊗ b = [ a 0 a 1 a 2 a 3 ] ⊗ [ b 0 b 1 b 2 b 3 ] = [ a 0 b 0 − a 1 b 1 − a 2 b 2 − a 3 b 3 a 0 b 1 + a 1 b 0 + a 2 b 3 − a 3 b 2 a 0 b 2 − a 1 b 3 + a 2 b 0 + a 3 b 1 a 0 b 3 + a 1 b 2 − a 2 b 1 + a 3 b 0 ] = [ b 0 − b 1 − b 2 − b 3 b 1 b 0 b 3 − b 2 b 2 − b 3 b 0 b 1 b 3 b 2 − b 1 b 0 ] ⋅ [ a 0 a 1 a 2 a 3 ] = [ a 0 − a 1 − a 2 − a 3 a 1 a 0 − a 3 a 2 a 2 a 3 a 0 − a 1 a 3 − a 2 a 1 a 0 ] ⋅ [ b 0 b 1 b 2 b 3 ] \begin{aligned} a\otimes b&=\begin{bmatrix}a_0\\a_1\\a_2\\a_3\end{bmatrix}\otimes\begin{bmatrix}b_0\\b_1\\b_2\\b_3\end{bmatrix}\\ &=\begin{bmatrix}a_0b_0-a_1b_1-a_2b_2-a_3b_3\\ a_0b_1+a_1b_0+a_2b_3-a_3b_2\\ a_0b_2-a_1b_3+a_2b_0+a_3b_1\\ a_0b_3+a_1b_2-a_2b_1+a_3b_0\end{bmatrix}\\ &=\begin{bmatrix} b_0&-b_1&-b_2&-b_3\\ b_1&b_0&b_3&-b_2\\ b_2&-b_3&b_0&b_1\\ b_3&b_2&-b_1&b_0 \end{bmatrix}\cdot \begin{bmatrix}a_0\\a_1\\a_2\\a_3\end{bmatrix}\\ &=\begin{bmatrix} a_0&-a_1&-a_2&-a_3\\ a_1&a_0&-a_3&a_2\\ a_2&a_3&a_0&-a_1\\ a_3&-a_2&a_1&a_0 \end{bmatrix}\cdot \begin{bmatrix}b_0\\b_1\\b_2\\b_3\end{bmatrix} \end{aligned} ab=a0a1a2a3b0b1b2b3=a0b0a1b1a2b2a3b3a0b1+a1b0+a2b3a3b2a0b2a1b3+a2b0+a3b1a0b3+a1b2a2b1+a3b0=b0b1b2b3b1b0b3b2b2b3b0b1b3b2b1b0a0a1a2a3=a0a1a2a3a1a0a3a2a2a3a0a1a3a2a1a0b0b1b2b3
a = [ a s , a v ] T a=[a_s, a_v]^T a=[as,av]T b = [ b s , b v ] T b=[b_s, b_v]^T b=[bs,bv]T,下标 s s s表示实部,下标 v v v表示虚部,则两者的乘积可表示为矩阵形式:
a ⊗ b = [ b s − b v T b v b s I − ( b v × ) ] [ a s a v ] = [ a s − a v T a v a s I + ( a v × ) ] [ b s b v ] \begin{aligned} a\otimes b &=\begin{bmatrix}b_s&-b_v^T\\b_v&b_sI-(b_v\times)\end{bmatrix}\begin{bmatrix}a_s\\a_v\end{bmatrix}\\ &=\begin{bmatrix}a_s&-a_v^T\\a_v&a_sI+(a_v\times)\end{bmatrix}\begin{bmatrix}b_s\\b_v\end{bmatrix} \end{aligned} ab=[bsbvbvTbsI(bv×)][asav]=[asavavTasI+(av×)][bsbv]
其中 v × v\times v×表示向量 v v v反对称矩阵,如下:
v × = [ 0 − v 3 v 2 v 3 0 − v 1 − v 2 v 1 0 ] v\times=\begin{bmatrix}0&-v_3&v_2\\v_3&0&-v_1\\-v_2&v_1&0\end{bmatrix} v×=0v3v2v30v1v2v10
假设 B A q _B^Aq BAq C B q _C^Bq CBq分别表示 B B B系→ A A A系、 C C C系→ B B B系的旋转,则 C C C系→ A A A系的旋转可表示为:
C A q = B A q ⊗ C B q _C^Aq={_B^Aq}\otimes {_C^Bq} CAq=BAqCBq
通过如下算子即可将 B B B系中的向量 B v ^Bv Bv转换为 A A A系中的向量 A v ^Av Av
[ 0 A v ] = B A q ⊗ [ 0 B v ] ⊗ B A q ∗ \begin{bmatrix}0\\^Av\end{bmatrix}={_B^Aq}\otimes \begin{bmatrix}0\\{^Bv}\end{bmatrix} \otimes {_B^Aq^*} [0Av]=BAq[0Bv]BAq
B A q = [ q 0 q 1 q 2 q 3 ] = [ q s q v ] {_B^Aq}=\begin{bmatrix}q_0\\q_1\\q_2\\q_3\end{bmatrix}=\begin{bmatrix}q_s\\q_v\end{bmatrix} BAq=q0q1q2q3=[qsqv] B A q ∗ = [ q 0 − q 1 − q 2 − q 3 ] = [ q s − q v ] {_B^Aq^*}=\begin{bmatrix}q_0\\-q_1\\-q_2\\-q_3\end{bmatrix}=\begin{bmatrix}q_s\\-q_v\end{bmatrix} BAq=q0q1q2q3=[qsqv],带入四元数乘法的矩阵表示形式,得到:
A v = B A q ⊗ [ 0 A v ] ⊗ B A q ∗ = [ q s q v T − q v q s I + ( q v × ) ] [ q s − q v T q v q s I + ( q v × ) ] [ 0 A v ] = [ 1 0 T 0 q v q v T + q s 2 I + 2 q s ( q v × ) + ( q v × ) 2 ] [ 0 A v ] \begin{aligned} ^Av&={_B^Aq}\otimes \begin{bmatrix}0\\^Av\end{bmatrix} \otimes {_B^Aq^*}\\ &=\begin{bmatrix}q_s&q_v^T\\-q_v&q_sI+(q_v\times)\end{bmatrix} \begin{bmatrix}q_s&-q_v^T\\q_v&q_sI+(q_v\times)\end{bmatrix}\begin{bmatrix}0\\^Av\end{bmatrix}\\ &=\begin{bmatrix}1&0^T\\0&q_vq_v^T+q_s^2I+2q_s(q_v\times)+(q_v\times)^2\end{bmatrix}\begin{bmatrix}0\\^Av\end{bmatrix} \end{aligned} Av=BAq[0Av]BAq=[qsqvqvTqsI+(qv×)][qsqvqvTqsI+(qv×)][0Av]=[100TqvqvT+qs2I+2qs(qv×)+(qv×)2][0Av]
其中,矩阵 R 3 × 3 = q v q v T + q s 2 I + 2 q s ( q v × ) + ( q v × ) 2 R_{3\times 3}=q_vq_v^T+q_s^2I+2q_s(q_v\times)+(q_v\times)^2 R3×3=qvqvT+qs2I+2qs(qv×)+(qv×)2就是旋转矩阵的四元数表示形式,展开如下:
R = [ 2 ( q 0 2 + q 1 2 ) − 1 2 ( q 1 q 2 − q 0 q 3 ) 2 ( q 1 q 3 + q 0 q 2 ) 2 ( q 1 q 2 + q 0 q 3 ) 2 ( q 0 2 + q 2 2 ) − 1 2 ( q 2 q 3 − q 0 q 1 ) 2 ( q 1 q 3 − q 0 q 2 ) 2 ( q 2 q 3 + q 0 q 1 ) 2 ( q 0 2 + q 3 2 ) − 1 ] R=\begin{bmatrix} 2(q_0^2+q_1^2)-1&2(q_1q_2-q_0q_3)&2(q_1q_3+q_0q_2)\\ 2(q_1q_2+q_0q_3)&2(q_0^2+q_2^2)-1&2(q_2q_3-q_0q_1)\\ 2(q_1q_3-q_0q_2)&2(q_2q_3+q_0q_1)&2(q_0^2+q_3^2)-1 \end{bmatrix} R=2(q02+q12)12(q1q2+q0q3)2(q1q3q0q2)2(q1q2q0q3)2(q02+q22)12(q2q3+q0q1)2(q1q3+q0q2)2(q2q3q0q1)2(q02+q32)1
向量 A v ^Av Av通过左乘 R R R,也能够转换为 B v ^Bv Bv,即: B v = R ⋅ A v ^Bv=R\cdot{^Av} Bv=RAv

通过如下公式可将四元数转换为欧拉角:
ϕ = A t a n 2 ( 2 q 2 q 3 − 2 q 0 q 1 , 2 q 0 2 + 2 q 3 2 − 1 ) θ = − s i n − 1 ( 2 q 1 q 3 + 2 q 0 q 2 ) ψ = A t a n 2 ( 2 q 1 q 2 − 2 q 0 q 3 , 2 q 0 2 + 2 q 1 2 − 1 ) \begin{aligned} \phi&=Atan2(2q_2q_3-2q_0q_1,2q_0^2+2q_3^2-1)\\ \theta&=-sin^{-1}(2q_1q_3+2q_0q_2)\\ \psi&=Atan2(2q_1q_2-2q_0q_3,2q_0^2+2q_1^2-1) \end{aligned} ϕθψ=Atan2(2q2q32q0q1,2q02+2q321)=sin1(2q1q3+2q0q2)=Atan2(2q1q22q0q3,2q02+2q121)
其中

  • ϕ \phi ϕ:横滚角(roll)
  • θ \theta θ:俯仰角(pitch)
  • ψ \psi ψ:偏航角(yaw)

姿态解算原理

基于角速度积分得到的姿态

对角速度进行积分,可以得到角度,四元数微分方程的推导过程较为复杂,本文直接给出,如下:

b n q ˙ = 1 2 b n q ⊗ [ 0 b ω ] {_b^n\dot{q}}=\frac{1}{2}{_b^nq}\otimes \begin{bmatrix}0\\{^b\omega}\end{bmatrix} bnq˙=21bnq[0bω]
其中,角标 b b b表示机体(body)坐标系,角标 n n n表示导航(navigation)坐标系, ω \omega ω表示角速度。使用前向差分法对其进行离散化:
q ˙ = q t − q t − 1 △ t = 1 2 q t − 1 ⊗ [ 0 ω ] \dot q = \frac{q_t-q_{t-1}}{\triangle t}=\frac{1}{2}q_{t-1}\otimes \begin{bmatrix}0\\{\omega}\end{bmatrix} q˙=tqtqt1=21qt1[0ω]
离散后的方程如下:
b n q ω , t = b n q t − 1 + 1 2 b n q t − 1 ⊗ [ 0 b ω ] △ t \begin{aligned} {_b^nq}_{\omega,t}&={_b^nq}_{t-1} +\frac{1}{2}{_b^nq}_{t-1}\otimes \begin{bmatrix}0\\{^b\omega}\end{bmatrix}\triangle t \end{aligned} bnqω,t=bnqt1+21bnqt1[0bω]t

b ω ^b\omega bω表示机体坐标系下的角速度,也就是陀螺仪的测量值。

基于加速度计观测得到的姿态

对角速度积分可以得到机体的姿态,但是这个姿态会随着时间漂移,还需要引入其他观测量来对积分得到的姿态进行校正,如使用加速度计测量得到的三轴加速度。

前面说到,旋转矩阵也是一种姿态的表示方法,它是一个正交矩阵,即满足: R T = R − 1 R^T=R^{-1} RT=R1。除了使用四元数表示外,它还可以用欧拉角表示:
KaTeX parse error: Expected 'EOF', got '&' at position 3: R&̲=\begin{bmatrix…
其中: ϕ \phi ϕ表示横滚角(roll), θ \theta θ表示俯仰角(pitch), ψ \psi ψ表示偏航角(yaw)

如果用 R b n R_b^n Rbn来表示 b b b系到 n n n系的旋转矩阵,则 b b b系中的向量 b v ^bv bv通过左乘 R b n R_b^n Rbn即可转换为 n n n系中的向量 n v ^nv nv,即:
n v = R b n ⋅ b v ^nv=R_b^n\cdot{^bv} nv=Rbnbv
那么如何用加速度计的三轴观测值得到机体的姿态呢?我们知道,当IMU在导航坐标系( n n n系)静止时,加速度计的输出为:
n a = [ 0 0 g ] ^na=\begin{bmatrix}0\\0\\g\end{bmatrix} na=00g
其中 g g g为重力加速度。此时加速度计在机体坐标系( b b b系)中的输出为:
b a = [ a x a y a z ] ^ba=\begin{bmatrix}a_x\\a_y\\a_z\end{bmatrix} ba=axayaz
n a ^na na b a ^ba ba带入 { b a = R n b ⋅ n a R n b = R b n T \begin{cases}^ba=R_n^b\cdot{^na}\\R_n^b={R_b^n}^T\end{cases} {ba=RnbnaRnb=RbnT,得:
[ a x a y a z ] = R n b ⋅ [ 0 0 g ] = [ − g s i n θ g c o s θ s i n ϕ g c o s θ c o s ϕ ] \begin{bmatrix}a_x\\a_y\\a_z\end{bmatrix}=R_n^b\cdot\begin{bmatrix}0\\0\\g\end{bmatrix}=\begin{bmatrix}-gsin\theta\\gcos\theta sin\phi\\gcos\theta cos\phi\end{bmatrix} axayaz=Rnb00g=gsinθgcosθsinϕgcosθcosϕ
解得:
ϕ = a r c t a n ( a y a z ) θ = a r c s i n ( − a x g ) \begin{aligned} \phi&=arctan(\frac{a_y}{a_z})\\ \theta&=arcsin(-\frac{a_x}{g}) \end{aligned} ϕθ=arctan(azay)=arcsin(gax)
由于偏航角 ψ \psi ψ的旋转轴与重力方向平行,所以使用加速度计没办法获得偏航角 ψ \psi ψ,还需要其他的传感器,如磁力计,这里不展开叙述。

以上只是说明使用加速度计测量值计算姿态的原理,但是在实际应用中并没有这么简单的,因为载体是会运动的,此时加速度计在 n n n系的输出值并不恒等于重力加速度

互补滤波

前面说到,陀螺仪解算得到的姿态具有良好的高频特性,但是会随着时间漂移,而加速度计解算得到的姿态具有良好的低频特性,不会随着时间漂移,但是载体剧烈运动时,往往不能解算出真实的姿态。这时可以将陀螺仪的高频特性和加速度计的低频特性相融合,得到高频、低频特性都很好的算法。

当然这里的融合不是简单地将陀螺仪和加速度计解算得到的姿态进行相加求平均,而是要用互补滤波,以下将介绍Mahony互补滤波算法。

误差定义

互补滤波是一种基于误差反馈的滤波器,首先要定义误差,Mahony算法中,用向量叉积来表示误差。向量叉积的模长如下:
∣ a × b ∣ = ∣ a ∣ ∣ b ∣ s i n θ |a\times b|=|a||b|sin\theta a×b=absinθ
a a a b b b均为单位向量,则 ∣ a × b ∣ = s i n θ |a\times b|=sin\theta a×b=sinθ,使用小角度假设,则:
∣ a × b ∣ = s i n θ ≈ θ |a\times b|=sin\theta ≈\theta a×b=sinθθ
两个向量差距越大,它们的叉积模长也越大,因此可以用向量的叉积来表示向量间的误差。接下来需要构造两个向量。

  • 加速度归一化向量

对加速度计的测量值进行归一化,得到:加速度的归一化向量
a ′ = [ a x ′ a y ′ a z ′ ] = 1 ∣ a ∣ [ a x a y a z ] a'=\begin{bmatrix}a_x'\\a_y'\\a_z'\end{bmatrix}=\frac{1}{|a|}\begin{bmatrix}a_x\\a_y\\a_z\end{bmatrix} a=axayaz=a1axayaz
Mahony源码中的相应程序为:

// 归一化加速度测量值
recipNorm = invSqrt(ax * ax + ay * ay + az * az);
ax *= recipNorm;
ay *= recipNorm;
az *= recipNorm;
  • 当前姿态估计得到重力方向向量

上一节中,当载体处于静止状态时,加速度计在 n n n系的输出恒为 [ 0 0 g ] \begin{bmatrix}0\\0\\g\end{bmatrix} 00g。如果带入四元数形式的旋转矩阵,则有
[ a x a y a z ] = R n b ⋅ [ 0 0 g ] = g [ 2 ( q 1 q 3 − q 0 q 2 ) 2 ( q 2 q 3 + q 0 q 1 ) 2 ( q 0 2 + q 3 2 ) − 1 ] \begin{bmatrix}a_x\\a_y\\a_z\end{bmatrix}=R_n^b\cdot\begin{bmatrix}0\\0\\g\end{bmatrix}=g\begin{bmatrix}2(q_1q_3-q_0q_2)\\2(q_2q_3+q_0q_1)\\2(q_0^2+q_3^2)-1\end{bmatrix} axayaz=Rnb00g=g2(q1q3q0q2)2(q2q3+q0q1)2(q02+q32)1
我们用 v = [ v x v y v z ] v=\begin{bmatrix}v_x\\v_y\\v_z\end{bmatrix} v=vxvyvz来表示重力方向向量:
v = [ v x v y v z ] = [ 2 ( q 1 q 3 − q 0 q 2 ) 2 ( q 2 q 3 + q 0 q 1 ) 2 ( q 0 2 + q 3 2 ) − 1 ] v=\begin{bmatrix}v_x\\v_y\\v_z\end{bmatrix}=\begin{bmatrix}2(q_1q_3-q_0q_2)\\2(q_2q_3+q_0q_1)\\2(q_0^2+q_3^2)-1\end{bmatrix} v=vxvyvz=2(q1q3q0q2)2(q2q3+q0q1)2(q02+q32)1
相应程序如下:

// 估计重力方向向量
halfvx = q1 * q3 - q0 * q2;
halfvy = q0 * q1 + q2 * q3;
halfvz = q0 * q0 - 0.5f + q3 * q3;

注意此处计算的向量是原始向量的一半,这样做的目的是减少乘法运算量,所以在程序开头的宏定义中,滤波系数 K p K_p Kp K i K_i Ki均为两倍的比例和积分增益。

#define twoKpDef	(2.0f * 0.5f)	// 2 * 比例增益
#define twoKiDef	(2.0f * 0.0f)	// 2 * 积分增益
  • 向量叉积得到误差

误差可以表示为:
E r r = a ′ × v Err = a'\times v Err=a×v
相应的程序为:

// 误差就是两个向量的叉积
halfex = (ay * halfvz - az * halfvy);
halfey = (az * halfvx - ax * halfvz);
halfez = (ax * halfvy - ay * halfvx);

误差补偿(补偿角速度)

使用PI控制算法来进行误差补偿,补偿量就是角速度的测量偏差,如下:
ω b i a s = K p ⋅ E r r + K i ⋅ ∫ E r r \omega_{bias}=K_p\cdot Err + K_i\cdot \int Err ωbias=KpErr+KiErr
其中 K p K_p Kp为比例系数, K i K_i Ki为积分系数。 K p K_p Kp可以表示对传感器的信任程度, K p K_p Kp越大,越信任加速度计的测量值,反之 K p K_p Kp越小,越信任陀螺仪的数据。 K i K_i Ki用于消除静态误差,也就是消除陀螺仪的零偏。

最后将补偿量加到角速度测量值上则可得到修正后的角速度。相应的程序为:

// 计算误差补偿,并应用到角速度测量值上
if(twoKi > 0.0f) {
    integralFBx += twoKi * halfex * (1.0f / sampleFreq);	// 误差积分
    integralFBy += twoKi * halfey * (1.0f / sampleFreq);
    integralFBz += twoKi * halfez * (1.0f / sampleFreq);
    gx += integralFBx;	// 应用积分反馈项
    gy += integralFBy;
    gz += integralFBz;
}
else {
    integralFBx = 0.0f;	// 防止积分饱和
    integralFBy = 0.0f;
    integralFBz = 0.0f;
}

// 应用比例反馈项
gx += twoKp * halfex;
gy += twoKp * halfey;
gz += twoKp * halfez;

姿态递推

通过前面的PI算法,可以得到修正后的角速度值。再根据前文提到的四元数微分方程对姿态进行递推即可得到融合后的姿态
b n q ω , t = b n q t − 1 + 1 2 b n q t − 1 ⊗ b ω △ t = b n q t − 1 + 1 2 △ t [ 0 − ω x − ω y − ω z ω x 0 ω z − ω y ω y − ω z 0 ω x ω z ω y − ω x 0 ] [ q 0 q 1 q 2 q 3 ] \begin{aligned} {_b^nq}_{\omega,t}&={_b^nq}_{t-1} +\frac{1}{2}{_b^nq}_{t-1}\otimes{^b\omega}\triangle t\\ &={_b^nq}_{t-1} +\frac{1}{2}\triangle t \begin{bmatrix} 0&-\omega_x&-\omega_y&-\omega_z\\ \omega_x&0&\omega_z&-\omega_y\\ \omega_y&-\omega_z&0&\omega_x\\ \omega_z&\omega_y&-\omega_x&0 \end{bmatrix}\begin{bmatrix}q_0\\q_1\\q_2\\q_3\end{bmatrix} \end{aligned} bnqω,t=bnqt1+21bnqt1bωt=bnqt1+21t0ωxωyωzωx0ωzωyωyωz0ωxωzωyωx0q0q1q2q3
相应的程序如下:

// 利用四元数微分方程进行迭代
gx *= (0.5f * (1.0f / sampleFreq));
gy *= (0.5f * (1.0f / sampleFreq));
gz *= (0.5f * (1.0f / sampleFreq));
qa = q0;
qb = q1;
qc = q2;
q0 += (-qb * gx - qc * gy - q3 * gz);
q1 += (qa * gx + qc * gz - q3 * gy);
q2 += (qa * gy - qb * gz + q3 * gx);
q3 += (qa * gz + qb * gy - qc * gx); 

// 四元数归一化,用于表示姿态的四元数,其模长必须为1
recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= recipNorm;
q1 *= recipNorm;
q2 *= recipNorm;
q3 *= recipNorm;

此时姿态还是用四元数来表示的,不是很直观,通过前文提到的公式可以将四元数转换为欧拉角:
ϕ = A t a n 2 ( 2 q 2 q 3 − 2 q 0 q 1 , 2 q 0 2 + 2 q 3 2 − 1 ) θ = − s i n − 1 ( 2 q 1 q 3 + 2 q 0 q 2 ) ψ = A t a n 2 ( 2 q 1 q 2 − 2 q 0 q 3 , 2 q 0 2 + 2 q 1 2 − 1 ) \begin{aligned} \phi&=Atan2(2q_2q_3-2q_0q_1,2q_0^2+2q_3^2-1)\\ \theta&=-sin^{-1}(2q_1q_3+2q_0q_2)\\ \psi&=Atan2(2q_1q_2-2q_0q_3,2q_0^2+2q_1^2-1) \end{aligned} ϕθψ=Atan2(2q2q32q0q1,2q02+2q321)=sin1(2q1q3+2q0q2)=Atan2(2q1q22q0q3,2q02+2q121)

调用方法

在调用Mahony解算程序前,建议先对陀螺仪和加速度计零偏进行估计。将IMU静止一段时间,这段时间角速度和加速度的均值就是陀螺仪和加速度计的零偏。程序如下:

if(!ready) 
{
    // 计算零偏
    gyro_offsets_sum[0] += imu.gyro[0];
    gyro_offsets_sum[1] += imu.gyro[1];
    gyro_offsets_sum[2] += imu.gyro[2];
    accel_offsets_sum[0] += imu.accel[0];
    accel_offsets_sum[1] += imu.accel[1];
    accel_offsets_sum[2] += imu.accel[2];
    offset_count++;
    if(offset_count > 3000)
    {
        gyroOffset[0] = gyro_offsets_sum[0] / offset_count;
        gyroOffset[1] = gyro_offsets_sum[1] / offset_count;
        gyroOffset[2] = gyro_offsets_sum[2] / offset_count;
        accelOffset[0] = accel_offsets_sum[0] / offset_count;
        accelOffset[1] = accel_offsets_sum[1] / offset_count;
        accelOffset[2] = accel_offsets_sum[2] / offset_count;
        accelOffset[2]-= 9.81; // 去除重力加速度常量

        offset_count = 0;
        gyro_offsets_sum[0] = 0;
        gyro_offsets_sum[1] = 0;
        gyro_offsets_sum[2] = 0;
        ready = true;
    }
    return;
}

// 去除陀螺仪零偏
imu.gyro[0] -= gyroOffset[0];
imu.gyro[1] -= gyroOffset[1];
imu.gyro[2] -= gyroOffset[2];
// 去除加速度计零偏
imu.accel[0] -= accelOffset[0];
imu.accel[1] -= accelOffset[1];
imu.accel[2] -= accelOffset[2];

// 调用Mahony解算程序
MahonyAHRSupdateIMU(imu.gyro[0], imu.gyro[1], imu.gyro[2], 
                 	imu.accel[0], imu.accel[1], imu.accel[2]);

参考

https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/

https://www.cnblogs.com/WangHongxi/p/12357230.html

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐