概述
在实际项目中,常常需要对数据进行滤波,这里分享卡尔曼滤波算法应用。
正文
卡尔曼滤波(KF)属于线性滤波器,使用均值和方差描述系统状态,将多个符合高斯分布的不确定信息源进行数据融合的一种最优状态估计算法。
使用前提:
-
线性:系统状态方程为线性(比例性、齐次性)的系统,非线性则需要使用扩展卡尔曼滤波(EKF)。
-
KF和EKF最大的区别在于:EKF的FkF_kFk和HkH_kHk都是使用雅各比矩阵表示
-
高斯分布:系统噪声服从高斯分布
-
传感器噪声
-
ADC采样噪声
-
预测方程:
X^k=FkX^k−1+BkU⃗k\hat{X}_k=F_k\hat{X}_{k-1}+B_k\vec{U}_kX^k=FkX^k−1+BkUk
Pk=FkPk−1FkT+QkP_k=F_kP_{k-1}F^T_k+Q_kPk=FkPk−1FkT+Qk
更新方程:
K′=PkHkTHkPkHkT+RkK'=\frac {P_kH^T_k}{H_kP_kH^T_k+R_k}K′=HkPkHkT+RkPkHkT
Xk′^=X^k+K′(Z⃗k−HkX^k)\hat{X_k'}=\hat{X}_k+K'(\vec{Z}_k-H_k\hat{X}_k)Xk′^=X^k+K′(Zk−HkX^k)
Pk′=Pk−K′HkPk{P_k}'=P_k-K'H_kP_kPk′=Pk−K′HkPk
-
X^k\hat{X}_kX^k为kkk时刻系统最优的状态向量
-
FkF_kFk为状态转移矩阵,表示用k−1k-1k−1时刻的状态向量估算kkk时刻的状态向量
-
BkB_kBk为输入控制矩阵,代表着控制向量U⃗k\vec{U}_kUk和状态向量X^k\hat{X}_kX^k的关系
-
U⃗k\vec{U}_kUk代表着控制向量,如加速度、角加速度等
-
PkP_kPk为状态向量的协方差矩阵,代表着状态向量每个元素之间的关系
-
QkQ_kQk为预测状态的高斯噪声协方差矩阵,它用来衡量模型的准确度,模型越准确其值越小
-
Z⃗k\vec{Z}_kZk为kkk时刻测量值的状态向量
-
HkH_kHk为转换矩阵,它将状态向量X^k\hat{X}_kX^k映射到测量值所在的向量空间Z⃗k\vec{Z}_kZk
-
RkR_kRk为测量值的高斯噪声协方差矩阵,代表着传感器的测量误差
一阶卡尔曼滤波
(1)预测状态方程:
在一阶系统下,FkF_kFk的值为1,无外部控制向量U⃗k\vec{U}_kUk的作用。所以,代入公式X^k=FkX^k−1+BkU⃗k\hat{X}_k=F_k\hat{X}_{k-1}+B_k\vec{U}_kX^k=FkX^k−1+BkUk得出:
X^k=X^k−1\hat{X}_k=\hat{X}_{k-1}X^k=X^k−1
(2)预测状态协方差方程:
在一阶系统下,FkF_kFk的值为1,所以转置矩阵FkTF^T_kFkT的值也为1。所以,代入公式Pk=FkPk−1FkT+QkP_k=F_kP_{k-1}F^T_k+Q_kPk=FkPk−1FkT+Qk得出:
Pk=Pk−1+QkP_k=P_{k-1}+Q_kPk=Pk−1+Qk
(3)卡尔曼增益方程:
在一阶系统下,HkH_kHk的值为1。所以,代入公式K′=PkHkTHkPkHkT+RkK'=\frac {P_kH^T_k}{H_kP_kH^T_k+R_k}K′=HkPkHkT+RkPkHkT得出:
K′=PkPk+RkK'=\frac {P_k}{P_k+R_k}K′=Pk+RkPk
(4)更新最优状态方程:
在一阶系统下,HkH_kHk的值为1。所以,代入公式Xk′^=X^k+K′(Z⃗k−HkX^k)\hat{X_k'}=\hat{X}_k+K'(\vec{Z}_k-H_k\hat{X}_k)Xk′^=X^k+K′(Zk−HkX^k)得出:
Xk′^=X^k+K′(Z⃗k−X^k)\hat{X_k'}=\hat{X}_k+K'(\vec{Z}_k-\hat{X}_k)Xk′^=X^k+K′(Zk−X^k)
(5)更新状态协方差方程:
在一阶系统下,HkH_kHk的值为1。所以,代入公式Pk′=Pk−K′HkPk{P_k}'=P_k-K'H_kP_kPk′=Pk−K′HkPk得出:
Pk′=Pk−K′Pk{P_k}'=P_k-K'P_kPk′=Pk−K′Pk
C代码实现
typedef struct{
float last_P;
float Q;
float R;
float out;
float now_P;
float Kg;
}kalman_t;
kalman_t my_kalman;
float kalman_filter_init(void)
{
my_kalman.last_P = 1.0;
my_kalman.Q = 0;
my_kalman.R = 0.01;
my_kalman.out = 0;
my_kalman.now_P = 0;
my_kalman.Kg = 0;
}
float kalman_filter_calc(kalman_t *kfp, float input)
{
kfp->now_P = kfp->last_P + kfp->Q;
kfp->Kg = kfp->now_P / (kfp->now_P + kfp->R);
kfp->out += kfp->Kg * (input - kfp->out);
kfp->last_P = (1 - kfp->Kg) * kfp->now_P;
return kfp->out;
}
实验效果

二阶卡尔曼滤波
以经典卡尔曼滤波模型:加速度计、陀螺仪进行角度数据融合为例。
(1)预测状态方程:
首先,根据系统建立系统方程:
anglek=anglek−1−Q_biask−1∗Δt+Gyrok∗Δt+12∗(Gyrok−Gyrok−1)∗Δt2angle_k = angle_{k-1} - Q\_bias_{k-1}*\Delta{t}+Gyro_k*\Delta{t}+\frac{1}{2}*(Gyro_k-Gyro_{k-1})*{\Delta _{t}}^{2}anglek=anglek−1−Q_biask−1∗Δt+Gyrok∗Δt+21∗(Gyrok−Gyrok−1)∗Δt2
Q_biask=Q_biask−1Q\_bias_{k} = Q\_bias_{k-1}Q_biask=Q_biask−1
用矩阵的方式表示为:
∣anglekQ_biask∣=∣1−Δt01∣∣anglek−1Q_biask−1∣+∣Δt0∣Gyrok+∣12∗Δt20∣(Gyrok−Gyrok−1)\begin{vmatrix}angle_k\\Q\_bias_k\end{vmatrix}=\begin{vmatrix}1&-\Delta{t}\\0&1\end{vmatrix}\begin{vmatrix}angle_{k-1}\\Q\_bias_{k-1}\end{vmatrix}+\begin{vmatrix}\Delta{t}\\0\end{vmatrix}Gyro_k+\begin{vmatrix}\frac{1}{2}*{\Delta _{t}}^{2}\\0\end{vmatrix}(Gyro_k-Gyro_{k-1})∣∣anglekQ_biask∣∣=∣∣10−Δt1∣∣∣∣anglek−1Q_biask−1∣∣+∣∣Δt0∣∣Gyrok+∣∣21∗Δt20∣∣(Gyrok−Gyrok−1)
-
angleangleangle为角度,如pitch、roll、yaw
-
Q_biasQ\_biasQ_bias为陀螺仪零漂,固定值
-
由于Δt2{\Delta_{t}}^{2}Δt2非常小,所以忽略不计,所以BkB_kBk为0
-
Fk=∣1−Δt01∣F_k=\begin{vmatrix}1&-\Delta{t}\\0&1\end{vmatrix}Fk=∣∣10−Δt1∣∣
所以,预测状态方程的C代码如下:
angle += (gyro - Q_bias) * dt;//预测角度等于上次最优角度加上
//角速度减去零漂后的积分角度
(2)预测状态协方差方程:
由于高斯分布中的每个数据点的协方差可以用Cov(x)=ΣCov(x)=\SigmaCov(x)=Σ表示,所以高斯噪声协方差矩阵Qk=∣Cov(angle,angle)Cov(Q_bias,angle)Cov(angle,Q_bias)Cov(Q_bias,Q_bias)∣Q_k=\begin{vmatrix}Cov(angle,angle)&Cov(Q\_bias,angle)\\Cov(angle,Q\_bias)&Cov(Q\_bias,Q\_bias)\end{vmatrix}Qk=∣∣Cov(angle,angle)Cov(angle,Q_bias)Cov(Q_bias,angle)Cov(Q_bias,Q_bias)∣∣
因为加速度计和陀螺仪噪声相互独立,所以:
Qk=∣Cov(angle,angle)00Cov(Qbias,Qbias)∣Q_k=\begin{vmatrix}Cov(angle,angle)&0\\0&Cov(Q_bias,Q_bias)\end{vmatrix}Qk=∣∣Cov(angle,angle)00Cov(Qbias,Qbias)∣∣
假设预测状态协方差矩阵Pk=∣akbkckdk∣P_k=\begin{vmatrix}a_k&b_k\\c_k&d_k\end{vmatrix}Pk=∣∣akckbkdk∣∣
代入预测状态协方差方程得出:
Pk=∣ak−1−(ck−1+bk−1)∗Δt+Δt2bk−1−dk−1∗Δtck−1−dk−1∗Δtdk−1∣+∣Cov(angle,angle)00Cov(Q_bias,Q_bias)∣P_k=\begin{vmatrix}a_{k-1}-(c_{k-1}+b_{k-1})*\Delta{t}+{\Delta{t}}^2&b_{k-1}-d_{k-1}*\Delta{t}\\c_{k-1}-d_{k-1}*\Delta{t}&d_{k-1}\end{vmatrix}+\begin{vmatrix}Cov(angle,angle)&0\\0&Cov(Q\_bias,Q\_bias)\end{vmatrix}Pk=∣∣ak−1−(ck−1+bk−1)∗Δt+Δt2ck−1−dk−1∗Δtbk−1−dk−1∗Δtdk−1∣∣+∣∣Cov(angle,angle)00Cov(Q_bias,Q_bias)∣∣
- 由于Δt2{\Delta_{t}}^{2}Δt2非常小,所以忽略不计
所以,预测状态协方差方程的C代码如下:
PP[0][0] = PP[0][0] + Q_angle - (PP[0][1] - PP[1][0])*dt;
PP[0][1] = PP[0][1] - PP[1][1]*dt;
PP[1][0] = PP[1][0] - PP[1][1]*dt;
PP[1][1] = PP[1][0] + Q_gyro;
(3)卡尔曼增益方程:
首先建立测量方程Zk=HkXk+VkZ_k=H_kX_k+V_kZk=HkXk+Vk
-
ZkZ_kZk为kkk时刻实际观测量
-
XkX_kXk为kkk时刻状态量
-
VkV_kVk为kkk时刻的噪声
由于传感器数据自带噪声,所以Vk=0V_k=0Vk=0
所以测量方程可以用矩阵表示为:
Zk=∣10∣∣Xk0∣Z_k=\begin{vmatrix}1&0\end{vmatrix}\begin{vmatrix}X_k\\0\end{vmatrix}Zk=∣∣10∣∣∣∣Xk0∣∣
所以转换矩阵Hk=∣10∣H_k=\begin{vmatrix}1&0\end{vmatrix}Hk=∣∣10∣∣
代入卡尔曼增益方程得出:
K′=∣abcd∣∣10∣∣10∣∣abcd∣∣10∣+R_angle=a+ca+R_angleK'=\frac{\begin{vmatrix}a&b\\c&d\end{vmatrix}\begin{vmatrix}1\\0\end{vmatrix}}{\begin{vmatrix}1&0\end{vmatrix}\begin{vmatrix}a&b\\c&d\end{vmatrix}\begin{vmatrix}1\\0\end{vmatrix}+R\_angle}=\frac{a+c}{a+R\_angle}K′=∣10∣∣∣acbd∣∣∣∣10∣∣+R_angle∣∣acbd∣∣∣∣10∣∣=a+R_anglea+c
所以卡尔曼增益方程的C代码如下:
K_0 = PP[0][0] / (PP[0][0] + R_angle); //angle增益
K_1 = PP[1][0] / (PP[0][0] + R_angle); //Q_bias增益
(4)更新最优状态方程:
代入更新最优状态方程得出:
∣angleQ_bias∣=∣angleQ_bias∣+∣K0K1∣(anglez−∣10∣∣angleQ_bias∣)\begin{vmatrix}angle\\Q\_bias\end{vmatrix}=\begin{vmatrix}angle\\Q\_bias\end{vmatrix}+\begin{vmatrix}K_0\\K_1\end{vmatrix}(angle_z-\begin{vmatrix}1&0\end{vmatrix}\begin{vmatrix}angle\\Q\_bias\end{vmatrix})∣∣angleQ_bias∣∣=∣∣angleQ_bias∣∣+∣∣K0K1∣∣(anglez−∣∣10∣∣∣∣angleQ_bias∣∣)
所以更新最优状态方程的C代码如下:
Q_bias += K_1 * (angle_now - angle);
angle += K_0 * (angle_now - angle);
(5)更新状态协方差方程
代入更新状态协方差方程得出:
∣ak′bk′ck′dk′∣=(∣1001∣−∣K0K1∣∣10∣)∗∣akbkckdk∣\begin{vmatrix}{a_k}'&{b_k}'\\{c_k}'&{d_k}'\end{vmatrix}=(\begin{vmatrix}1&0\\0&1\end{vmatrix}-\begin{vmatrix}K_0\\K_1\end{vmatrix}\begin{vmatrix}1&0\end{vmatrix})*\begin{vmatrix}a_k&b_k\\c_k&d_k\end{vmatrix}∣∣ak′ck′bk′dk′∣∣=(∣∣1001∣∣−∣∣K0K1∣∣∣∣10∣∣)∗∣∣akckbkdk∣∣
所以更新状态协方差的C代码如下:
PP[0][0] = PP[0][0] - K_0 * PP[0][0];
PP[0][1] = PP[0][1] - K_0 * PP[0][1];
PP[1][0] = PP[1][0] - K_1 * PP[0][0];
PP[1][1] = PP[1][1] - K_1 * PP[0][1];
整理完整代码
static float Q_angle = 0.001; //角度噪声的协方差
static float Q_gyro = 0.003; //角速度噪声的协方差
static float R_angle = 0.5; //加速度计测量噪声的协方差
static float dt = 0.01; //采样周期即计算任务周期10ms
float kalman_acc_gyro(float angle_now, float gyro)
{
static float Q_bias;
static float K_0,K_1;
static float PP[2][2] = {{1, 0}, {0, 1}};
static float angle;
angle += (gyro - Q_bias) * dt;
PP[0][0] += Q_angle - (PP[0][1] - PP[1][0])*dt;
PP[0][1] -= PP[1][1]*dt;
PP[1][0] -= PP[1][1]*dt;
PP[1][1] += Q_gyro;
K_0 = PP[0][0] / (PP[0][0] + R_angle);
K_1 = PP[1][0] / (PP[0][0] + R_angle);
Q_bias += K_1 * (angle_now - angle);
angle += K_0 * (angle_now - angle);
PP[0][0] -= K_0 * PP[0][0];
PP[0][1] -= K_0 * PP[0][1];
PP[1][0] -= K_1 * PP[0][0];
PP[1][1] -= K_1 * PP[0][1];
return angle;
}
void test(float acc_x, float acc_y, float acc_z,
float gyro_x, float gyro_y, float gyro_z)
{
float roll = (atan(acc_x/acc_z))*180/3.14159;
kalman_acc_gyro(roll, gyro_y);
}
实验效果

总结
这样就完成了卡尔曼滤波的简单分析和基础应用,实际项目中不同系统的传递函数不同,所以需要进行调整设计,但是5大核心公式都不会变。扩展卡尔曼其实也是利用线性去模拟非线性,具体的应用大家有兴趣可以自行查文献实验,这里不做分析介绍。水平有限,这里只做自己的理解分析,有错误的地方,请大佬们不吝赐教!
本文详细介绍了卡尔曼滤波的基本原理与实现步骤,并通过一阶与二阶滤波实例,展示了如何在实际项目中运用卡尔曼滤波进行数据融合。
2892

被折叠的 条评论
为什么被折叠?



