Mahony filter滤波:IMU数据融合最好的算法-论文&代码详细解读(Nonlinear Complementary Filters on the Special Orthogonal)
首先,我的系列原创教程Unleashing Robotics: Mastering Quaternion Kinematics with Python - (原创系列教程)还在继续,今天先插个我经常使用的算法的论文的技术分析和讲解,本来想将这个技术放到我的Mastering Quaternion Kinematics with Python教程中,但是为了保证教程专注性,还是分开讲解下。
这篇论文题为2006年Mahony发表了《基于特殊正交群上非线性互补滤波器》的论文(Nonlinear Complementary Filters on the Special Orthogonal Group),作者是R. Mahony, Tarek Hamel, Jean-Michel Pflimlin。
论文主要介绍了在特殊正交群SO(3)上进行姿态估计的非线性互补滤波器设计与分析,并提供了一种新的姿态融合算法。在无人机、各类需要IMU的角速度和加速度计融合得出角度的场景中得到了广泛的应用,个人认为最适合工程场景使用的算法,接下来我会从数理和代码实现角度深入剥析。
1. 引言
随着MEMS(微机电系统)技术的发展,低成本轻量化的惯性测量单元(IMU)得到广泛应用。但这类IMU输出信号往往含有高噪声和时变偏置,需要进行滤波处理以获得平滑的姿态估计和偏置校正后的角速率测量。
本方法直接在SO(3)流形上提出了一类新的非线性姿态观测器。与之前的工作相比,主要有以下几点创新:
-
直接在SO(3)上进行公式推导和稳定性分析,而非使用其他表示方法如欧拉角、四元数等。
-
提出了三种互补滤波器:直接互补滤波器、被动互补滤波器和显式互补滤波器。
-
证明了估计误差的局部指数稳定性和几乎全局渐近稳定性。
-
可以在线估计陀螺仪偏置。
-
适合在嵌入式硬件上实现。
2. 问题描述和数学准备
2.1 惯性测量模型
考虑基体坐标系{B}和惯性坐标系{A},它们之间的旋转矩阵用 R∈SO(3)R\in SO(3)R∈SO(3) 表示。假设IMU输出如下:
- 陀螺仪测量 Ωy=Ω+b+μ\Omega_{y} = \Omega + b + \muΩy=Ω+b+μ, 其中 Ω\OmegaΩ 为真实角速率, bbb 为偏置,μ\muμ 为噪声。
- 加速度计测量 a=RT(v˙−g0)+ba+μaa=R^T(\dot{v}-g_0) + b_a +\mu_aa=RT(v˙−g0)+ba+μa,其中 v˙\dot{v}v˙ 是加速度, g0g_0g0 是重力加速度,ba,μab_a,\mu_aba,μa 分别为偏置和噪声。在准静态时,可以近似 va=−RTe3v_a=-R^Te_3va=−RTe3 。
- 磁力计测量 m=RTmA+bm+μbm=R^Tm_A+b_m+\mu_bm=RTmA+bm+μb, 其中 mAm_AmA 为地磁场方向, bm,μbb_m,\mu_bbm,μb 为偏置和噪声。磁力计向量 vm=m∣m∣v_m=\frac{m}{|m|}vm=∣m∣m 。
利用加速度计和磁力计的矢量测量,可以重构出一个姿态矩阵的代数估计值RyR_yRy。
2.2 SO(3)上的姿态误差准则
定义估计矩阵 R^\hat{R}R^ 和姿态误差 R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR。论文使用如下Lyapunov函数来构建估计误差:
Etr=14∥I−R~∥2=12tr(I−R~)=2sin2(θ/2)E_{tr}=\frac{1}{4}\|I-\tilde{R}\|^2=\frac{1}{2}\mathrm{tr}(I-\tilde{R})=2sin^2(\theta/2)Etr=41∥I−R~∥2=21tr(I−R~)=2sin2(θ/2)
其中 θ\thetaθ 是旋转 R~\tilde{R}R~ 的旋转角度。驱动 EtrE_{tr}Etr 趋于零可以使估计值趋于真实值。
这个函数有以下几个优点:
它是旋转矩阵误差 R~\tilde{R}R~ 的二次型函数,类似于线性系统中的误差二次型。
它有明确的几何意义,即误差旋转角的正弦平方,可以度量 R^\hat{R}R^ 偏离 RRR 的程度。
在 R~=I\tilde{R}=IR~=I 处取得最小值0,且在 R~\tilde{R}R~ 附近是正定的,满足Lyapunov函数的条件1。
其导数 E˙tr=−kP∣ω∣2≤0\dot{E}_{tr}=-k_P|\omega|^2\leq 0E˙tr=−kP∣ω∣2≤0,其中 ω\omegaω 是旋转误差的度量,满足Lyapunov函数的条件2。
因此,这个函数是姿态误差 R~\tilde{R}R~ 的一个合适的Lyapunov函数。通过证明 EtrE_{tr}Etr 沿滤波器轨线收敛到0,就可以证明滤波器是渐近稳定的,即估计值 R^\hat{R}R^ 会收敛到真实值 RRR。作者通过巧妙构造SO(3)上的Lyapunov函数,并利用其导数为负半定这一性质,证明了所提出的互补滤波器能够实现姿态估计误差的渐近收敛。
当然如果你不知道什么是Lyapunov也没关系,这一步就是构建误差项。
我一步步推导下这个公式的来历。
首先,我们知道旋转矩阵 RRR 是正交矩阵,即 RRT=IRR^T=IRRT=I。因此,姿态误差 R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR 也是正交矩阵。
对于任意正交矩阵 QQQ,我们可以将其写成如下形式:
Q=exp(θa×)Q = \exp(\theta a^\times)Q=exp(θa×)
其中 θ\thetaθ 为旋转角度, aaa 为单位旋转轴, a×a^\timesa× 为 aaa 的反对称矩阵。
利用指数函数的泰勒展开,并注意到 (a×)3=−a×(a^\times)^3=-a^\times(a×)3=−a×,我们可以得到:
Q=I+sinθa×+(1−cosθ)(a×)2Q = I + \sin\theta a^\times + (1-\cos\theta)(a^\times)^2Q=I+sinθa×+(1−cosθ)(a×)2
现在,让我们看看 ∥I−Q∥2\|I-Q\|^2∥I−Q∥2 的计算:
∥I−Q∥2=∥(I−Q)T(I−Q)∥=∥I−QT−Q+I∥=∥2I−2cosθI∥=4(1−cosθ)∥I∥=8(1−cosθ)\begin{aligned} \|I-Q\|^2 &= \|(I-Q)^T(I-Q)\| \\ &= \|I-Q^T-Q+I\| \\ &= \|2I-2\cos\theta I\| \\ &= 4(1-\cos\theta)\|I\| \\ &= 8(1-\cos\theta) \end{aligned}∥I−Q∥2=∥(I−Q)T(I−Q)∥=∥I−QT−Q+I∥=∥2I−2cosθI∥=4(1−cosθ)∥I∥=8(1−cosθ)
其中我们用到了 QT=Q−1Q^T=Q^{-1}QT=Q−1,以及 QQQ 的展开式。
接下来,我们看看 tr(I−Q)\mathrm{tr}(I-Q)tr(I−Q) 的计算:
tr(I−Q)=tr(I)−tr(Q)=3−(3cosθ)=3(1−cosθ)\begin{aligned} \mathrm{tr}(I-Q) &= \mathrm{tr}(I) - \mathrm{tr}(Q) \\ &= 3 - (3\cos\theta) \\ &= 3(1-\cos\theta) \end{aligned}tr(I−Q)=tr(I)−tr(Q)=3−(3cosθ)=3(1−cosθ)
其中我们用到了旋转矩阵的迹等于余弦值之和。
最后,我们利用半角公式:
1−cosθ=2sin2θ21-\cos\theta = 2\sin^2\frac{\theta}{2}1−cosθ=2sin22θ
就得到了:
∥I−Q∥2=8(1−cosθ)=16sin2θ2tr(I−Q)=3(1−cosθ)=6sin2θ2\begin{aligned} \|I-Q\|^2 &= 8(1-\cos\theta) = 16\sin^2\frac{\theta}{2} \\ \mathrm{tr}(I-Q) &= 3(1-\cos\theta) = 6\sin^2\frac{\theta}{2} \end{aligned}∥I−Q∥2tr(I−Q)=8(1−cosθ)=16sin22θ=3(1−cosθ)=6sin22θ
所以,我们有:
14∥I−Q∥2=12tr(I−Q)=2sin2θ2\boxed{ \frac{1}{4}\|I-Q\|^2 = \frac{1}{2}\mathrm{tr}(I-Q) = 2\sin^2\frac{\theta}{2} }41∥I−Q∥2=21tr(I−Q)=2sin22θ
这个公式的巧妙之处在于,它将旋转矩阵的"距离"与旋转角度联系起来,而且非常容易计算。在姿态估计问题中,它提供了一个简洁而有效的误差度量。
下面给出被动互补滤波器估计姿态和偏置的伪代码:
def passive_complementary_filter(Omega_y, R_y, dt, k_P, k_I):
# 初始化估计值
R_hat = I # 初始估计为单位矩阵
b_hat = 0 # 初始偏置估计为零
while True:
# 计算误差项
R_tilde = R_hat.T @ R_y
omega = vex(Pa(R_tilde))
# 更新姿态估计
Omega_hat = Omega_y - b_hat + k_P * omega
R_hat = R_hat @ exp(Omega_hat * dt)
# 更新偏置估计
b_hat = b_hat - k_I * omega * dt
yield R_hat, b_hat
其中 Pa 表示反对称矩阵投影, vex 是其逆运算, exp 是SO(3)上的指数映射,可以用罗德里格斯公式计算。k_P 和 k_I 分别是比例和积分增益。
总的来说,互补滤波的核心思想是利用Lyapunov函数构造估计误差,并设计修正项使其递减。通过巧妙选取旋转矩阵的误差函数,可以得到简洁高效的滤波器结构。在此基础上,论文进一步提出了被动、显式等改进形式,并证明了它们的稳定性。这些互补滤波器为SO(3)上的姿态估计提供了新的思路。
3. SO(3)上的互补滤波器设计
3.1 直接互补滤波器
假设角速率 Ω\OmegaΩ 和重构姿态 RyR_yRy 已知,直接互补滤波器设计如下:
R^˙=(RyΩy+kPR^ω)×R^ω=vex(Pa(R~))=vex(Pa(R^TRy))\begin{aligned} \dot{\hat{R}} &= (R_y\Omega_y+k_P\hat{R}\omega)_\times \hat{R} \\ \omega &= \mathrm{vex}(P_a(\tilde{R})) = \mathrm{vex}(P_a(\hat{R}^TR_y)) \end{aligned}R^˙ω=(RyΩy+kPR^ω)×R^=vex(Pa(R~))=vex(Pa(R^TRy))
其中 kP>0k_P>0kP>0 为增益, ω\omegaω 为修正项。对误差系统进行Lyapunov分析可得:
E˙tr=−kP2tr(ω×R~)=−kP∣ω∣2\dot{E}_{tr}=-\frac{k_P}{2}\mathrm{tr}(\omega_\times \tilde{R})=-k_P|\omega|^2E˙tr=−2kPtr(ω×R~)=−kP∣ω∣2
这说明误差是渐近稳定的。
我再来详细解释下这部分内容如何在IMU融合姿态中实现:在姿态估计问题中,除了IMU给出的三个轴的角速度直接积分得到角度外,我们通常可以从加速度计和磁力计的测量值中得到关于姿态的部分信息,我们将其称为"重构姿态"。具体来说:
-
加速度计测量重力在体坐标系下的分量,如果物体处于静止或匀速运动状态,那么加速度计读数 aaa 就等于重力向量在体坐标系下的投影,即:
a=RT[0,0,g]Ta = R^T[0,0,g]^Ta=RT[0,0,g]T
其中 ggg 为重力加速度大小。从这个关系可以得到姿态矩阵 RRR 的部分信息。
-
磁力计测量地磁场在体坐标系下的分量,设地磁场在惯性坐标系下的单位方向向量为 mmm,则磁力计读数 bbb 满足:
b=RTmb = R^Tmb=RTm
同样,这个关系也提供了姿态矩阵 RRR 的部分信息,这就是重构姿态。
通过适当地组合加速度计和磁力计的信息,我们可以得到一个姿态的估计值,即"重构姿态"RyR_yRy。这个过程通常用到最小二乘法,奇异值分解等优化算法,或是最简单直接加权平均(建议最好不要这样处理,会损失精度)。
下面给出一个Python代码示例,演示如何从9轴IMU数据重构姿态:
import numpy as np
def reconstruct_attitude(accel, mag):
"""
从加速度计和磁力计数据重构姿态
:param accel: 加速度计读数,3x1
:param mag: 磁力计读数,3x1
:return: 重构的姿态矩阵
"""
# 归一化加速度计读数
accel = accel / np.linalg.norm(accel)
# 假设磁力计读数已经校准,即去除了硬铁和软铁效应
mag = mag / np.linalg.norm(mag)
# 计算东北天坐标系下的重力和磁场方向
g = np.array([0, 0, 1])
m = np.array([0, 1, 0])
# 构造最小二乘问题
A = np.vstack((accel, mag))
b = np.vstack((g, m))
# 用SVD求解最小二乘问题
U, _, Vt = np.linalg.svd(A.T @ b)
R = U @ Vt
# 保证旋转矩阵满足右手定则
if np.linalg.det(R) < 0:
R = -R
return R
这个函数的输入是加速度计和磁力计的读数,输出是重构的姿态矩阵。其中主要步骤是:
- 归一化加速度计和磁力计读数
- 根据加速度计和磁力计的参考方向(这里假设为重力向下,磁场向北),构造最小二乘问题
- 用奇异值分解(SVD)求解最小二乘问题,得到姿态矩阵
- 检查姿态矩阵行列式的符号,保证满足右手定则
得到重构姿态后,就可以用它来修正陀螺仪积分得到的姿态估计值,这就是互补滤波的核心思想。直接互补滤波器就是将重构姿态 RyR_yRy 直接代入滤波器方程:
R^˙=(RyΩy+kPR^ω)×R^\dot{\hat{R}} = (R_y\Omega_y+k_P\hat{R}\omega)_\times \hat{R}R^˙=(RyΩy+kPR^ω)×R^
其中修正项 ω\omegaω 由姿态估计值 R^\hat{R}R^ 和重构姿态 RyR_yRy 计算得到:
ω=vex(Pa(R^TRy))\omega = \mathrm{vex}(P_a(\hat{R}^TR_y))ω=vex(Pa(R^TRy))
这样,互补滤波器就可以融合陀螺仪、加速度计和磁力计的信息,得到更准确和稳定的姿态估计。Lyapunov分析保证了滤波器的收敛性和稳定性。
下面我来推导这个方程。
直接互补滤波器的推导与实现
符号说明
- RRR: 真实姿态矩阵
- R^\hat{R}R^: 估计姿态矩阵
- R~\tilde{R}R~: 姿态误差矩阵,R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR
- Ω\OmegaΩ: 真实角速度
- Ωy\Omega_yΩy: 陀螺仪测量角速度
- RyR_yRy: 重构姿态矩阵
- ω\omegaω: 修正项
- kPk_PkP: 比例增益
- PaP_aPa: 反对称矩阵投影算子
- vex\mathrm{vex}vex: PaP_aPa 的逆运算
首先,真实姿态 RRR 满足运动学方程:
R˙=RΩ×\dot{R} = R\Omega_\timesR˙=RΩ×
而估计姿态 R^\hat{R}R^ 的运动学方程可以写为:
R^˙=R^(Ωy+δω)×\dot{\hat{R}} = \hat{R}(\Omega_y+\delta\omega)_\timesR^˙=R^(Ωy+δω)×
其中 δω\delta\omegaδω 是一个修正角速度,用于补偿陀螺仪误差。我们的目标是设计 δω\delta\omegaδω,使估计姿态 R^\hat{R}R^ 收敛到真实姿态 RRR。
为此,我们引入姿态误差 R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR,并设计如下的Lyapunov函数:
Etr=12tr(I−R~)E_{tr} = \frac{1}{2}\mathrm{tr}(I-\tilde{R})Etr=21tr(I−R~)
对 EtrE_{tr}Etr 求导,并利用 R~\tilde{R}R~ 的运动学方程 R~˙=R~(Ω−R^T(Ωy+δω))×\dot{\tilde{R}}=\tilde{R}(\Omega-\hat{R}^T(\Omega_y+\delta\omega))_\timesR~˙=R~(Ω−R^T(Ωy+δω))×,可得:
E˙tr=−12tr(R~˙)=−12tr(R~(Ω−R^T(Ωy+δω))×)=−12tr(Pa(R~)R^T(Ωy−Ω+δω)×)=⟨vex(Pa(R~)),R^T(Ωy−Ω+δω)⟩\begin{aligned} \dot{E}_{tr} &= -\frac{1}{2}\mathrm{tr}(\dot{\tilde{R}}) \\ &= -\frac{1}{2}\mathrm{tr}(\tilde{R}(\Omega-\hat{R}^T(\Omega_y+\delta\omega))_\times) \\ &= -\frac{1}{2}\mathrm{tr}(P_a(\tilde{R})\hat{R}^T(\Omega_y-\Omega+\delta\omega)_\times) \\ &= \langle\mathrm{vex}(P_a(\tilde{R})),\hat{R}^T(\Omega_y-\Omega+\delta\omega)\rangle \end{aligned}E˙tr=−21tr(R~˙)=−21tr(R~(Ω−R^T(Ωy+δω))×)=−21tr(Pa(R~)R^T(Ωy−Ω+δω)×)=⟨vex(Pa(R~)),R^T(Ωy−Ω+δω)⟩
为了使 E˙tr≤0\dot{E}_{tr}\leq0E˙tr≤0,我们可以选择:
δω=kPvex(Pa(R~))+R^T(Ω−Ωy)\delta\omega = k_P\mathrm{vex}(P_a(\tilde{R})) + \hat{R}^T(\Omega-\Omega_y)δω=kPvex(Pa(R~))+R^T(Ω−Ωy)
将其代入 R^˙\dot{\hat{R}}R^˙ 的表达式,并利用 R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR,可得:
R^˙=R^(Ωy+kPvex(Pa(R~))+R^T(Ω−Ωy))×=(R^Ωy+kPR^vex(Pa(R^TR))+RΩ−RΩy)×R^=(RΩ+kPR^vex(Pa(R^TRy)))×R^\begin{aligned} \dot{\hat{R}} &= \hat{R}(\Omega_y+k_P\mathrm{vex}(P_a(\tilde{R}))+\hat{R}^T(\Omega-\Omega_y))_\times \\ &= (\hat{R}\Omega_y+k_P\hat{R}\mathrm{vex}(P_a(\hat{R}^TR))+R\Omega-R\Omega_y)_\times\hat{R} \\ &= (R\Omega+k_P\hat{R}\mathrm{vex}(P_a(\hat{R}^TR_y)))_\times\hat{R} \end{aligned}R^˙=R^(Ωy+kPvex(Pa(R~))+R^T(Ω−Ωy))×=(R^Ωy+kPR^vex(Pa(R^TR))+RΩ−RΩy)×R^=(RΩ+kPR^vex(Pa(R^TRy)))×R^
其中最后一步利用了重构姿态 RyR_yRy 作为真实姿态 RRR 的估计值。这就得到了直接互补滤波器的核心方程。
Python实现
下面给出直接互补滤波器的Python实现:
import numpy as np
from scipy.spatial.transform import Rotation
def direct_complementary_filter(gyro, accel, mag, dt, kp):
"""
直接互补滤波器
:param gyro: 陀螺仪读数,3x1
:param accel: 加速度计读数,3x1
:param mag: 磁力计读数,3x1
:param dt: 采样时间
:param kp: 比例增益
:return: 估计姿态矩阵
"""
# 归一化加速度计和磁力计读数
accel = accel / np.linalg.norm(accel)
mag = mag / np.linalg.norm(mag)
# 重构姿态
R_y = reconstruct_attitude(accel, mag)
# 初始化估计值
R_hat = Rotation.from_matrix(np.eye(3))
# 滤波器更新
omega = vex(Pa(R_hat.as_matrix().T @ R_y))
delta_omega = kp * omega
R_omega = Rotation.from_rotvec((gyro + delta_omega) * dt)
R_hat = R_omega * R_hat
return R_hat.as_matrix()
def reconstruct_attitude(accel, mag):
"""
从加速度计和磁力计数据重构姿态
:param accel: 加速度计读数,3x1
:param mag: 磁力计读数,3x1
:return: 重构的姿态矩阵
"""
... 上一部分给出了
def Pa(R):
"""
反对称矩阵投影算子
:param R: 旋转矩阵
:return: R的反对称部分
"""
return 0.5 * (R - R.T)
def vex(R):
"""
反对称矩阵到向量的转换
:param R: 3x3反对称矩阵
:return: 3x1向量
"""
return np.array([R[2, 1], R[0, 2], R[1, 0]])
这个实现中,我们首先从加速度计和磁力计数据重构姿态 RyR_yRy,然后根据陀螺仪数据和修正项更新姿态估计 R^\hat{R}R^。其中 Pa 和 vex 函数分别实现了反对称矩阵投影算子及其逆运算。
直接互补滤波器的优点是结构简单,易于实现和调试。但其缺点是重构姿态 RyR_yRy 的误差会直接引入滤波器,影响估计精度。针对这一问题,论文还提出了间接互补滤波器等改进方案。
3.2 被动互补滤波器
考虑到直接滤波器中重构姿态的高频噪声会耦合进反馈,提出了被动互补滤波器:
R^˙=(R^Ωy+kPR^ω)×R^\dot{\hat{R}} = (\hat{R}\Omega_y+k_P\hat{R}\omega)_\times \hat{R}R^˙=(R^Ωy+kPR^ω)×R^
这里预测项使用估计姿态 R^\hat{R}R^ 来映射角速率。Lyapunov分析与直接滤波器类似。滤波器结构可以进一步化简,避免将 Ωy\Omega_yΩy 和 ω\omegaω 变换到惯性系:
R^˙=R^(Ωy+kPω)×\dot{\hat{R}} = \hat{R}(\Omega_y+k_P\omega)_\timesR^˙=R^(Ωy+kPω)×
我依然进行下推导便于你理解,实际上在工程中你直接拿最终公式用就可以,考虑到部分读者是研究人员有理解算法的需求,我还是推导一下。
被动互补滤波器的推导与实现
符号说明
- RRR: 真实姿态矩阵
- R^\hat{R}R^: 估计姿态矩阵
- R~\tilde{R}R~: 姿态误差矩阵,R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR
- Ω\OmegaΩ: 真实角速度
- Ωy\Omega_yΩy: 陀螺仪测量角速度
- RyR_yRy: 重构姿态矩阵
- ω\omegaω: 修正项
- kPk_PkP: 比例增益
- PaP_aPa: 反对称矩阵投影算子
- vex\mathrm{vex}vex: PaP_aPa 的逆运算
滤波器方程推导
被动互补滤波器的核心方程为:
R^˙=(R^Ωy+kPR^ω)×R^\dot{\hat{R}} = (\hat{R}\Omega_y+k_P\hat{R}\omega)_\times \hat{R}R^˙=(R^Ωy+kPR^ω)×R^
其中修正项 ω\omegaω 仍由下式计算:
ω=vex(Pa(R^TRy))\omega = \mathrm{vex}(P_a(\hat{R}^TR_y))ω=vex(Pa(R^TRy))
与直接互补滤波器不同,被动互补滤波器在预测项中使用估计姿态 R^\hat{R}R^ 来映射角速度测量值 Ωy\Omega_yΩy。这样做的好处是避免了将重构姿态 RyR_yRy 的噪声引入预测项。
下面我们来推导被动互补滤波器的稳定性。姿态误差 R~\tilde{R}R~ 的运动学方程为:
R~˙=R^˙TR+R^TR˙=((R^Ωy+kPR^ω)×R^)TR+R^TRΩ×=−(Ωy+kPω)×R~+R~Ω×=(R~Ω−Ωy−kPω)×R~\begin{aligned} \dot{\tilde{R}} &= \dot{\hat{R}}^TR + \hat{R}^T\dot{R} \\ &= ((\hat{R}\Omega_y+k_P\hat{R}\omega)_\times \hat{R})^TR + \hat{R}^TR\Omega_\times \\ &= -(\Omega_y+k_P\omega)_\times\tilde{R} + \tilde{R}\Omega_\times \\ &= (\tilde{R}\Omega-\Omega_y-k_P\omega)_\times\tilde{R} \end{aligned}R~˙=R^˙TR+R^TR˙=((R^Ωy+kPR^ω)×R^)TR+R^TRΩ×=−(Ωy+kPω)×R~+R~Ω×=(R~Ω−Ωy−kPω)×R~
对Lyapunov函数 Etr=12tr(I−R~)E_{tr}=\frac{1}{2}\mathrm{tr}(I-\tilde{R})Etr=21tr(I−R~) 求导,可得:
E˙tr=−12tr(R~˙)=−12tr((R~Ω−Ωy−kPω)×R~)=12tr(R~(R~Ω−Ωy−kPω)×)=12tr(Pa(R~)(R~Ω−Ωy−kPω)×)=−⟨vex(Pa(R~)),R~Ω−Ωy−kPω⟩=−⟨vex(Pa(R~)),R~Ω−Ωy⟩−kP∥vex(Pa(R~))∥2\begin{aligned} \dot{E}_{tr} &= -\frac{1}{2}\mathrm{tr}(\dot{\tilde{R}}) \\ &= -\frac{1}{2}\mathrm{tr}((\tilde{R}\Omega-\Omega_y-k_P\omega)_\times\tilde{R}) \\ &= \frac{1}{2}\mathrm{tr}(\tilde{R}(\tilde{R}\Omega-\Omega_y-k_P\omega)_\times) \\ &= \frac{1}{2}\mathrm{tr}(P_a(\tilde{R})(\tilde{R}\Omega-\Omega_y-k_P\omega)_\times) \\ &= -\langle\mathrm{vex}(P_a(\tilde{R})),\tilde{R}\Omega-\Omega_y-k_P\omega\rangle \\ &= -\langle\mathrm{vex}(P_a(\tilde{R})),\tilde{R}\Omega-\Omega_y\rangle - k_P\|\mathrm{vex}(P_a(\tilde{R}))\|^2 \end{aligned}E˙tr=−21tr(R~˙)=−21tr((R~Ω−Ωy−kPω)×R~)=21tr(R~(R~Ω−Ωy−kPω)×)=21tr(Pa(R~)(R~Ω−Ωy−kPω)×)=−⟨vex(Pa(R~)),R~Ω−Ωy−kPω⟩=−⟨vex(Pa(R~)),R~Ω−Ωy⟩−kP∥vex(Pa(R~))∥2
如果真实角速度 Ω\OmegaΩ 与姿态误差 R~\tilde{R}R~ 不相关,那么第一项的均值为零,而第二项始终非正。因此,我们可以期望 EtrE_{tr}Etr 在被动互补滤波器作用下收敛到零,即 R~\tilde{R}R~ 收敛到单位矩阵 III。
进一步,我们可以将被动互补滤波器改写为如下形式:
R^˙=R^(Ωy+kPω)×\dot{\hat{R}} = \hat{R}(\Omega_y+k_P\omega)_\timesR^˙=R^(Ωy+kPω)×
这种形式避免了将 Ωy\Omega_yΩy 和 ω\omegaω 变换到惯性系,减少了计算量。
Python实现
下面给出被动互补滤波器的Python实现:
import numpy as np
from scipy.spatial.transform import Rotation
def passive_complementary_filter(gyro, accel, mag, dt, kp):
"""
被动互补滤波器
:param gyro: 陀螺仪读数,3x1
:param accel: 加速度计读数,3x1
:param mag: 磁力计读数,3x1
:param dt: 采样时间
:param kp: 比例增益
:return: 估计姿态矩阵
"""
# 归一化加速度计和磁力计读数
accel = accel / np.linalg.norm(accel)
mag = mag / np.linalg.norm(mag)
# 重构姿态
R_y = reconstruct_attitude(accel, mag)
# 初始化估计值
R_hat = Rotation.from_matrix(np.eye(3))
# 滤波器更新
omega = vex(Pa(R_hat.as_matrix().T @ R_y))
R_omega = Rotation.from_rotvec((gyro + kp * omega) * dt)
R_hat = R_hat * R_omega
return R_hat.as_matrix()
def reconstruct_attitude(accel, mag):
"""
从加速度计和磁力计数据重构姿态
:param accel: 加速度计读数,3x1
:param mag: 磁力计读数,3x1
:return: 重构的姿态矩阵
"""
... # 此处省略,同个给你的代码
def Pa(R):
"""
反对称矩阵投影算子
:param R: 旋转矩阵
:return: R的反对称部分
"""
return 0.5 * (R - R.T)
def vex(R):
"""
反对称矩阵到向量的转换
:param R: 3x3反对称矩阵
:return: 3x1向量
"""
return np.array([R[2, 1], R[0, 2], R[1, 0]])
与直接互补滤波器相比,被动互补滤波器的实现主要有两点不同:
-
在滤波器更新中,先计算旋转矢量
gyro + kp * omega,再将其转换为旋转矩阵R_omega,最后用R_hat * R_omega更新姿态估计。这避免了将 Ωy\Omega_yΩy 和 ω\omegaω 变换到惯性系。 -
在滤波器更新的最后,直接用
R_hat * R_omega更新姿态估计,而不是用R_omega * R_hat。这是因为在被动互补滤波器中,我们希望用 R^\hat{R}R^ 左乘角速度,而不是右乘。
总的来说,被动互补滤波器通过巧妙的方程设计,避免了直接互补滤波器中的一些问题,提高了滤波器的鲁棒性和计算效率。同时,它也为后续的显式互补滤波器奠定了基础。
4. 滤波器的稳定性分析
4.1 带偏置估计的直接互补滤波器
考虑系统方程:
R˙=RΩ×,Ωy=Ω+b+μ\dot{R}=R\Omega_\times, \quad \Omega_y=\Omega+b+\muR˙=RΩ×,Ωy=Ω+b+μ
直接互补滤波器带偏置估计的形式为:
R^˙=(Ry(Ωy−b^)+kPR^ω)×R^b^˙=−kIω\begin{aligned} \dot{\hat{R}} &= (R_y(\Omega_y-\hat{b})+k_P\hat{R}\omega)_\times \hat{R} \\ \dot{\hat{b}} &= -k_I\omega \end{aligned}R^˙b^˙=(Ry(Ωy−b^)+kPR^ω)×R^=−kIω
其中 kI>0k_I>0kI>0 为积分增益。定义 Lyapunov 函数:
V(t)=12tr(I−R~)+12kI∣b~∣2,b~=b−b^V(t)=\frac{1}{2}\mathrm{tr}(I-\tilde{R})+\frac{1}{2k_I}|\tilde{b}|^2, \quad \tilde{b}=b-\hat{b}V(t)=21tr(I−R~)+2kI1∣b~∣2,b~=b−b^
对其求导并利用 tr([R~,Ω×])=0\mathrm{tr}([\tilde{R},\Omega_\times])=0tr([R~,Ω×])=0,可得:
V˙=−kP∣ω∣2\dot{V}=-k_P|\omega|^2V˙=−kP∣ω∣2
根据LaSalle不变集原理,系统状态将收敛到最大不变集:
M={(R~,b~):Pa(R~)=0,b~×R~=0}M=\{(\tilde{R},\tilde{b}): P_a(\tilde{R})=0, \tilde{b}\times \tilde{R}=0 \}M={(R~,b~):Pa(R~)=0,b~×R~=0}
论文进一步分析了不变集 MMM 的稳定性,得到如下结论:
-
平衡点 (I,0)(I,0)(I,0) 是局部指数稳定的。
-
除了零测集之外的所有初值,估计值都会渐近收敛到真实值。
4.2 带偏置估计的被动互补滤波器
被动互补滤波器的偏置估计形式为:
R^˙=R^(Ωy−b^+kPω)×b^˙=−kIω\begin{aligned} \dot{\hat{R}} &= \hat{R}(\Omega_y-\hat{b}+k_P\omega)_\times \\ \dot{\hat{b}} &= -k_I\omega \end{aligned}R^˙b^˙=R^(Ωy−b^+kPω)×=−kIω
其误差动力学满足:
R~˙=[R~,Ω×]−kPω×R~−b~×R~,b~˙=kIω\dot{\tilde{R}}=[\tilde{R},\Omega_\times]-k_P\omega_\times \tilde{R}-\tilde{b}_\times \tilde{R}, \quad \dot{\tilde{b}}=k_I\omegaR~˙=[R~,Ω×]−kPω×R~−b~×R~,b~˙=kIω
取相同的 Lyapunov 函数,其导数为:
V˙=−kP∣ω∣2\dot{V}=-k_P|\omega|^2V˙=−kP∣ω∣2
由于存在非自治项 Ω(t)\Omega(t)Ω(t),不能直接应用LaSalle不变集原理,而是使用Barbalat引理证明 ω→0\omega\to 0ω→0 。再进一步分析得到如下结论:
-
除了平衡点 (I,0)(I,0)(I,0),还存在不稳定平衡点集合 U0={R~:tr(R~)=−1,b~=0}U_0=\{\tilde{R}:\mathrm{tr}(\tilde{R})=-1, \tilde{b}=0\}U0={R~:tr(R~)=−1,b~=0}。
-
平衡点 (I,0)(I,0)(I,0) 依然局部指数稳定。
-
除了 U0U_0U0 内的零测集,其他所有初值的估计值都收敛到真实值。
这里的关键是要求 Ω(t)\Omega(t)Ω(t) 与 R~\tilde{R}R~ “渐近独立”,这样 Ω(t)\Omega(t)Ω(t) 的扰动可以保证 b~→0\tilde{b} \to 0b~→0。
5. 显式互补滤波器
为了避免在滤波器中重构姿态 RyR_yRy,提出了显式互补滤波器。考虑系统直接输出若干惯性方向的观测值:
vi=RTv0i+μi,i=1,⋯ ,nv_i=R^Tv_{0i}+\mu_i, \quad i=1,\cdots,nvi=RTv0i+μi,i=1,⋯,n
其中 v0iv_{0i}v0i 为惯性坐标系中的已知参考方向。定义如下代价函数:
Ei=1−⟨v^i,vi⟩=1−tr(R^Tv0iv0iTR)Emes=∑i=1nkiEi=∑i=1nki−tr(R~M),M=RTM0R,M0=∑i=1nkiv0iv0iT\begin{aligned} E_i &= 1-\langle \hat{v}_i,v_i \rangle = 1-\mathrm{tr}(\hat{R}^Tv_{0i}v_{0i}^TR) \\ E_{mes} &= \sum_{i=1}^n k_iE_i = \sum_{i=1}^n k_i-\mathrm{tr}(\tilde{R}M), \quad M=R^TM_0R, \quad M_0=\sum_{i=1}^n k_iv_{0i}v_{0i}^T \end{aligned}EiEmes=1−⟨v^i,vi⟩=1−tr(R^Tv0iv0iTR)=i=1∑nkiEi=i=1∑nki−tr(R~M),M=RTM0R,M0=i=1∑nkiv0iv0iT
其中 v^i=R^Tvi\hat{v}_i=\hat{R}^Tv_iv^i=R^Tvi, ki>0k_i>0ki>0 为权重。当 n≥2n\geq 2n≥2时,矩阵 M0M_0M0 正定。利用它构造如下滤波器:
R^˙=R^(Ωy−b^+kPω×)b^˙=−kIωω=∑i=1nkivi×v^i\begin{aligned} \dot{\hat{R}} &= \hat{R}(\Omega_y-\hat{b}+k_P\omega_\times) \\ \dot{\hat{b}} &= -k_I\omega \\ \omega &= \sum_{i=1}^n k_i v_i\times \hat{v}_i \end{aligned}R^˙b^˙ω=R^(Ωy−b^+kPω×)=−kIω=i=1∑nkivi×v^i
可以证明它有如下性质:
-
存在与特征值对应的三个不稳定平衡点:(R^i∗,b^i∗)=(U0DiU0TR,b)(\hat{R}_i^*,\hat{b}_i^*)=(U_0D_iU_0^TR,b)(R^i∗,b^i∗)=(U0DiU0TR,b), 其中 Di=diag(1,−1,−1),(1,1,−1)D_i=diag(1,-1,-1),(1,1,-1)Di=diag(1,−1,−1),(1,1,−1) 或 (1,−1,1)(1,-1,1)(1,−1,1)。
-
误差在 (I,0)(I,0)(I,0) 处局部指数稳定。
-
除了前述三个平衡点外,其他所有初值都收敛到真实值。
当 n=2n=2n=2时,有类似的结论。特别地,当只有一个方向矢量(如重力加速度)时,有如下结果:
-
不稳定平衡集合为 U1={R~:v0TR~v0=−1,b~=0}U_1=\{\tilde{R}: v_0^T\tilde{R}v_0=-1, \tilde{b}=0 \}U1={R~:v0TR~v0=−1,b~=0}。
-
除 U1U_1U1外,估计值收敛到真实值。
这说明利用单个参考方向也可以实现除旋转轴外两个方向的渐近估计。
这里我再次详细介绍和讲解下并给出示例代码
显式互补滤波器的推导与实现
符号说明
- RRR: 真实姿态矩阵
- R^\hat{R}R^: 估计姿态矩阵
- R~\tilde{R}R~: 姿态误差矩阵,R~=R^TR\tilde{R}=\hat{R}^TRR~=R^TR
- Ωy\Omega_yΩy: 陀螺仪测量角速度
- bbb: 陀螺仪偏置
- b^\hat{b}b^: 偏置估计
- viv_ivi: 惯性方向在体坐标系下的观测值
- v0iv_{0i}v0i: 惯性方向在惯性坐标系下的已知值
- v^i\hat{v}_iv^i: 惯性方向在估计姿态下的计算值,v^i=R^Tv0i\hat{v}_i=\hat{R}^Tv_{0i}v^i=R^Tv0i
- μi\mu_iμi: 观测噪声
- kik_iki: 第i个观测值的权重
- kPk_PkP: 比例增益
- kIk_IkI: 积分增益
- ω\omegaω: 修正项
滤波器方程推导
显式互补滤波器的核心思想是直接利用惯性方向的观测值,而不是重构姿态。首先,我们定义了如下的姿态误差代价函数:
Ei=1−⟨v^i,vi⟩=1−tr(R^Tv0iv0iTR)E_i = 1-\langle \hat{v}_i,v_i \rangle = 1-\mathrm{tr}(\hat{R}^Tv_{0i}v_{0i}^TR)Ei=1−⟨v^i,vi⟩=1−tr(R^Tv0iv0iTR)
其中 v^i=R^Tv0i\hat{v}_i=\hat{R}^Tv_{0i}v^i=R^Tv0i 是第i个惯性方向在估计姿态下的计算值。这个代价函数表示了估计值 v^i\hat{v}_iv^i 与观测值 viv_ivi 之间的误差。
进一步,我们定义了总的代价函数:
Emes=∑i=1nkiEi=∑i=1nki−tr(R~M),M=RTM0R,M0=∑i=1nkiv0iv0iTE_{mes} = \sum_{i=1}^n k_iE_i = \sum_{i=1}^n k_i-\mathrm{tr}(\tilde{R}M), \quad M=R^TM_0R, \quad M_0=\sum_{i=1}^n k_iv_{0i}v_{0i}^TEmes=i=1∑nkiEi=i=1∑nki−tr(R~M),M=RTM0R,M0=i=1∑nkiv0iv0iT
其中 ki>0k_i>0ki>0 是第i个观测值的权重。可以证明,当观测值个数 n≥2n\geq 2n≥2 时,矩阵 M0M_0M0 是正定的。
基于这个代价函数,我们提出了如下的显式互补滤波器:
R^˙=R^(Ωy−b^+kPω×)b^˙=−kIωω=∑i=1nkivi×v^i\begin{aligned} \dot{\hat{R}} &= \hat{R}(\Omega_y-\hat{b}+k_P\omega_\times) \\ \dot{\hat{b}} &= -k_I\omega \\ \omega &= \sum_{i=1}^n k_i v_i\times \hat{v}_i \end{aligned}R^˙b^˙ω=R^(Ωy−b^+kPω×)=−kIω=i=1∑nkivi×v^i
其中 b^\hat{b}b^ 是陀螺仪偏置的估计值, kPk_PkP 和 kIk_IkI 分别是比例和积分增益。修正项 ω\omegaω 是所有观测值与估计值之间误差的加权和。
对总代价函数 EmesE_{mes}Emes 求导,可得:
E˙mes=−tr(R~˙M+R~M˙)=−tr((R~Ω−Ωy+b^−kPω)×R~M)=⟨ω−b^,vex(Pa(R~M))⟩−kP∥ω∥2\begin{aligned} \dot{E}_{mes} &= -\mathrm{tr}(\dot{\tilde{R}}M + \tilde{R}\dot{M}) \\ &= -\mathrm{tr}((\tilde{R}\Omega-\Omega_y+\hat{b}-k_P\omega)_\times\tilde{R}M) \\ &= \langle\omega-\hat{b},\mathrm{vex}(P_a(\tilde{R}M))\rangle - k_P\|\omega\|^2 \end{aligned}E˙mes=−tr(R~˙M+R~M˙)=−tr((R~Ω−Ωy+b^−kPω)×R~M)=⟨ω−b^,vex(Pa(R~M))⟩−kP∥ω∥2
其中我们用到了 M˙=0\dot{M}=0M˙=0 和 tr(A×B)=2⟨vex(Pa(A)),vex(Pa(B))⟩\mathrm{tr}(A_\times B)=2\langle\mathrm{vex}(P_a(A)),\mathrm{vex}(P_a(B))\rangletr(A×B)=2⟨vex(Pa(A)),vex(Pa(B))⟩。
进一步可以证明,修正项 ω\omegaω 满足:
ω×=Pa(R~M)\omega_\times = P_a(\tilde{R}M)ω×=Pa(R~M)
将其代入 E˙mes\dot{E}_{mes}E˙mes 的表达式,可得:
E˙mes=−⟨b^,ω⟩−kP∥ω∥2\dot{E}_{mes} = -\langle\hat{b},\omega\rangle - k_P\|\omega\|^2E˙mes=−⟨b^,ω⟩−kP∥ω∥2
这说明,在显式互补滤波器作用下,总代价函数 EmesE_{mes}Emes 是单调递减的,而且递减的速度与修正项 ω\omegaω 的大小成正比。因此,我们可以期望滤波器能够使姿态估计值 R^\hat{R}R^ 收敛到真实值 RRR。
进一步的稳定性分析表明,显式互补滤波器有如下性质:
- 存在与矩阵 M0M_0M0 的特征值对应的三个不稳定平衡点。
- 真实值 (R,b)(R,b)(R,b) 处是局部指数稳定的。
- 除了那三个不稳定平衡点外,其他所有初值都能收敛到真实值。
特别地,当只有一个参考方向(如重力加速度)时,滤波器仍然能保证除参考方向外的两个方向上的收敛性。
Python实现
下面给出显式互补滤波器的Python实现:
import numpy as np
from scipy.spatial.transform import Rotation
def explicit_complementary_filter(gyro, dt, kp, ki, *vs):
"""
显式互补滤波器
:param gyro: 陀螺仪读数,3x1
:param dt: 采样时间
:param kp: 比例增益
:param ki: 积分增益
:param vs: 惯性方向观测值,每个为3x1
:return: 估计姿态矩阵和偏置
"""
# 初始化估计值
R_hat = Rotation.from_matrix(np.eye(3))
b_hat = np.zeros(3)
# 计算参考方向和权重
v0s = [v / np.linalg.norm(v) for v in vs]
n = len(v0s)
ks = np.ones(n) / n
# 滤波器更新
vhs = [R_hat.as_matrix().T @ v0 for v0 in v0s]
omega = np.sum([ki * np.cross(vi, vhi) for ki, vi, vhi in zip(ks, vs, vhs)], axis=0)
b_hat = b_hat - ki * omega * dt
R_omega = Rotation.from_rotvec((gyro - b_hat + kp * omega) * dt)
R_hat = R_hat * R_omega
return R_hat.as_matrix(), b_hat
def Pa(R):
"""
反对称矩阵投影算子
:param R: 旋转矩阵
:return: R的反对称部分
"""
return 0.5 * (R - R.T)
def vex(R):
"""
反对称矩阵到向量的转换
:param R: 3x3反对称矩阵
:return: 3x1向量
"""
return np.array([R[2, 1], R[0, 2], R[1, 0]])
这个实现中,我们首先归一化所有的参考方向,并设置相应的权重。然后根据当前的姿态估计计算每个参考方向的估计值,并计算它们与观测值之间的误差。修正项 omega 就是所有这些误差的加权和。最后,我们用修正后的角速度更新姿态估计,并用 omega 更新偏置估计。
显式互补滤波器的优点是不需要重构姿态,而是直接利用惯性方向的观测值。这不仅简化了计算,而且提高了滤波器的鲁棒性。同时,显式互补滤波器也继承了被动互补滤波器的优点,即避免了将重构姿态的噪声引入预测项。
总的来说,显式互补滤波器是一种简洁、高效、鲁棒的姿态估计算法,非常适合在嵌入式系统中实现。它的理论分析和实验验证都表明,它能够在各种услов件下实现良好的姿态估计性能。
6. 仿真验证
论文最后通过两个实验验证了算法的有效性。第一个实验在机械臂上安装IMU,模拟飞行器的机动。分别使用直接和被动互补滤波器。从实验结果看,两者的渐近性能接近,但被动滤波器的瞬态响应和估计噪声略优于直接滤波器。
论文的第二个实验在一个四旋翼无人机Hoverye上进行,使用显式互补滤波器。通过与纯积分得到的偏航角对比,滤波器可以较好地估计出偏置,最终偏航误差小于5度。
总结
这篇论文提出了一类直接在SO(3)流形上进行姿态估计的非线性互补滤波器。通过巧妙的误差函数设计和Lyapunov分析,证明了估计误差的局部指数稳定性和全局渐近稳定性。同时,通过集成偏置估计,可以在线校正陀螺仪偏置。相比欧拉角和四元数等表示,基于SO(3)的方法在公式推导和稳定性分析上都更加简洁自然。
论文还提出了几种不同的滤波器实现形式。直接和被动互补滤波器在渐近意义下等价,但被动实现的噪声抑制性能更优。进一步利用方向传感器的直接输出,得到了显式互补滤波器,它避免了在线重构姿态,计算负荷小,非常适合在嵌入式系统上实现。同时,即使只有一个参考方向(如重力加速度),该滤波器也能实现局部渐近估计。丰富的数值仿真和实物实验验证了算法的有效性和优越性。
互补滤波的基本思想
互补滤波的基本思想是将两个互补的传感器信号融合,常用于姿态估计问题。例如,陀螺仪的积分误差为低频,而加速度计和磁力计的噪声主要在高频。互补滤波就是设计一个低通和一个高通滤波器,将它们融合。
设计互补滤波器的关键是如何在SO(3)流形上定义姿态误差。本文利用旋转矩阵的迹(trace)构造二次型误差函数,既简单又能反映旋转误差的大小。
Q&A
Lyapunov函数与稳定性
Lyapunov稳定性理论是控制中分析非线性系统稳定性的有力工具。其基本思想是,构造一个标量函数 V(x)V(x)V(x) 作为系统"能量"的度量。如果 V(x)V(x)V(x) 在平衡点处取得最小值,且随时间单调递减,则平衡点是渐近稳定的。常见的Lyapunov函数有二次型、线性矩阵不等式等。
本文中的Lyapunov函数由两项组成: V=Etr+12kIb~2V=E_{tr}+\frac{1}{2k_I}\tilde{b}^2V=Etr+2kI1b~2。第一项表征姿态误差,第二项表征偏置估计误差。如果 V˙≤0\dot{V}\leq 0V˙≤0,则姿态误差和偏置误差都有界。进一步利用LaSalle不变集原理和Barbalat引理,可以分析收敛到哪些平衡点。
编程实现
下面我给出一个完整的Mahony滤波器的Python实现示例,包括数据生成、滤波器实现和结果可视化。你可以直接运行这个脚本来观察滤波器的效果。
import numpy as np
from scipy.spatial.transform import Rotation
import matplotlib.pyplot as plt
# 生成模拟数据
def generate_data(t, dt):
"""
生成模拟数据
:param t: 时间序列
:param dt: 采样时间间隔
:return: 角速度、加速度计和磁力计读数
"""
# 真实姿态
w = 2 * np.pi * 0.1 # 角速度幅值
R_true = Rotation.from_euler('ZYX', [w * t, 0.1 * np.sin(0.7 * t), 0.2 * np.sin(0.3 * t)])
# 角速度
gyro = R_true.as_rotvec() / dt + 0.1 * np.random.randn(len(t), 3)
gyro_bias = 0.01 * np.random.randn(3)
gyro += gyro_bias
# 加速度计
a0 = np.array([0, 0, 1.0]) # 参考重力方向
a_true = R_true.apply(a0)
accel = a_true + 0.05 * np.random.randn(len(t), 3)
# 磁力计
m0 = np.array([1.0, 0, 0]) # 参考磁场方向
m_true = R_true.apply(m0)
mag = m_true + 0.05 * np.random.randn(len(t), 3)
return gyro, accel, mag
# Mahony滤波器
def mahony_filter(gyro, accel, mag, dt, kp, ki):
"""
Mahony滤波器
:param gyro: 陀螺仪读数
:param accel: 加速度计读数
:param mag: 磁力计读数
:param dt: 采样时间间隔
:param kp: 比例增益
:param ki: 积分增益
:return: 估计姿态和偏置
"""
# 初始化估计值
R_hat = Rotation.from_matrix(np.eye(3))
b_hat = np.zeros(3)
# 参考方向
a0 = np.array([0, 0, 1.0])
m0 = np.array([1.0, 0, 0])
# 存储估计值
R_hist = [R_hat]
b_hist = [b_hat]
for i in range(len(gyro)):
# 归一化测量值
a = accel[i] / np.linalg.norm(accel[i])
m = mag[i] / np.linalg.norm(mag[i])
# 计算误差项
R = R_hat.as_matrix()
va = R.T @ a
vm = R.T @ m
e = np.cross(a0, va) + np.cross(m0, vm)
# 更新估计值
b_hat = b_hat - ki * e * dt
R_omega = Rotation.from_rotvec((gyro[i] - b_hat + kp * e) * dt)
R_hat = R_hat * R_omega
# 存储估计值
R_hist.append(R_hat)
b_hist.append(b_hat)
return R_hist, b_hist
# 主函数
if __name__ == "__main__":
# 参数设置
t = np.arange(0, 10, 0.01)
dt = t[1] - t[0]
kp = 5.0
ki = 0.1
# 生成数据
gyro, accel, mag = generate_data(t, dt)
# 运行滤波器
R_hist, b_hist = mahony_filter(gyro, accel, mag, dt, kp, ki)
# 绘制结果
fig, axs = plt.subplots(3, 1, figsize=(8, 8))
axs[0].plot(t, [R.as_euler('ZYX', degrees=True)[0] for R in R_hist], label='Estimated')
axs[0].set_ylabel('Z (deg)')
axs[0].legend()
axs[1].plot(t, [R.as_euler('ZYX', degrees=True)[1] for R in R_hist], label='Estimated')
axs[1].set_ylabel('Y (deg)')
axs[1].legend()
axs[2].plot(t, [R.as_euler('ZYX', degrees=True)[2] for R in R_hist], label='Estimated')
axs[2].set_xlabel('Time (s)')
axs[2].set_ylabel('X (deg)')
axs[2].legend()
fig.suptitle('Attitude Estimation by Mahony Filter')
fig.tight_layout()
plt.show()
这个示例首先生成了一组模拟数据,包括真实姿态、角速度(带偏置)、加速度计和磁力计读数。然后,它实现了Mahony滤波器,并用生成的数据对其进行测试。最后,它绘制了滤波器估计的姿态(欧拉角)随时间的变化曲线。
在generate_data函数中,我们模拟了一个周期性的姿态运动,并加入了一定的测量噪声和偏置。在mahony_filter函数中,我们按照Mahony滤波器的算法,依次更新姿态和偏置的估计值。注意,我们使用了scipy.spatial.transform.Rotation类来表示旋转,这大大简化了姿态的计算和转换。
你可以运行这个脚本,观察Mahony滤波器对姿态的估计效果。你也可以尝试改变模拟数据的特性(如噪声大小、偏置等)或滤波器的参数(如增益),看看会有什么影响。
在实际应用中,你可以将这个滤波器应用于从IMU获取的真实数据,以获得实时、鲁棒的姿态估计。
矩阵的特征值
在上面的推导中用到了矩阵的特征值说明微分方程的稳定性,这里就一并介绍下在机器人学中经常用的几个方面。
-
矩阵对角化
如果一个 n×nn\times nn×n 矩阵 AAA 有 nnn 个线性无关的特征向量,那么 AAA 可以对角化,即存在可逆矩阵 PPP 使得 P−1AP=ΛP^{-1}AP=\LambdaP−1AP=Λ,其中 Λ\LambdaΛ 是对角矩阵,其对角元素为 AAA 的特征值。这种对角化可以简化矩阵的运算,如矩阵的幂等。 -
微分方程的稳定性
在线性微分方程组 x˙=Ax\dot{x}=Axx˙=Ax 中,系数矩阵 AAA 的特征值决定了系统的稳定性:如果所有特征值的实部都是负的,那么系统是渐近稳定的;如果有特征值的实部为正,那么系统是不稳定的。这个原理被广泛应用于控制理论和动力系统分析。
-
最优化问题
在一些最优化问题中,目标函数的Hessian矩阵的特征值提供了关于函数曲率的信息。特别地,如果Hessian矩阵在最优点处是正定的(即所有特征值都是正的),那么这个最优点是局部最小点。这个原理被用于牛顿法、拟牛顿法等二阶优化算法中。
个人陈述
SO(3)上的互补滤波为姿态估计提供了一种简洁高效的新思路。通过巧妙的误差函数设计和Lyapunov稳定性分析,可以直接对旋转矩阵进行滤波,并证明其局部指数稳定性和全局渐近稳定性。进一步耦合偏置估计,可以在线校正陀螺仪漂移。采用被动互补结构和显式误差反馈,有助于提高滤波器性能并简化其工程实现。这些思想对其他非线性估计问题也有一定启发意义。
如果对SO3上的运动学不了解的可以看我的hands-on系列教程:Unleashing Robotics: Mastering Quaternion Kinematics with Python - (原创系列教程),看完之后你就可以轻松理解了。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)