BLOG8
跳转至

传感器数据融合技术

概述

传感器数据融合是将来自多个传感器的数据进行组合和处理,以获得比单个传感器更准确、更可靠的信息。在嵌入式系统中,数据融合技术广泛应用于姿态估计、导航定位、目标跟踪等领域。本教程将深入讲解卡尔曼滤波、互补滤波等核心算法,并提供完整的实现代码。

为什么需要数据融合

  1. 传感器互补性:
  2. 加速度计:长期稳定,但对振动敏感
  3. 陀螺仪:短期准确,但存在累积漂移
  4. 磁力计:提供绝对航向,但易受磁场干扰
  5. 融合后:取长补短,获得最佳性能

  6. 提高精度:

  7. 单传感器误差:±5°
  8. 融合后误差:±0.5°
  9. 精度提升10倍

  10. 增强鲁棒性:

  11. 单传感器失效时系统仍可工作
  12. 抗干扰能力增强
  13. 系统可靠性提高

  14. 实时性能:

  15. 优化算法可在MCU上实时运行
  16. 适合无人机、机器人等应用
  17. 低延迟、高更新率

数据融合方法对比

融合算法对比:

┌──────────────┬──────────┬──────────┬──────────┬──────────┐
│ 算法         │ 计算复杂度│ 精度     │ 实时性   │ 适用场景 │
├──────────────┼──────────┼──────────┼──────────┼──────────┤
│ 简单平均     │ 极低     │ 低       │ 极好     │ 简单应用 │
│ 加权平均     │ 低       │ 中       │ 很好     │ 基础融合 │
│ 互补滤波     │ 低       │ 中高     │ 很好     │ 姿态估计 │
│ 卡尔曼滤波   │ 中       │ 高       │ 好       │ 精确估计 │
│ 扩展卡尔曼   │ 高       │ 很高     │ 中       │ 非线性系统│
│ 粒子滤波     │ 极高     │ 极高     │ 差       │ 复杂系统 │
└──────────────┴──────────┴──────────┴──────────┴──────────┘

选择建议:
- 资源受限:互补滤波
- 精度要求高:卡尔曼滤波
- 非线性系统:扩展卡尔曼滤波
- 实时性要求高:互补滤波

第一部分:互补滤波算法

互补滤波原理

基本思想:

互补滤波原理图:

加速度计测量              陀螺仪测量
(长期稳定)              (短期准确)
     │                         │
     │ 低通滤波                │ 高通滤波
     │ (1-α)                   │ α
     │                         │
     └────────┬────────────────┘
              │
         融合输出
      (最佳估计)

数学公式:
θ(t) = α × [θ(t-1) + ω×Δt] + (1-α) × θ_accel

其中:
- θ(t): 当前角度估计
- θ(t-1): 上一次角度估计
- ω: 陀螺仪角速度
- Δt: 时间间隔
- θ_accel: 加速度计测量角度
- α: 滤波系数(0-1)

滤波系数选择:
- α = 0.98: 更信任陀螺仪(常用)
- α = 0.95: 平衡
- α = 0.90: 更信任加速度计

频率特性:
- 高频(快速运动):主要来自陀螺仪
- 低频(缓慢漂移):主要来自加速度计

互补滤波实现

头文件定义:

/**
 * @file    complementary_filter.h
 * @brief   互补滤波器实现
 */

#ifndef __COMPLEMENTARY_FILTER_H
#define __COMPLEMENTARY_FILTER_H

#include <stdint.h>
#include <stdbool.h>
#include <math.h>

/**
 * @brief  互补滤波器配置
 */
typedef struct {
    float alpha;            // 滤波系数(0-1,推荐0.98)
    float roll;             // 横滚角(度)
    float pitch;            // 俯仰角(度)
    float yaw;              // 偏航角(度)
    uint32_t last_time_ms;  // 上次更新时间(毫秒)
    bool initialized;       // 初始化标志
} ComplementaryFilter_t;

/**
 * @brief  传感器数据结构
 */
typedef struct {
    float accel_x;          // 加速度X (g)
    float accel_y;          // 加速度Y (g)
    float accel_z;          // 加速度Z (g)
    float gyro_x;           // 陀螺仪X (°/s)
    float gyro_y;           // 陀螺仪Y (°/s)
    float gyro_z;           // 陀螺仪Z (°/s)
} SensorData_t;

// 函数声明
void ComplementaryFilter_Init(ComplementaryFilter_t* filter, float alpha);
void ComplementaryFilter_Update(ComplementaryFilter_t* filter, 
                                SensorData_t* data, 
                                uint32_t current_time_ms);
void ComplementaryFilter_GetAngles(ComplementaryFilter_t* filter,
                                   float* roll, float* pitch, float* yaw);
void ComplementaryFilter_Reset(ComplementaryFilter_t* filter);

#endif /* __COMPLEMENTARY_FILTER_H */

源文件实现:

/**
 * @file    complementary_filter.c
 * @brief   互补滤波器实现
 */

#include "complementary_filter.h"

#define RAD_TO_DEG  (180.0f / M_PI)
#define DEG_TO_RAD  (M_PI / 180.0f)

/**
 * @brief  初始化互补滤波器
 * @param  filter: 滤波器结构指针
 * @param  alpha: 滤波系数(0-1,推荐0.98)
 */
void ComplementaryFilter_Init(ComplementaryFilter_t* filter, float alpha) {
    filter->alpha = alpha;
    filter->roll = 0.0f;
    filter->pitch = 0.0f;
    filter->yaw = 0.0f;
    filter->last_time_ms = 0;
    filter->initialized = false;
}

/**
 * @brief  更新互补滤波器
 * @param  filter: 滤波器结构指针
 * @param  data: 传感器数据
 * @param  current_time_ms: 当前时间(毫秒)
 */
void ComplementaryFilter_Update(ComplementaryFilter_t* filter,
                                SensorData_t* data,
                                uint32_t current_time_ms) {
    // 首次调用,初始化角度
    if (!filter->initialized) {
        // 使用加速度计初始化横滚角和俯仰角
        filter->roll = atan2f(data->accel_y, data->accel_z) * RAD_TO_DEG;
        filter->pitch = atan2f(-data->accel_x,
                              sqrtf(data->accel_y * data->accel_y +
                                    data->accel_z * data->accel_z)) * RAD_TO_DEG;
        filter->yaw = 0.0f;
        filter->last_time_ms = current_time_ms;
        filter->initialized = true;
        return;
    }

    // 计算时间间隔(秒)
    float dt = (current_time_ms - filter->last_time_ms) / 1000.0f;
    filter->last_time_ms = current_time_ms;

    // 防止时间间隔异常
    if (dt <= 0.0f || dt > 1.0f) {
        return;
    }

    // 步骤1:使用加速度计计算角度
    float accel_roll = atan2f(data->accel_y, data->accel_z) * RAD_TO_DEG;
    float accel_pitch = atan2f(-data->accel_x,
                               sqrtf(data->accel_y * data->accel_y +
                                     data->accel_z * data->accel_z)) * RAD_TO_DEG;

    // 步骤2:使用陀螺仪积分计算角度变化
    float gyro_roll_delta = data->gyro_x * dt;
    float gyro_pitch_delta = data->gyro_y * dt;
    float gyro_yaw_delta = data->gyro_z * dt;

    // 步骤3:互补滤波融合
    // 角度 = α × (上次角度 + 陀螺仪积分) + (1-α) × 加速度计角度
    filter->roll = filter->alpha * (filter->roll + gyro_roll_delta) +
                   (1.0f - filter->alpha) * accel_roll;

    filter->pitch = filter->alpha * (filter->pitch + gyro_pitch_delta) +
                    (1.0f - filter->alpha) * accel_pitch;

    // 偏航角只能通过陀螺仪积分(需要磁力计校正)
    filter->yaw += gyro_yaw_delta;

    // 限制偏航角范围在-180到180度
    while (filter->yaw > 180.0f) filter->yaw -= 360.0f;
    while (filter->yaw < -180.0f) filter->yaw += 360.0f;
}

/**
 * @brief  获取姿态角
 */
void ComplementaryFilter_GetAngles(ComplementaryFilter_t* filter,
                                   float* roll, float* pitch, float* yaw) {
    if (roll) *roll = filter->roll;
    if (pitch) *pitch = filter->pitch;
    if (yaw) *yaw = filter->yaw;
}

/**
 * @brief  重置滤波器
 */
void ComplementaryFilter_Reset(ComplementaryFilter_t* filter) {
    filter->roll = 0.0f;
    filter->pitch = 0.0f;
    filter->yaw = 0.0f;
    filter->initialized = false;
}

第二部分:卡尔曼滤波算法

卡尔曼滤波原理

基本概念:

卡尔曼滤波流程:

初始状态
  │
  ├──→ 预测步骤 ────────────┐
  │    - 状态预测           │
  │    - 协方差预测         │
  │                         │
  │                         ↓
  │    ┌─────────────────────┐
  │    │   预测值            │
  │    └─────────────────────┘
  │                         │
  │                         ↓
  └──→ 更新步骤 ←───────────┤
       - 计算卡尔曼增益     │
       - 更新状态估计       │
       - 更新协方差         │
                            │
                            ↓
                        输出估计值

数学模型:

预测步骤:
  x̂(k|k-1) = A × x̂(k-1|k-1) + B × u(k)
  P(k|k-1) = A × P(k-1|k-1) × A^T + Q

更新步骤:
  K(k) = P(k|k-1) × H^T × [H × P(k|k-1) × H^T + R]^(-1)
  x̂(k|k) = x̂(k|k-1) + K(k) × [z(k) - H × x̂(k|k-1)]
  P(k|k) = [I - K(k) × H] × P(k|k-1)

符号说明:
- x̂: 状态估计
- P: 估计协方差矩阵
- A: 状态转移矩阵
- B: 控制输入矩阵
- u: 控制输入
- Q: 过程噪声协方差
- K: 卡尔曼增益
- H: 观测矩阵
- z: 观测值
- R: 观测噪声协方差
- I: 单位矩阵

一维卡尔曼滤波实现

简化版本(单变量):

/**
 * @file    kalman_filter_1d.h
 * @brief   一维卡尔曼滤波器
 */

#ifndef __KALMAN_FILTER_1D_H
#define __KALMAN_FILTER_1D_H

#include <stdint.h>
#include <stdbool.h>

/**
 * @brief  一维卡尔曼滤波器结构
 */
typedef struct {
    float x;            // 状态估计
    float P;            // 估计协方差
    float Q;            // 过程噪声协方差
    float R;            // 观测噪声协方差
    float K;            // 卡尔曼增益
    bool initialized;   // 初始化标志
} KalmanFilter1D_t;

// 函数声明
void KalmanFilter1D_Init(KalmanFilter1D_t* kf, float Q, float R);
float KalmanFilter1D_Update(KalmanFilter1D_t* kf, float measurement);
void KalmanFilter1D_Reset(KalmanFilter1D_t* kf);

#endif /* __KALMAN_FILTER_1D_H */
/**
 * @file    kalman_filter_1d.c
 * @brief   一维卡尔曼滤波器实现
 */

#include "kalman_filter_1d.h"

/**
 * @brief  初始化一维卡尔曼滤波器
 * @param  kf: 滤波器结构指针
 * @param  Q: 过程噪声协方差(越大越信任测量值)
 * @param  R: 观测噪声协方差(越大越信任预测值)
 */
void KalmanFilter1D_Init(KalmanFilter1D_t* kf, float Q, float R) {
    kf->x = 0.0f;
    kf->P = 1.0f;
    kf->Q = Q;
    kf->R = R;
    kf->K = 0.0f;
    kf->initialized = false;
}

/**
 * @brief  更新卡尔曼滤波器
 * @param  kf: 滤波器结构指针
 * @param  measurement: 测量值
 * @return 滤波后的估计值
 */
float KalmanFilter1D_Update(KalmanFilter1D_t* kf, float measurement) {
    // 首次调用,直接使用测量值初始化
    if (!kf->initialized) {
        kf->x = measurement;
        kf->initialized = true;
        return kf->x;
    }

    // 预测步骤
    // x̂(k|k-1) = x̂(k-1|k-1)  (假设状态不变)
    // P(k|k-1) = P(k-1|k-1) + Q
    kf->P = kf->P + kf->Q;

    // 更新步骤
    // K(k) = P(k|k-1) / [P(k|k-1) + R]
    kf->K = kf->P / (kf->P + kf->R);

    // x̂(k|k) = x̂(k|k-1) + K(k) × [z(k) - x̂(k|k-1)]
    kf->x = kf->x + kf->K * (measurement - kf->x);

    // P(k|k) = [1 - K(k)] × P(k|k-1)
    kf->P = (1.0f - kf->K) * kf->P;

    return kf->x;
}

/**
 * @brief  重置卡尔曼滤波器
 */
void KalmanFilter1D_Reset(KalmanFilter1D_t* kf) {
    kf->x = 0.0f;
    kf->P = 1.0f;
    kf->K = 0.0f;
    kf->initialized = false;
}

姿态估计卡尔曼滤波

二维卡尔曼滤波(角度+角速度):

/**
 * @file    kalman_filter_angle.h
 * @brief   角度估计卡尔曼滤波器
 */

#ifndef __KALMAN_FILTER_ANGLE_H
#define __KALMAN_FILTER_ANGLE_H

#include <stdint.h>
#include <stdbool.h>

/**
 * @brief  角度卡尔曼滤波器结构
 */
typedef struct {
    // 状态向量 [角度, 角速度偏差]
    float angle;            // 角度估计(度)
    float bias;             // 角速度偏差估计(度/秒)

    // 协方差矩阵 P
    float P[2][2];

    // 噪声参数
    float Q_angle;          // 角度过程噪声
    float Q_bias;           // 偏差过程噪声
    float R_measure;        // 测量噪声

    // 时间间隔
    float dt;

    bool initialized;
} KalmanFilterAngle_t;

// 函数声明
void KalmanFilterAngle_Init(KalmanFilterAngle_t* kf,
                            float Q_angle, float Q_bias, float R_measure);
float KalmanFilterAngle_Update(KalmanFilterAngle_t* kf,
                               float newAngle, float newRate, float dt);
void KalmanFilterAngle_SetAngle(KalmanFilterAngle_t* kf, float angle);
float KalmanFilterAngle_GetAngle(KalmanFilterAngle_t* kf);
float KalmanFilterAngle_GetRate(KalmanFilterAngle_t* kf);

#endif /* __KALMAN_FILTER_ANGLE_H */
/**
 * @file    kalman_filter_angle.c
 * @brief   角度估计卡尔曼滤波器实现
 */

#include "kalman_filter_angle.h"

/**
 * @brief  初始化角度卡尔曼滤波器
 * @param  kf: 滤波器结构指针
 * @param  Q_angle: 角度过程噪声(推荐0.001)
 * @param  Q_bias: 偏差过程噪声(推荐0.003)
 * @param  R_measure: 测量噪声(推荐0.03)
 */
void KalmanFilterAngle_Init(KalmanFilterAngle_t* kf,
                            float Q_angle, float Q_bias, float R_measure) {
    kf->angle = 0.0f;
    kf->bias = 0.0f;

    // 初始化协方差矩阵
    kf->P[0][0] = 0.0f;
    kf->P[0][1] = 0.0f;
    kf->P[1][0] = 0.0f;
    kf->P[1][1] = 0.0f;

    kf->Q_angle = Q_angle;
    kf->Q_bias = Q_bias;
    kf->R_measure = R_measure;

    kf->dt = 0.0f;
    kf->initialized = false;
}

/**
 * @brief  更新卡尔曼滤波器
 * @param  kf: 滤波器结构指针
 * @param  newAngle: 加速度计测量的角度(度)
 * @param  newRate: 陀螺仪测量的角速度(度/秒)
 * @param  dt: 时间间隔(秒)
 * @return 滤波后的角度估计(度)
 */
float KalmanFilterAngle_Update(KalmanFilterAngle_t* kf,
                               float newAngle, float newRate, float dt) {
    // 首次调用,初始化
    if (!kf->initialized) {
        kf->angle = newAngle;
        kf->initialized = true;
        return kf->angle;
    }

    kf->dt = dt;

    // 步骤1:预测
    // 状态预测:angle = angle + dt * (newRate - bias)
    float rate = newRate - kf->bias;
    kf->angle += dt * rate;

    // 协方差预测:P = A * P * A^T + Q
    kf->P[0][0] += dt * (dt * kf->P[1][1] - kf->P[0][1] - kf->P[1][0] + kf->Q_angle);
    kf->P[0][1] -= dt * kf->P[1][1];
    kf->P[1][0] -= dt * kf->P[1][1];
    kf->P[1][1] += kf->Q_bias * dt;

    // 步骤2:更新
    // 计算卡尔曼增益:K = P * H^T * (H * P * H^T + R)^(-1)
    float S = kf->P[0][0] + kf->R_measure;  // 新息协方差
    float K[2];  // 卡尔曼增益
    K[0] = kf->P[0][0] / S;
    K[1] = kf->P[1][0] / S;

    // 更新状态估计:x = x + K * (z - H * x)
    float y = newAngle - kf->angle;  // 新息(测量残差)
    kf->angle += K[0] * y;
    kf->bias += K[1] * y;

    // 更新协方差:P = (I - K * H) * P
    float P00_temp = kf->P[0][0];
    float P01_temp = kf->P[0][1];

    kf->P[0][0] -= K[0] * P00_temp;
    kf->P[0][1] -= K[0] * P01_temp;
    kf->P[1][0] -= K[1] * P00_temp;
    kf->P[1][1] -= K[1] * P01_temp;

    return kf->angle;
}

/**
 * @brief  设置角度
 */
void KalmanFilterAngle_SetAngle(KalmanFilterAngle_t* kf, float angle) {
    kf->angle = angle;
}

/**
 * @brief  获取角度估计
 */
float KalmanFilterAngle_GetAngle(KalmanFilterAngle_t* kf) {
    return kf->angle;
}

/**
 * @brief  获取角速度估计(已去除偏差)
 */
float KalmanFilterAngle_GetRate(KalmanFilterAngle_t* kf) {
    return -kf->bias;  // 返回负偏差作为校正后的角速度
}

第三部分:多传感器融合

AHRS算法(姿态航向参考系统)

AHRS系统架构:

AHRS系统组成:

┌─────────────────────────────────────────┐
│         AHRS系统                        │
│                                         │
│  ┌──────────┐  ┌──────────┐  ┌────────┐│
│  │ 加速度计 │  │ 陀螺仪   │  │磁力计  ││
│  │ (3轴)    │  │ (3轴)    │  │(3轴)   ││
│  └────┬─────┘  └────┬─────┘  └───┬────┘│
│       │             │             │     │
│       └─────────────┼─────────────┘     │
│                     │                   │
│              ┌──────▼──────┐            │
│              │  数据融合   │            │
│              │  算法       │            │
│              └──────┬──────┘            │
│                     │                   │
│              ┌──────▼──────┐            │
│              │  姿态输出   │            │
│              │ Roll/Pitch/ │            │
│              │ Yaw         │            │
│              └─────────────┘            │
└─────────────────────────────────────────┘

传感器作用:
- 加速度计:提供横滚角和俯仰角(静态)
- 陀螺仪:提供角速度(动态)
- 磁力计:提供航向角(绝对方向)

融合策略:
1. 加速度计 + 陀螺仪 → Roll/Pitch
2. 磁力计 + 陀螺仪 → Yaw
3. 三者融合 → 完整姿态

九轴传感器融合实现

/**
 * @file    ahrs.h
 * @brief   AHRS姿态解算系统
 */

#ifndef __AHRS_H
#define __AHRS_H

#include <stdint.h>
#include <stdbool.h>
#include <math.h>

/**
 * @brief  九轴传感器数据
 */
typedef struct {
    float accel_x, accel_y, accel_z;    // 加速度 (g)
    float gyro_x, gyro_y, gyro_z;       // 陀螺仪 (°/s)
    float mag_x, mag_y, mag_z;          // 磁力计 (μT)
} AHRS_SensorData_t;

/**
 * @brief  AHRS姿态输出
 */
typedef struct {
    float roll;     // 横滚角 (°)
    float pitch;    // 俯仰角 (°)
    float yaw;      // 偏航角 (°)
} AHRS_Attitude_t;

/**
 * @brief  AHRS系统配置
 */
typedef struct {
    // 卡尔曼滤波器(Roll和Pitch)
    KalmanFilterAngle_t kf_roll;
    KalmanFilterAngle_t kf_pitch;

    // 互补滤波器(Yaw)
    ComplementaryFilter_t cf_yaw;

    // 磁偏角校正
    float magnetic_declination;  // 磁偏角(度)

    // 时间戳
    uint32_t last_time_ms;
    bool initialized;
} AHRS_t;

// 函数声明
void AHRS_Init(AHRS_t* ahrs, float magnetic_declination);
void AHRS_Update(AHRS_t* ahrs, AHRS_SensorData_t* data, uint32_t current_time_ms);
void AHRS_GetAttitude(AHRS_t* ahrs, AHRS_Attitude_t* attitude);
void AHRS_Reset(AHRS_t* ahrs);

#endif /* __AHRS_H */
/**
 * @file    ahrs.c
 * @brief   AHRS姿态解算系统实现
 */

#include "ahrs.h"
#include "kalman_filter_angle.h"
#include "complementary_filter.h"

#define RAD_TO_DEG  (180.0f / M_PI)
#define DEG_TO_RAD  (M_PI / 180.0f)

/**
 * @brief  初始化AHRS系统
 * @param  ahrs: AHRS结构指针
 * @param  magnetic_declination: 磁偏角(度)
 */
void AHRS_Init(AHRS_t* ahrs, float magnetic_declination) {
    // 初始化Roll卡尔曼滤波器
    KalmanFilterAngle_Init(&ahrs->kf_roll, 0.001f, 0.003f, 0.03f);

    // 初始化Pitch卡尔曼滤波器
    KalmanFilterAngle_Init(&ahrs->kf_pitch, 0.001f, 0.003f, 0.03f);

    // 初始化Yaw互补滤波器
    ComplementaryFilter_Init(&ahrs->cf_yaw, 0.98f);

    ahrs->magnetic_declination = magnetic_declination;
    ahrs->last_time_ms = 0;
    ahrs->initialized = false;
}

/**
 * @brief  更新AHRS系统
 * @param  ahrs: AHRS结构指针
 * @param  data: 九轴传感器数据
 * @param  current_time_ms: 当前时间(毫秒)
 */
void AHRS_Update(AHRS_t* ahrs, AHRS_SensorData_t* data, uint32_t current_time_ms) {
    // 计算时间间隔
    float dt;
    if (!ahrs->initialized) {
        dt = 0.01f;  // 假设10ms
        ahrs->last_time_ms = current_time_ms;
        ahrs->initialized = true;
    } else {
        dt = (current_time_ms - ahrs->last_time_ms) / 1000.0f;
        ahrs->last_time_ms = current_time_ms;
    }

    // 防止时间间隔异常
    if (dt <= 0.0f || dt > 1.0f) {
        dt = 0.01f;
    }

    // 步骤1:使用加速度计计算Roll和Pitch
    float accel_roll = atan2f(data->accel_y, data->accel_z) * RAD_TO_DEG;
    float accel_pitch = atan2f(-data->accel_x,
                               sqrtf(data->accel_y * data->accel_y +
                                     data->accel_z * data->accel_z)) * RAD_TO_DEG;

    // 步骤2:使用卡尔曼滤波融合加速度计和陀螺仪
    float roll = KalmanFilterAngle_Update(&ahrs->kf_roll, accel_roll, data->gyro_x, dt);
    float pitch = KalmanFilterAngle_Update(&ahrs->kf_pitch, accel_pitch, data->gyro_y, dt);

    // 步骤3:使用磁力计计算Yaw(航向角)
    // 磁力计倾斜补偿
    float roll_rad = roll * DEG_TO_RAD;
    float pitch_rad = pitch * DEG_TO_RAD;

    float mag_x_comp = data->mag_x * cosf(pitch_rad) +
                       data->mag_y * sinf(roll_rad) * sinf(pitch_rad) +
                       data->mag_z * cosf(roll_rad) * sinf(pitch_rad);

    float mag_y_comp = data->mag_y * cosf(roll_rad) -
                       data->mag_z * sinf(roll_rad);

    // 计算航向角
    float mag_yaw = atan2f(-mag_y_comp, mag_x_comp) * RAD_TO_DEG;

    // 应用磁偏角校正
    mag_yaw += ahrs->magnetic_declination;

    // 限制范围到0-360度
    while (mag_yaw < 0.0f) mag_yaw += 360.0f;
    while (mag_yaw >= 360.0f) mag_yaw -= 360.0f;

    // 步骤4:使用互补滤波融合磁力计和陀螺仪
    // 将磁力计航向角和陀螺仪Z轴数据融合
    SensorData_t yaw_data = {
        .accel_x = 0.0f,
        .accel_y = 0.0f,
        .accel_z = mag_yaw,  // 使用Z轴存储航向角
        .gyro_x = 0.0f,
        .gyro_y = 0.0f,
        .gyro_z = data->gyro_z
    };

    ComplementaryFilter_Update(&ahrs->cf_yaw, &yaw_data, current_time_ms);
}

/**
 * @brief  获取姿态角
 */
void AHRS_GetAttitude(AHRS_t* ahrs, AHRS_Attitude_t* attitude) {
    attitude->roll = KalmanFilterAngle_GetAngle(&ahrs->kf_roll);
    attitude->pitch = KalmanFilterAngle_GetAngle(&ahrs->kf_pitch);
    ComplementaryFilter_GetAngles(&ahrs->cf_yaw, NULL, NULL, &attitude->yaw);
}

/**
 * @brief  重置AHRS系统
 */
void AHRS_Reset(AHRS_t* ahrs) {
    KalmanFilterAngle_Init(&ahrs->kf_roll, 0.001f, 0.003f, 0.03f);
    KalmanFilterAngle_Init(&ahrs->kf_pitch, 0.001f, 0.003f, 0.03f);
    ComplementaryFilter_Reset(&ahrs->cf_yaw);
    ahrs->initialized = false;
}

第四部分:实践应用

应用示例1:互补滤波姿态估计

/**
 * @file    example_complementary_filter.c
 * @brief   互补滤波姿态估计示例
 */

#include "stm32f1xx_hal.h"
#include "mpu6050.h"
#include "complementary_filter.h"
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;

void example_complementary_filter(void) {
    // 初始化MPU6050
    MPU6050_Config mpu_config = {
        .hi2c = &hi2c1,
        .address = MPU6050_ADDR,
        .gyro_fs = MPU6050_GYRO_FS_250,
        .accel_fs = MPU6050_ACCEL_FS_2
    };

    if (!MPU6050_Init(&mpu_config)) {
        printf("MPU6050初始化失败!\n");
        return;
    }

    // 初始化互补滤波器
    ComplementaryFilter_t filter;
    ComplementaryFilter_Init(&filter, 0.98f);

    printf("互补滤波姿态估计\n");
    printf("═══════════════════════════════════════\n\n");

    MPU6050_Data mpu_data;
    SensorData_t sensor_data;
    float roll, pitch, yaw;

    while (1) {
        // 读取MPU6050数据
        if (MPU6050_ReadData(&mpu_config, &mpu_data)) {
            // 转换为传感器数据格式
            sensor_data.accel_x = mpu_data.accel_x;
            sensor_data.accel_y = mpu_data.accel_y;
            sensor_data.accel_z = mpu_data.accel_z;
            sensor_data.gyro_x = mpu_data.gyro_x;
            sensor_data.gyro_y = mpu_data.gyro_y;
            sensor_data.gyro_z = mpu_data.gyro_z;

            // 更新滤波器
            uint32_t current_time = HAL_GetTick();
            ComplementaryFilter_Update(&filter, &sensor_data, current_time);

            // 获取姿态角
            ComplementaryFilter_GetAngles(&filter, &roll, &pitch, &yaw);

            // 打印结果
            printf("姿态角: Roll=%.2f°, Pitch=%.2f°, Yaw=%.2f°\n",
                   roll, pitch, yaw);
        }

        HAL_Delay(10);  // 100Hz更新率
    }
}

应用示例2:卡尔曼滤波角度估计

/**
 * @file    example_kalman_filter.c
 * @brief   卡尔曼滤波角度估计示例
 */

#include "stm32f1xx_hal.h"
#include "mpu6050.h"
#include "kalman_filter_angle.h"
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;

void example_kalman_filter(void) {
    // 初始化MPU6050
    MPU6050_Config mpu_config = {
        .hi2c = &hi2c1,
        .address = MPU6050_ADDR,
        .gyro_fs = MPU6050_GYRO_FS_250,
        .accel_fs = MPU6050_ACCEL_FS_2
    };

    if (!MPU6050_Init(&mpu_config)) {
        printf("MPU6050初始化失败!\n");
        return;
    }

    // 初始化卡尔曼滤波器
    KalmanFilterAngle_t kf_roll, kf_pitch;
    KalmanFilterAngle_Init(&kf_roll, 0.001f, 0.003f, 0.03f);
    KalmanFilterAngle_Init(&kf_pitch, 0.001f, 0.003f, 0.03f);

    printf("卡尔曼滤波角度估计\n");
    printf("═══════════════════════════════════════\n\n");

    MPU6050_Data mpu_data;
    uint32_t last_time = HAL_GetTick();

    while (1) {
        // 读取MPU6050数据
        if (MPU6050_ReadData(&mpu_config, &mpu_data)) {
            // 计算时间间隔
            uint32_t current_time = HAL_GetTick();
            float dt = (current_time - last_time) / 1000.0f;
            last_time = current_time;

            // 使用加速度计计算角度
            float accel_roll = atan2f(mpu_data.accel_y, mpu_data.accel_z) * 180.0f / M_PI;
            float accel_pitch = atan2f(-mpu_data.accel_x,
                                      sqrtf(mpu_data.accel_y * mpu_data.accel_y +
                                            mpu_data.accel_z * mpu_data.accel_z)) * 180.0f / M_PI;

            // 卡尔曼滤波更新
            float roll = KalmanFilterAngle_Update(&kf_roll, accel_roll, mpu_data.gyro_x, dt);
            float pitch = KalmanFilterAngle_Update(&kf_pitch, accel_pitch, mpu_data.gyro_y, dt);

            // 打印结果
            printf("角度: Roll=%.2f°, Pitch=%.2f°\n", roll, pitch);
            printf("  加速度计: Roll=%.2f°, Pitch=%.2f°\n", accel_roll, accel_pitch);
            printf("  陀螺仪: X=%.2f°/s, Y=%.2f°/s\n\n",
                   mpu_data.gyro_x, mpu_data.gyro_y);
        }

        HAL_Delay(10);  // 100Hz更新率
    }
}

应用示例3:性能对比测试

/**
 * @file    example_comparison.c
 * @brief   不同滤波算法性能对比
 */

#include "stm32f1xx_hal.h"
#include "mpu6050.h"
#include "complementary_filter.h"
#include "kalman_filter_angle.h"
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;

void example_comparison(void) {
    // 初始化MPU6050
    MPU6050_Config mpu_config = {
        .hi2c = &hi2c1,
        .address = MPU6050_ADDR,
        .gyro_fs = MPU6050_GYRO_FS_250,
        .accel_fs = MPU6050_ACCEL_FS_2
    };

    if (!MPU6050_Init(&mpu_config)) {
        printf("MPU6050初始化失败!\n");
        return;
    }

    // 初始化三种方法
    ComplementaryFilter_t cf;
    KalmanFilterAngle_t kf;

    ComplementaryFilter_Init(&cf, 0.98f);
    KalmanFilterAngle_Init(&kf, 0.001f, 0.003f, 0.03f);

    printf("滤波算法性能对比\n");
    printf("═══════════════════════════════════════\n\n");
    printf("方法1: 仅加速度计\n");
    printf("方法2: 互补滤波\n");
    printf("方法3: 卡尔曼滤波\n\n");

    MPU6050_Data mpu_data;
    SensorData_t sensor_data;
    uint32_t last_time = HAL_GetTick();

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &mpu_data)) {
            uint32_t current_time = HAL_GetTick();
            float dt = (current_time - last_time) / 1000.0f;
            last_time = current_time;

            // 方法1:仅使用加速度计
            float accel_roll = atan2f(mpu_data.accel_y, mpu_data.accel_z) * 180.0f / M_PI;

            // 方法2:互补滤波
            sensor_data.accel_x = mpu_data.accel_x;
            sensor_data.accel_y = mpu_data.accel_y;
            sensor_data.accel_z = mpu_data.accel_z;
            sensor_data.gyro_x = mpu_data.gyro_x;
            sensor_data.gyro_y = mpu_data.gyro_y;
            sensor_data.gyro_z = mpu_data.gyro_z;

            ComplementaryFilter_Update(&cf, &sensor_data, current_time);
            float cf_roll, cf_pitch, cf_yaw;
            ComplementaryFilter_GetAngles(&cf, &cf_roll, &cf_pitch, &cf_yaw);

            // 方法3:卡尔曼滤波
            float kf_roll = KalmanFilterAngle_Update(&kf, accel_roll, mpu_data.gyro_x, dt);

            // 打印对比结果
            printf("Roll角度对比:\n");
            printf("  加速度计: %.2f°\n", accel_roll);
            printf("  互补滤波: %.2f°\n", cf_roll);
            printf("  卡尔曼滤波: %.2f°\n\n", kf_roll);
        }

        HAL_Delay(100);  // 10Hz更新率
    }
}

应用示例4:无人机姿态稳定

/**
 * @file    example_drone_stabilization.c
 * @brief   无人机姿态稳定应用
 */

#include "stm32f1xx_hal.h"
#include "mpu6050.h"
#include "complementary_filter.h"
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;
extern TIM_HandleTypeDef htim1;  // PWM定时器

// PID控制器结构
typedef struct {
    float Kp;           // 比例系数
    float Ki;           // 积分系数
    float Kd;           // 微分系数
    float integral;     // 积分累积
    float last_error;   // 上次误差
    float output_min;   // 输出最小值
    float output_max;   // 输出最大值
} PID_Controller_t;

/**
 * @brief  PID控制器更新
 */
float PID_Update(PID_Controller_t* pid, float setpoint, float measured, float dt) {
    // 计算误差
    float error = setpoint - measured;

    // 比例项
    float P = pid->Kp * error;

    // 积分项
    pid->integral += error * dt;
    float I = pid->Ki * pid->integral;

    // 微分项
    float D = pid->Kd * (error - pid->last_error) / dt;
    pid->last_error = error;

    // 计算输出
    float output = P + I + D;

    // 限幅
    if (output > pid->output_max) output = pid->output_max;
    if (output < pid->output_min) output = pid->output_min;

    return output;
}

/**
 * @brief  无人机姿态稳定主函数
 */
void example_drone_stabilization(void) {
    // 初始化MPU6050
    MPU6050_Config mpu_config = {
        .hi2c = &hi2c1,
        .address = MPU6050_ADDR,
        .gyro_fs = MPU6050_GYRO_FS_500,
        .accel_fs = MPU6050_ACCEL_FS_4
    };

    if (!MPU6050_Init(&mpu_config)) {
        printf("MPU6050初始化失败!\n");
        return;
    }

    // 初始化互补滤波器
    ComplementaryFilter_t filter;
    ComplementaryFilter_Init(&filter, 0.98f);

    // 初始化PID控制器
    PID_Controller_t pid_roll = {
        .Kp = 1.5f,
        .Ki = 0.05f,
        .Kd = 0.8f,
        .integral = 0.0f,
        .last_error = 0.0f,
        .output_min = -500.0f,
        .output_max = 500.0f
    };

    PID_Controller_t pid_pitch = {
        .Kp = 1.5f,
        .Ki = 0.05f,
        .Kd = 0.8f,
        .integral = 0.0f,
        .last_error = 0.0f,
        .output_min = -500.0f,
        .output_max = 500.0f
    };

    printf("无人机姿态稳定系统\n");
    printf("═══════════════════════════════════════\n\n");

    MPU6050_Data mpu_data;
    SensorData_t sensor_data;
    float roll, pitch, yaw;
    uint32_t last_time = HAL_GetTick();

    // 目标姿态(水平)
    float target_roll = 0.0f;
    float target_pitch = 0.0f;

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &mpu_data)) {
            uint32_t current_time = HAL_GetTick();
            float dt = (current_time - last_time) / 1000.0f;
            last_time = current_time;

            // 更新姿态估计
            sensor_data.accel_x = mpu_data.accel_x;
            sensor_data.accel_y = mpu_data.accel_y;
            sensor_data.accel_z = mpu_data.accel_z;
            sensor_data.gyro_x = mpu_data.gyro_x;
            sensor_data.gyro_y = mpu_data.gyro_y;
            sensor_data.gyro_z = mpu_data.gyro_z;

            ComplementaryFilter_Update(&filter, &sensor_data, current_time);
            ComplementaryFilter_GetAngles(&filter, &roll, &pitch, &yaw);

            // PID控制计算
            float roll_correction = PID_Update(&pid_roll, target_roll, roll, dt);
            float pitch_correction = PID_Update(&pid_pitch, target_pitch, pitch, dt);

            // 计算电机PWM值(假设四旋翼)
            // 基础油门 + 姿态修正
            float base_throttle = 1500.0f;  // 中位值

            float motor1 = base_throttle + roll_correction + pitch_correction;
            float motor2 = base_throttle - roll_correction + pitch_correction;
            float motor3 = base_throttle - roll_correction - pitch_correction;
            float motor4 = base_throttle + roll_correction - pitch_correction;

            // 限制PWM范围(1000-2000μs)
            motor1 = fminf(fmaxf(motor1, 1000.0f), 2000.0f);
            motor2 = fminf(fmaxf(motor2, 1000.0f), 2000.0f);
            motor3 = fminf(fmaxf(motor3, 1000.0f), 2000.0f);
            motor4 = fminf(fmaxf(motor4, 1000.0f), 2000.0f);

            // 输出PWM到电机
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, (uint16_t)motor1);
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, (uint16_t)motor2);
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, (uint16_t)motor3);
            __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, (uint16_t)motor4);

            // 打印调试信息
            printf("姿态: Roll=%.2f°, Pitch=%.2f°\n", roll, pitch);
            printf("修正: Roll=%.2f, Pitch=%.2f\n", roll_correction, pitch_correction);
            printf("电机: M1=%.0f, M2=%.0f, M3=%.0f, M4=%.0f\n\n",
                   motor1, motor2, motor3, motor4);
        }

        HAL_Delay(5);  // 200Hz控制频率
    }
}

应用示例5:数据记录与分析

/**
 * @file    example_data_logging.c
 * @brief   传感器数据记录与分析
 */

#include "stm32f1xx_hal.h"
#include "mpu6050.h"
#include "complementary_filter.h"
#include "kalman_filter_angle.h"
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;

#define LOG_BUFFER_SIZE 1000

// 数据记录结构
typedef struct {
    uint32_t timestamp;
    float accel_roll;
    float cf_roll;
    float kf_roll;
} DataLog_t;

DataLog_t log_buffer[LOG_BUFFER_SIZE];
uint32_t log_index = 0;

/**
 * @brief  记录数据
 */
void log_data(uint32_t timestamp, float accel_roll, float cf_roll, float kf_roll) {
    if (log_index < LOG_BUFFER_SIZE) {
        log_buffer[log_index].timestamp = timestamp;
        log_buffer[log_index].accel_roll = accel_roll;
        log_buffer[log_index].cf_roll = cf_roll;
        log_buffer[log_index].kf_roll = kf_roll;
        log_index++;
    }
}

/**
 * @brief  导出数据(CSV格式)
 */
void export_data(void) {
    printf("\n数据导出(CSV格式)\n");
    printf("═══════════════════════════════════════\n");
    printf("Timestamp,Accel_Roll,CF_Roll,KF_Roll\n");

    for (uint32_t i = 0; i < log_index; i++) {
        printf("%lu,%.3f,%.3f,%.3f\n",
               log_buffer[i].timestamp,
               log_buffer[i].accel_roll,
               log_buffer[i].cf_roll,
               log_buffer[i].kf_roll);
    }

    printf("\n总共记录 %lu 条数据\n", log_index);
}

/**
 * @brief  数据记录主函数
 */
void example_data_logging(void) {
    // 初始化MPU6050
    MPU6050_Config mpu_config = {
        .hi2c = &hi2c1,
        .address = MPU6050_ADDR,
        .gyro_fs = MPU6050_GYRO_FS_250,
        .accel_fs = MPU6050_ACCEL_FS_2
    };

    if (!MPU6050_Init(&mpu_config)) {
        printf("MPU6050初始化失败!\n");
        return;
    }

    // 初始化滤波器
    ComplementaryFilter_t cf;
    KalmanFilterAngle_t kf;

    ComplementaryFilter_Init(&cf, 0.98f);
    KalmanFilterAngle_Init(&kf, 0.001f, 0.003f, 0.03f);

    printf("传感器数据记录\n");
    printf("═══════════════════════════════════════\n");
    printf("记录时间: 10秒\n");
    printf("采样率: 100Hz\n\n");

    MPU6050_Data mpu_data;
    SensorData_t sensor_data;
    uint32_t start_time = HAL_GetTick();
    uint32_t last_time = start_time;

    // 记录10秒数据
    while ((HAL_GetTick() - start_time) < 10000 && log_index < LOG_BUFFER_SIZE) {
        if (MPU6050_ReadData(&mpu_config, &mpu_data)) {
            uint32_t current_time = HAL_GetTick();
            float dt = (current_time - last_time) / 1000.0f;
            last_time = current_time;

            // 计算加速度计角度
            float accel_roll = atan2f(mpu_data.accel_y, mpu_data.accel_z) * 180.0f / M_PI;

            // 互补滤波
            sensor_data.accel_x = mpu_data.accel_x;
            sensor_data.accel_y = mpu_data.accel_y;
            sensor_data.accel_z = mpu_data.accel_z;
            sensor_data.gyro_x = mpu_data.gyro_x;
            sensor_data.gyro_y = mpu_data.gyro_y;
            sensor_data.gyro_z = mpu_data.gyro_z;

            ComplementaryFilter_Update(&cf, &sensor_data, current_time);
            float cf_roll, cf_pitch, cf_yaw;
            ComplementaryFilter_GetAngles(&cf, &cf_roll, &cf_pitch, &cf_yaw);

            // 卡尔曼滤波
            float kf_roll = KalmanFilterAngle_Update(&kf, accel_roll, mpu_data.gyro_x, dt);

            // 记录数据
            log_data(current_time - start_time, accel_roll, cf_roll, kf_roll);

            // 显示进度
            if (log_index % 100 == 0) {
                printf("已记录 %lu 条数据...\n", log_index);
            }
        }

        HAL_Delay(10);  // 100Hz采样率
    }

    printf("\n记录完成!\n\n");

    // 导出数据
    export_data();
}

第五部分:高级主题

参数调优指南

互补滤波参数调优:

互补滤波系数α的选择:

α值影响:
┌─────────┬──────────────┬──────────────┐
│ α值     │ 特性         │ 适用场景     │
├─────────┼──────────────┼──────────────┤
│ 0.90    │ 更信任加速度计│ 静态应用     │
│ 0.95    │ 平衡         │ 一般应用     │
│ 0.98    │ 更信任陀螺仪 │ 动态应用     │
│ 0.99    │ 极度信任陀螺仪│ 高速运动     │
└─────────┴──────────────┴──────────────┘

调优步骤:
1. 从α=0.95开始测试
2. 如果对振动敏感,增大α(如0.98)
3. 如果存在明显漂移,减小α(如0.90)
4. 观察响应速度和稳定性
5. 在实际应用场景中验证

经验公式:
α = τ / (τ + Δt)

其中:
- τ: 时间常数(秒)
- Δt: 采样周期(秒)

示例:
- τ = 0.5s, Δt = 0.01s → α = 0.98
- τ = 0.2s, Δt = 0.01s → α = 0.95

卡尔曼滤波参数调优:

卡尔曼滤波噪声参数:

Q_angle(角度过程噪声):
- 推荐值:0.001
- 增大:更快响应,但更不稳定
- 减小:更稳定,但响应慢

Q_bias(偏差过程噪声):
- 推荐值:0.003
- 增大:更快适应陀螺仪漂移
- 减小:更稳定的偏差估计

R_measure(测量噪声):
- 推荐值:0.03
- 增大:更信任预测值
- 减小:更信任测量值

调优方法:
1. 固定R_measure = 0.03
2. 调整Q_angle观察响应速度
3. 调整Q_bias观察漂移补偿
4. 在实际环境中微调

参数关系:
Q/R比值越大 → 越信任测量值
Q/R比值越小 → 越信任预测值

性能优化技巧

计算优化:

/**
 * @brief  快速平方根倒数(Quake III算法)
 */
float fast_inv_sqrt(float x) {
    float halfx = 0.5f * x;
    float y = x;
    long i = *(long*)&y;
    i = 0x5f3759df - (i >> 1);
    y = *(float*)&i;
    y = y * (1.5f - (halfx * y * y));
    return y;
}

/**
 * @brief  优化的向量归一化
 */
void normalize_vector(float* x, float* y, float* z) {
    float inv_norm = fast_inv_sqrt(*x * *x + *y * *y + *z * *z);
    *x *= inv_norm;
    *y *= inv_norm;
    *z *= inv_norm;
}

/**
 * @brief  查表法三角函数(适用于精度要求不高的场景)
 */
#define SIN_TABLE_SIZE 360
static float sin_table[SIN_TABLE_SIZE];

void init_sin_table(void) {
    for (int i = 0; i < SIN_TABLE_SIZE; i++) {
        sin_table[i] = sinf(i * M_PI / 180.0f);
    }
}

float fast_sin(float angle_deg) {
    int index = (int)angle_deg % 360;
    if (index < 0) index += 360;
    return sin_table[index];
}

float fast_cos(float angle_deg) {
    return fast_sin(angle_deg + 90.0f);
}

内存优化:

/**
 * @brief  使用定点数代替浮点数(节省内存和计算)
 */
typedef int32_t fixed_point_t;

#define FIXED_POINT_SHIFT 16
#define FIXED_POINT_ONE (1 << FIXED_POINT_SHIFT)

// 浮点转定点
fixed_point_t float_to_fixed(float f) {
    return (fixed_point_t)(f * FIXED_POINT_ONE);
}

// 定点转浮点
float fixed_to_float(fixed_point_t fp) {
    return (float)fp / FIXED_POINT_ONE;
}

// 定点乘法
fixed_point_t fixed_mul(fixed_point_t a, fixed_point_t b) {
    return (fixed_point_t)(((int64_t)a * b) >> FIXED_POINT_SHIFT);
}

// 定点除法
fixed_point_t fixed_div(fixed_point_t a, fixed_point_t b) {
    return (fixed_point_t)(((int64_t)a << FIXED_POINT_SHIFT) / b);
}

故障诊断与调试

常见问题诊断:

问题1:角度估计不稳定,抖动严重
原因:
- 加速度计噪声过大
- 滤波参数不合适
- 采样率过低

解决方案:
1. 增大互补滤波α值(如0.98)
2. 增大卡尔曼滤波R_measure
3. 添加低通滤波器
4. 提高采样率到100Hz以上

问题2:角度估计存在漂移
原因:
- 陀螺仪零点偏移
- 温度影响
- 滤波参数不合适

解决方案:
1. 进行陀螺仪校准
2. 减小互补滤波α值(如0.95)
3. 增大卡尔曼滤波Q_bias
4. 添加温度补偿

问题3:响应速度慢
原因:
- 滤波参数过于保守
- 采样率过低
- 计算延迟

解决方案:
1. 减小卡尔曼滤波R_measure
2. 增大Q_angle
3. 提高采样率
4. 优化计算代码

问题4:磁力计干扰严重
原因:
- 附近有强磁场
- 电机电流干扰
- 金属物体影响

解决方案:
1. 远离干扰源
2. 磁力计校准
3. 使用软铁/硬铁补偿
4. 降低磁力计权重

调试工具:

/**
 * @brief  实时数据监控
 */
void debug_monitor(void) {
    static uint32_t last_print = 0;
    uint32_t current_time = HAL_GetTick();

    // 每100ms打印一次
    if (current_time - last_print >= 100) {
        last_print = current_time;

        printf("=== 传感器融合调试信息 ===\n");
        printf("时间: %lu ms\n", current_time);
        printf("加速度: X=%.3f, Y=%.3f, Z=%.3f g\n",
               sensor_data.accel_x, sensor_data.accel_y, sensor_data.accel_z);
        printf("陀螺仪: X=%.2f, Y=%.2f, Z=%.2f °/s\n",
               sensor_data.gyro_x, sensor_data.gyro_y, sensor_data.gyro_z);
        printf("姿态角: Roll=%.2f, Pitch=%.2f, Yaw=%.2f °\n",
               attitude.roll, attitude.pitch, attitude.yaw);
        printf("卡尔曼增益: %.4f\n", kf.K);
        printf("协方差: P00=%.6f, P11=%.6f\n\n", kf.P[0][0], kf.P[1][1]);
    }
}

/**
 * @brief  性能分析
 */
void performance_analysis(void) {
    uint32_t start_time, end_time;
    uint32_t total_time = 0;
    uint32_t iterations = 1000;

    printf("性能分析\n");
    printf("═══════════════════════════════════════\n\n");

    // 测试互补滤波性能
    start_time = HAL_GetTick();
    for (uint32_t i = 0; i < iterations; i++) {
        ComplementaryFilter_Update(&cf, &sensor_data, HAL_GetTick());
    }
    end_time = HAL_GetTick();
    total_time = end_time - start_time;

    printf("互补滤波:\n");
    printf("  总时间: %lu ms\n", total_time);
    printf("  平均时间: %.3f ms\n", (float)total_time / iterations);
    printf("  最大频率: %.0f Hz\n\n", 1000.0f * iterations / total_time);

    // 测试卡尔曼滤波性能
    start_time = HAL_GetTick();
    for (uint32_t i = 0; i < iterations; i++) {
        KalmanFilterAngle_Update(&kf, 0.0f, 0.0f, 0.01f);
    }
    end_time = HAL_GetTick();
    total_time = end_time - start_time;

    printf("卡尔曼滤波:\n");
    printf("  总时间: %lu ms\n", total_time);
    printf("  平均时间: %.3f ms\n", (float)total_time / iterations);
    printf("  最大频率: %.0f Hz\n\n", 1000.0f * iterations / total_time);
}

总结

关键要点

  1. 数据融合的必要性:
  2. 单传感器存在固有缺陷
  3. 融合可以取长补短
  4. 提高精度和鲁棒性
  5. 适应不同应用场景

  6. 互补滤波:

  7. 计算简单,实时性好
  8. 适合资源受限的嵌入式系统
  9. 参数调优相对简单
  10. 精度中等,满足大多数应用

  11. 卡尔曼滤波:

  12. 理论基础扎实,精度高
  13. 可以估计系统状态和噪声
  14. 计算量适中,可在MCU上运行
  15. 参数调优需要经验

  16. AHRS系统:

  17. 九轴传感器融合
  18. 提供完整姿态信息
  19. 适用于导航和控制
  20. 需要磁力计校准

  21. 实际应用:

  22. 无人机姿态稳定
  23. 机器人平衡控制
  24. 可穿戴设备
  25. 虚拟现实追踪

算法选择建议

应用场景与算法选择:

┌──────────────────┬──────────────┬──────────────┐
│ 应用场景         │ 推荐算法     │ 理由         │
├──────────────────┼──────────────┼──────────────┤
│ 简单姿态检测     │ 互补滤波     │ 简单高效     │
│ 无人机飞控       │ 卡尔曼滤波   │ 精度要求高   │
│ 机器人导航       │ AHRS系统     │ 需要航向角   │
│ 可穿戴设备       │ 互补滤波     │ 功耗低       │
│ 虚拟现实         │ 卡尔曼滤波   │ 低延迟高精度 │
│ 工业测量         │ 卡尔曼滤波   │ 精度要求高   │
└──────────────────┴──────────────┴──────────────┘

性能对比:
- 计算复杂度:互补滤波 < 卡尔曼滤波 < 扩展卡尔曼
- 精度:互补滤波 < 卡尔曼滤波 < 扩展卡尔曼
- 实时性:互补滤波 > 卡尔曼滤波 > 扩展卡尔曼
- 易用性:互补滤波 > 卡尔曼滤波 > 扩展卡尔曼

最佳实践

  1. 传感器校准:
  2. 定期进行零点校准
  3. 记录温度补偿系数
  4. 磁力计需要三维校准
  5. 保存校准数据到Flash

  6. 参数调优:

  7. 从推荐值开始
  8. 在实际环境中测试
  9. 记录不同参数的效果
  10. 建立参数数据库

  11. 性能优化:

  12. 使用定点数代替浮点数
  13. 查表法替代三角函数
  14. 减少不必要的计算
  15. 使用DMA传输数据

  16. 错误处理:

  17. 检测传感器故障
  18. 处理数据异常值
  19. 实现故障安全机制
  20. 记录错误日志

  21. 测试验证:

  22. 静态精度测试
  23. 动态响应测试
  24. 长时间稳定性测试
  25. 极端环境测试

常见误区

  1. 误区1:滤波参数越大越好
  2. 错误:盲目增大滤波参数
  3. 正确:根据应用场景选择合适参数
  4. 原因:参数过大会导致响应慢或不稳定

  5. 误区2:采样率越高越好

  6. 错误:无限提高采样率
  7. 正确:选择合适的采样率(通常100-200Hz)
  8. 原因:过高采样率增加计算负担,收益递减

  9. 误区3:只使用一种传感器

  10. 错误:仅依赖加速度计或陀螺仪
  11. 正确:融合多种传感器数据
  12. 原因:单传感器无法克服固有缺陷

  13. 误区4:忽略传感器校准

  14. 错误:直接使用原始数据
  15. 正确:定期校准传感器
  16. 原因:零点偏移会严重影响精度

  17. 误区5:过度依赖滤波算法

  18. 错误:期望滤波解决所有问题
  19. 正确:从硬件设计开始优化
  20. 原因:算法无法弥补硬件缺陷

进阶学习

  1. 扩展卡尔曼滤波(EKF):
  2. 处理非线性系统
  3. 更高的精度
  4. 更复杂的实现

  5. 无迹卡尔曼滤波(UKF):

  6. 更好的非线性处理
  7. 不需要计算雅可比矩阵
  8. 计算量较大

  9. 粒子滤波:

  10. 处理非高斯噪声
  11. 多模态分布
  12. 计算量极大

  13. 四元数姿态表示:

  14. 避免万向节死锁
  15. 计算效率高
  16. 插值平滑

  17. 传感器标定:

  18. 六面法标定
  19. 椭球拟合
  20. 温度补偿

参考资料

书籍: - 《卡尔曼滤波原理及应用》 - 《惯性导航系统》 - 《传感器融合算法》 - 《现代控制理论》

论文: - "Keeping a Good Attitude: A Quaternion-Based Orientation Filter" - "An efficient orientation filter for inertial and inertial/magnetic sensor arrays" - "Complementary Filter Design on the Special Orthogonal Group SO(3)"

开源项目: - MultiWii:多旋翼飞控 - Betaflight:竞速无人机飞控 - PX4:专业飞控系统 - Madgwick AHRS:开源姿态解算算法

在线资源: - InvenSense开发者中心 - STM32传感器应用笔记 - Arduino传感器库 - ROS IMU驱动包

实践建议

  1. 从简单开始:
  2. 先实现互补滤波
  3. 理解基本原理
  4. 逐步增加复杂度

  5. 充分测试:

  6. 静态测试
  7. 动态测试
  8. 边界条件测试
  9. 长时间运行测试

  10. 记录数据:

  11. 保存原始数据
  12. 记录滤波结果
  13. 分析性能指标
  14. 建立数据库

  15. 持续优化:

  16. 分析性能瓶颈
  17. 优化关键代码
  18. 调整参数
  19. 验证改进效果

  20. 学习交流:

  21. 参与开源项目
  22. 阅读他人代码
  23. 分享经验
  24. 持续学习

下一步学习: - 传感器校准与误差补偿 - 智能环境监测站项目 - 无刷电机FOC控制

相关内容: - 加速度计与陀螺仪基础 - 生物传感器:心率与血氧检测 - PID控制算法实战