卡尔曼滤波器(Kalman Filter)¶
📋 学习目标¶
完成本章学习后,您将能够:
- ✅ 理解卡尔曼滤波器的基本原理
- ✅ 实现标准卡尔曼滤波器(KF)
- ✅ 实现扩展卡尔曼滤波器(EKF)
- ✅ 应用卡尔曼滤波器进行传感器融合
- ✅ 在医疗信号处理中应用卡尔曼滤波
前置知识¶
- 线性代数(矩阵运算)
- 概率论与统计
- 状态空间模型
- C/C++编程
1. 卡尔曼滤波器基础¶
1.1 什么是卡尔曼滤波器?¶
卡尔曼滤波器是一种最优递归数据处理算法,用于从一系列含有噪声的测量中估计动态系统的状态。
核心思想: - 使用系统的动态模型进行预测 - 使用测量值进行校正 - 在预测和测量之间找到最优平衡
优势: - ✅ 最优估计(最小均方误差) - ✅ 递归计算(不需要存储历史数据) - ✅ 实时性好 - ✅ 能融合多个传感器
1.2 系统模型¶
卡尔曼滤波器基于两个模型:
状态方程(预测模型):
观测方程(测量模型):
其中:
- x(k):k时刻的状态向量
- F:状态转移矩阵
- B:控制输入矩阵
- u(k):控制输入
- w(k):过程噪声,协方差为Q
- z(k):测量向量
- H:观测矩阵
- v(k):测量噪声,协方差为R
1.3 卡尔曼滤波器算法¶
预测步骤:
更新步骤:
3. 卡尔曼增益: K(k) = P(k|k-1)·H^T·[H·P(k|k-1)·H^T + R]^(-1)
4. 状态更新: x̂(k|k) = x̂(k|k-1) + K(k)·[z(k) - H·x̂(k|k-1)]
5. 协方差更新: P(k|k) = [I - K(k)·H]·P(k|k-1)
符号说明:
- x̂(k|k-1):基于k-1时刻的k时刻状态预测
- x̂(k|k):基于k时刻测量的k时刻状态估计
- P(k|k):估计误差协方差矩阵
- K(k):卡尔曼增益
2. 标准卡尔曼滤波器实现¶
2.1 数据结构定义¶
#include <stdint.h>
#include <string.h>
#include <math.h>
#define MAX_STATE_DIM 10 // 最大状态维度
#define MAX_MEAS_DIM 5 // 最大测量维度
typedef struct {
// 状态向量和协方差
float x[MAX_STATE_DIM]; // 状态估计
float P[MAX_STATE_DIM][MAX_STATE_DIM]; // 误差协方差
// 系统矩阵
float F[MAX_STATE_DIM][MAX_STATE_DIM]; // 状态转移矩阵
float H[MAX_MEAS_DIM][MAX_STATE_DIM]; // 观测矩阵
float Q[MAX_STATE_DIM][MAX_STATE_DIM]; // 过程噪声协方差
float R[MAX_MEAS_DIM][MAX_MEAS_DIM]; // 测量噪声协方差
float B[MAX_STATE_DIM][MAX_STATE_DIM]; // 控制输入矩阵(可选)
// 维度
uint8_t state_dim; // 状态维度
uint8_t meas_dim; // 测量维度
// 临时变量(避免重复分配)
float K[MAX_STATE_DIM][MAX_MEAS_DIM]; // 卡尔曼增益
float temp_x[MAX_STATE_DIM];
float temp_P[MAX_STATE_DIM][MAX_STATE_DIM];
} KalmanFilter;
/**
* @brief 初始化卡尔曼滤波器
*/
void kalman_filter_init(KalmanFilter* kf, uint8_t state_dim, uint8_t meas_dim) {
kf->state_dim = state_dim;
kf->meas_dim = meas_dim;
// 初始化为零
memset(kf->x, 0, sizeof(kf->x));
memset(kf->P, 0, sizeof(kf->P));
memset(kf->F, 0, sizeof(kf->F));
memset(kf->H, 0, sizeof(kf->H));
memset(kf->Q, 0, sizeof(kf->Q));
memset(kf->R, 0, sizeof(kf->R));
memset(kf->B, 0, sizeof(kf->B));
// P初始化为单位矩阵
for (uint8_t i = 0; i < state_dim; i++) {
kf->P[i][i] = 1.0f;
}
}
2.2 矩阵运算辅助函数¶
/**
* @brief 矩阵乘法 C = A * B
*/
void matrix_multiply(const float A[][MAX_STATE_DIM], uint8_t A_rows, uint8_t A_cols,
const float B[][MAX_STATE_DIM], uint8_t B_cols,
float C[][MAX_STATE_DIM]) {
for (uint8_t i = 0; i < A_rows; i++) {
for (uint8_t j = 0; j < B_cols; j++) {
C[i][j] = 0.0f;
for (uint8_t k = 0; k < A_cols; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
}
/**
* @brief 矩阵转置 B = A^T
*/
void matrix_transpose(const float A[][MAX_STATE_DIM], uint8_t rows, uint8_t cols,
float B[][MAX_STATE_DIM]) {
for (uint8_t i = 0; i < rows; i++) {
for (uint8_t j = 0; j < cols; j++) {
B[j][i] = A[i][j];
}
}
}
/**
* @brief 矩阵求逆(使用高斯-约旦消元法)
* @note 仅适用于小矩阵,大矩阵应使用更高效的算法
*/
int matrix_inverse(float A[][MAX_MEAS_DIM], uint8_t n,
float A_inv[][MAX_MEAS_DIM]) {
// 创建增广矩阵 [A | I]
float aug[MAX_MEAS_DIM][2 * MAX_MEAS_DIM];
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
aug[i][j] = A[i][j];
aug[i][j + n] = (i == j) ? 1.0f : 0.0f;
}
}
// 高斯-约旦消元
for (uint8_t i = 0; i < n; i++) {
// 寻找主元
float pivot = aug[i][i];
if (fabsf(pivot) < 1e-10f) {
return -1; // 矩阵奇异
}
// 归一化当前行
for (uint8_t j = 0; j < 2 * n; j++) {
aug[i][j] /= pivot;
}
// 消元
for (uint8_t k = 0; k < n; k++) {
if (k != i) {
float factor = aug[k][i];
for (uint8_t j = 0; j < 2 * n; j++) {
aug[k][j] -= factor * aug[i][j];
}
}
}
}
// 提取逆矩阵
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
A_inv[i][j] = aug[i][j + n];
}
}
return 0;
}
2.3 预测步骤¶
/**
* @brief 卡尔曼滤波器预测步骤
* @param kf 卡尔曼滤波器结构
* @param u 控制输入(可为NULL)
*/
void kalman_predict(KalmanFilter* kf, const float* u) {
uint8_t n = kf->state_dim;
// 1. 状态预测:x̂(k|k-1) = F·x̂(k-1|k-1) + B·u(k)
for (uint8_t i = 0; i < n; i++) {
kf->temp_x[i] = 0.0f;
for (uint8_t j = 0; j < n; j++) {
kf->temp_x[i] += kf->F[i][j] * kf->x[j];
}
// 添加控制输入(如果有)
if (u != NULL) {
for (uint8_t j = 0; j < n; j++) {
kf->temp_x[i] += kf->B[i][j] * u[j];
}
}
}
// 更新状态
memcpy(kf->x, kf->temp_x, n * sizeof(float));
// 2. 协方差预测:P(k|k-1) = F·P(k-1|k-1)·F^T + Q
// 计算 F·P
float FP[MAX_STATE_DIM][MAX_STATE_DIM];
matrix_multiply(kf->F, n, n, kf->P, n, FP);
// 计算 F^T
float FT[MAX_STATE_DIM][MAX_STATE_DIM];
matrix_transpose(kf->F, n, n, FT);
// 计算 F·P·F^T
matrix_multiply(FP, n, n, FT, n, kf->temp_P);
// 添加过程噪声 Q
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
kf->P[i][j] = kf->temp_P[i][j] + kf->Q[i][j];
}
}
}
2.4 更新步骤¶
/**
* @brief 卡尔曼滤波器更新步骤
* @param kf 卡尔曼滤波器结构
* @param z 测量向量
*/
void kalman_update(KalmanFilter* kf, const float* z) {
uint8_t n = kf->state_dim;
uint8_t m = kf->meas_dim;
// 1. 计算创新(innovation):y = z - H·x̂(k|k-1)
float y[MAX_MEAS_DIM];
for (uint8_t i = 0; i < m; i++) {
y[i] = z[i];
for (uint8_t j = 0; j < n; j++) {
y[i] -= kf->H[i][j] * kf->x[j];
}
}
// 2. 计算创新协方差:S = H·P·H^T + R
// 计算 H·P
float HP[MAX_MEAS_DIM][MAX_STATE_DIM];
for (uint8_t i = 0; i < m; i++) {
for (uint8_t j = 0; j < n; j++) {
HP[i][j] = 0.0f;
for (uint8_t k = 0; k < n; k++) {
HP[i][j] += kf->H[i][k] * kf->P[k][j];
}
}
}
// 计算 H^T
float HT[MAX_STATE_DIM][MAX_MEAS_DIM];
for (uint8_t i = 0; i < m; i++) {
for (uint8_t j = 0; j < n; j++) {
HT[j][i] = kf->H[i][j];
}
}
// 计算 S = H·P·H^T + R
float S[MAX_MEAS_DIM][MAX_MEAS_DIM];
for (uint8_t i = 0; i < m; i++) {
for (uint8_t j = 0; j < m; j++) {
S[i][j] = kf->R[i][j];
for (uint8_t k = 0; k < n; k++) {
S[i][j] += HP[i][k] * HT[k][j];
}
}
}
// 3. 计算卡尔曼增益:K = P·H^T·S^(-1)
// 计算 S^(-1)
float S_inv[MAX_MEAS_DIM][MAX_MEAS_DIM];
if (matrix_inverse(S, m, S_inv) != 0) {
// 矩阵奇异,跳过更新
return;
}
// 计算 P·H^T
float PHT[MAX_STATE_DIM][MAX_MEAS_DIM];
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < m; j++) {
PHT[i][j] = 0.0f;
for (uint8_t k = 0; k < n; k++) {
PHT[i][j] += kf->P[i][k] * HT[k][j];
}
}
}
// 计算 K = P·H^T·S^(-1)
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < m; j++) {
kf->K[i][j] = 0.0f;
for (uint8_t k = 0; k < m; k++) {
kf->K[i][j] += PHT[i][k] * S_inv[k][j];
}
}
}
// 4. 状态更新:x̂(k|k) = x̂(k|k-1) + K·y
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < m; j++) {
kf->x[i] += kf->K[i][j] * y[j];
}
}
// 5. 协方差更新:P(k|k) = (I - K·H)·P(k|k-1)
// 计算 K·H
float KH[MAX_STATE_DIM][MAX_STATE_DIM];
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
KH[i][j] = 0.0f;
for (uint8_t k = 0; k < m; k++) {
KH[i][j] += kf->K[i][k] * kf->H[k][j];
}
}
}
// 计算 (I - K·H)·P
for (uint8_t i = 0; i < n; i++) {
for (uint8_t j = 0; j < n; j++) {
float I_KH = (i == j) ? 1.0f : 0.0f;
I_KH -= KH[i][j];
kf->temp_P[i][j] = 0.0f;
for (uint8_t k = 0; k < n; k++) {
float I_KH_k = (i == k) ? 1.0f : 0.0f;
I_KH_k -= KH[i][k];
kf->temp_P[i][j] += I_KH_k * kf->P[k][j];
}
}
}
// 更新P
memcpy(kf->P, kf->temp_P, sizeof(kf->temp_P));
}
💬 讨论区
欢迎在这里分享您的想法、提出问题或参与讨论。需要 GitHub 账号登录。