跳转至

加速度计与陀螺仪基础

概述

加速度计和陀螺仪是现代嵌入式系统中最重要的运动传感器,广泛应用于智能手机、无人机、机器人、可穿戴设备、游戏控制器等领域。本文将详细介绍MEMS(微机电系统)加速度计和陀螺仪的工作原理,重点讲解MPU6050六轴传感器的使用方法、数据解析和姿态计算。

为什么需要运动传感器

  1. 姿态检测
  2. 设备方向识别
  3. 屏幕旋转控制
  4. 水平校准
  5. 倾斜角度测量

  6. 运动追踪

  7. 步数统计
  8. 运动轨迹记录
  9. 手势识别
  10. 跌倒检测

  11. 导航定位

  12. 惯性导航系统
  13. 航向角计算
  14. 位置估算
  15. GPS辅助定位

  16. 稳定控制

  17. 无人机姿态稳定
  18. 云台防抖
  19. 机器人平衡
  20. 相机防抖

加速度计与陀螺仪对比

传感器特性对比:

┌──────────┬────────────────┬────────────────┐
│ 特性     │ 加速度计       │ 陀螺仪         │
├──────────┼────────────────┼────────────────┤
│ 测量对象 │ 线性加速度     │ 角速度         │
│ 单位     │ g (m/s²)       │ °/s (rad/s)    │
│ 坐标轴   │ X, Y, Z        │ X, Y, Z        │
│ 静态精度 │ 高             │ 低(漂移)     │
│ 动态响应 │ 慢             │ 快             │
│ 重力影响 │ 有             │ 无             │
│ 主要用途 │ 倾角测量       │ 旋转速度测量   │
│ 典型应用 │ 水平检测       │ 航向角计算     │
└──────────┴────────────────┴────────────────┘

互补特性:
- 加速度计:长期稳定,但对振动敏感
- 陀螺仪:短期准确,但存在累积漂移
- 组合使用:通过数据融合获得最佳性能

第一部分:MEMS原理

加速度计工作原理

MEMS加速度计结构

MEMS加速度计原理图:

固定电极        可动质量块        固定电极
    │              │                │
    ├──────────────┼──────────────┤
    │   ┌──────────┴──────────┐   │
    │   │                      │   │
    ├───┤      质量块 (m)      ├───┤
    │   │                      │   │
    │   └──────────┬──────────┘   │
    ├──────────────┼──────────────┤
    │              │                │
   C1            弹簧              C2

工作原理:
1. 质量块通过弹簧悬浮在固定电极之间
2. 加速度使质量块产生位移
3. 位移改变电容值(C1和C2)
4. 电容变化转换为电压信号
5. ADC将电压转换为数字量

测量公式:
F = m × a  (牛顿第二定律)
x = F / k  (胡克定律,k为弹簧系数)

电容变化:
ΔC = ε₀ × A × Δx / d²
其中:
- ε₀: 真空介电常数
- A: 电极面积
- Δx: 位移
- d: 电极间距

三轴加速度计

三轴加速度计坐标系:

        Z轴(垂直向上)
        └────→ X轴(向右)
     ↙ Y轴(向前)

重力加速度分量:
- 水平放置:X=0g, Y=0g, Z=1g
- 垂直放置:X=1g, Y=0g, Z=0g
- 倾斜θ角:X=sin(θ)g, Y=0g, Z=cos(θ)g

测量范围:
- ±2g:高精度,适合姿态检测
- ±4g:平衡精度和范围
- ±8g:适合运动检测
- ±16g:高冲击应用

陀螺仪工作原理

MEMS陀螺仪结构

MEMS陀螺仪原理(科里奥利效应):

驱动方向
    │  质量块振动
    └────→ 检测方向
         (科里奥利力)

工作原理:
1. 质量块在驱动方向上振动
2. 当传感器旋转时,产生科里奥利力
3. 科里奥利力使质量块在检测方向上位移
4. 位移量正比于角速度
5. 电容检测位移并转换为数字量

科里奥利力公式:
Fc = 2m × v × ω
其中:
- m: 质量块质量
- v: 振动速度
- ω: 角速度

角速度积分得到角度:
θ = ∫ω dt

三轴陀螺仪

三轴陀螺仪测量:

绕X轴旋转(Roll - 横滚):
    └────→ X轴

绕Y轴旋转(Pitch - 俯仰):
    └────→ Y轴

绕Z轴旋转(Yaw - 偏航):
    └────→ Z轴

测量范围:
- ±250°/s:高精度,慢速旋转
- ±500°/s:平衡精度和范围
- ±1000°/s:快速旋转
- ±2000°/s:极快旋转

第二部分:MPU6050传感器

MPU6050简介

MPU6050特性

MPU6050六轴传感器:

集成组件:
┌─────────────────────────────┐
│        MPU6050              │
│                             │
│  ┌──────────────────────┐  │
│  │  三轴加速度计        │  │
│  │  ±2g/±4g/±8g/±16g   │  │
│  └──────────────────────┘  │
│                             │
│  ┌──────────────────────┐  │
│  │  三轴陀螺仪          │  │
│  │  ±250/±500/±1000/   │  │
│  │  ±2000 °/s          │  │
│  └──────────────────────┘  │
│                             │
│  ┌──────────────────────┐  │
│  │  16位ADC             │  │
│  └──────────────────────┘  │
│                             │
│  ┌──────────────────────┐  │
│  │  数字运动处理器(DMP)│  │
│  └──────────────────────┘  │
│                             │
│  ┌──────────────────────┐  │
│  │  I2C接口             │  │
│  └──────────────────────┘  │
└─────────────────────────────┘

主要特性:
- 工作电压:2.375V - 3.46V
- 工作电流:3.9mA(正常模式)
- 睡眠电流:5μA
- I2C速度:400kHz
- 数据输出速率:最高8kHz
- 内置温度传感器
- 可编程中断
- 内置FIFO缓冲区(1024字节)

硬件连接

MPU6050引脚定义

MPU6050模块引脚(GY-521):

    ┌─────────────┐
    │   MPU6050   │
    │   GY-521    │
    └─┬─┬─┬─┬─┬─┬─┬─┬─┘
      1 2 3 4 5 6 7 8

引脚说明:
1. VCC  - 电源正极(3.3V-5V)
2. GND  - 电源地
3. SCL  - I2C时钟线
4. SDA  - I2C数据线
5. XDA  - 辅助I2C数据(可选)
6. XCL  - 辅助I2C时钟(可选)
7. AD0  - I2C地址选择
8. INT  - 中断输出(可选)

I2C地址:
- AD0接GND: 0x68
- AD0接VCC: 0x69

硬件连接电路

连接方案:

MCU                    MPU6050
                      ┌─────────┐
3.3V ─────────────────┤1 VCC    │
                      │         │
GND ──────────────────┤2 GND    │
                      │         │
SCL ──────┬───────────┤3 SCL    │
          │           │         │
         ┌┴┐          │4 SDA    │
    4.7k │ │          │         │
         └┬┘          │5 XDA    │──NC
          │           │         │
SDA ──────┼──┬────────┤6 XCL    │──NC
          │  │        │         │
         ┌┴┐ │        │7 AD0    │──GND (地址0x68)
    4.7k │ │ │        │         │
         └┬┘ │        │8 INT    │──GPIO (可选)
          │  │        └─────────┘
GND ──────┴──┘

关键要点:
1. SCL和SDA需要上拉电阻(4.7kΩ)
2. 建议使用3.3V供电(更稳定)
3. AD0引脚选择I2C地址
4. INT引脚可用于数据就绪中断
5. 模块通常已集成上拉电阻和稳压器

MPU6050寄存器

重要寄存器列表

MPU6050寄存器地址:

配置寄存器:
0x19  SMPLRT_DIV      - 采样率分频器
0x1A  CONFIG          - 配置寄存器(DLPF)
0x1B  GYRO_CONFIG     - 陀螺仪配置
0x1C  ACCEL_CONFIG    - 加速度计配置
0x23  FIFO_EN         - FIFO使能
0x37  INT_PIN_CFG     - 中断引脚配置
0x38  INT_ENABLE      - 中断使能
0x3A  INT_STATUS      - 中断状态
0x6A  USER_CTRL       - 用户控制
0x6B  PWR_MGMT_1      - 电源管理1
0x6C  PWR_MGMT_2      - 电源管理2
0x75  WHO_AM_I        - 设备ID(0x68)

数据寄存器:
0x3B  ACCEL_XOUT_H    - 加速度X高字节
0x3C  ACCEL_XOUT_L    - 加速度X低字节
0x3D  ACCEL_YOUT_H    - 加速度Y高字节
0x3E  ACCEL_YOUT_L    - 加速度Y低字节
0x3F  ACCEL_ZOUT_H    - 加速度Z高字节
0x40  ACCEL_ZOUT_L    - 加速度Z低字节
0x41  TEMP_OUT_H      - 温度高字节
0x42  TEMP_OUT_L      - 温度低字节
0x43  GYRO_XOUT_H     - 陀螺仪X高字节
0x44  GYRO_XOUT_L     - 陀螺仪X低字节
0x45  GYRO_YOUT_H     - 陀螺仪Y高字节
0x46  GYRO_YOUT_L     - 陀螺仪Y低字节
0x47  GYRO_ZOUT_H     - 陀螺仪Z高字节
0x48  GYRO_ZOUT_L     - 陀螺仪Z低字节

配置示例:
PWR_MGMT_1 (0x6B):
  Bit 7: DEVICE_RESET  - 设备复位
  Bit 6: SLEEP         - 睡眠模式
  Bit 5: CYCLE         - 循环模式
  Bit 3: TEMP_DIS      - 温度传感器禁用
  Bit 2-0: CLKSEL      - 时钟源选择

GYRO_CONFIG (0x1B):
  Bit 4-3: FS_SEL      - 量程选择
    00: ±250°/s
    01: ±500°/s
    10: ±1000°/s
    11: ±2000°/s

ACCEL_CONFIG (0x1C):
  Bit 4-3: AFS_SEL     - 量程选择
    00: ±2g
    01: ±4g
    10: ±8g
    11: ±16g

第三部分:MPU6050驱动开发

驱动头文件

/**
 * @file    mpu6050.h
 * @brief   MPU6050六轴传感器驱动
 * @author  嵌入式知识平台
 */

#ifndef __MPU6050_H
#define __MPU6050_H

#include "stm32f1xx_hal.h"
#include <stdbool.h>
#include <math.h>

// MPU6050 I2C地址
#define MPU6050_ADDR        0x68    // AD0接GND
#define MPU6050_ADDR_ALT    0x69    // AD0接VCC

// MPU6050寄存器地址
#define MPU6050_REG_SMPLRT_DIV      0x19
#define MPU6050_REG_CONFIG          0x1A
#define MPU6050_REG_GYRO_CONFIG     0x1B
#define MPU6050_REG_ACCEL_CONFIG    0x1C
#define MPU6050_REG_INT_ENABLE      0x38
#define MPU6050_REG_ACCEL_XOUT_H    0x3B
#define MPU6050_REG_TEMP_OUT_H      0x41
#define MPU6050_REG_GYRO_XOUT_H     0x43
#define MPU6050_REG_PWR_MGMT_1      0x6B
#define MPU6050_REG_WHO_AM_I        0x75

/**
 * @brief  陀螺仪量程
 */
typedef enum {
    MPU6050_GYRO_FS_250  = 0,    // ±250°/s
    MPU6050_GYRO_FS_500  = 1,    // ±500°/s
    MPU6050_GYRO_FS_1000 = 2,    // ±1000°/s
    MPU6050_GYRO_FS_2000 = 3     // ±2000°/s
} MPU6050_GyroFS;

/**
 * @brief  加速度计量程
 */
typedef enum {
    MPU6050_ACCEL_FS_2  = 0,     // ±2g
    MPU6050_ACCEL_FS_4  = 1,     // ±4g
    MPU6050_ACCEL_FS_8  = 2,     // ±8g
    MPU6050_ACCEL_FS_16 = 3      // ±16g
} MPU6050_AccelFS;

/**
 * @brief  原始数据结构
 */
typedef struct {
    int16_t accel_x;    // 加速度X原始值
    int16_t accel_y;    // 加速度Y原始值
    int16_t accel_z;    // 加速度Z原始值
    int16_t temp;       // 温度原始值
    int16_t gyro_x;     // 陀螺仪X原始值
    int16_t gyro_y;     // 陀螺仪Y原始值
    int16_t gyro_z;     // 陀螺仪Z原始值
} MPU6050_RawData;

/**
 * @brief  处理后数据结构
 */
typedef struct {
    float accel_x;      // 加速度X (g)
    float accel_y;      // 加速度Y (g)
    float accel_z;      // 加速度Z (g)
    float temperature;  // 温度 (°C)
    float gyro_x;       // 陀螺仪X (°/s)
    float gyro_y;       // 陀螺仪Y (°/s)
    float gyro_z;       // 陀螺仪Z (°/s)
} MPU6050_Data;

/**
 * @brief  姿态角结构
 */
typedef struct {
    float roll;         // 横滚角 (°)
    float pitch;        // 俯仰角 (°)
    float yaw;          // 偏航角 (°)
} MPU6050_Attitude;

/**
 * @brief  MPU6050配置
 */
typedef struct {
    I2C_HandleTypeDef* hi2c;        // I2C句柄
    uint8_t address;                // I2C地址
    MPU6050_GyroFS gyro_fs;         // 陀螺仪量程
    MPU6050_AccelFS accel_fs;       // 加速度计量程
    float gyro_scale;               // 陀螺仪比例因子
    float accel_scale;              // 加速度计比例因子
} MPU6050_Config;

// 函数声明
bool MPU6050_Init(MPU6050_Config* config);
bool MPU6050_ReadRaw(MPU6050_Config* config, MPU6050_RawData* raw);
bool MPU6050_ReadData(MPU6050_Config* config, MPU6050_Data* data);
void MPU6050_CalculateAttitude(MPU6050_Data* data, MPU6050_Attitude* attitude);
bool MPU6050_Calibrate(MPU6050_Config* config);

#endif /* __MPU6050_H */

驱动实现

/**
 * @file    mpu6050.c
 * @brief   MPU6050六轴传感器驱动实现
 */

#include "mpu6050.h"

// 私有函数声明
static bool MPU6050_WriteReg(MPU6050_Config* config, uint8_t reg, uint8_t data);
static bool MPU6050_ReadReg(MPU6050_Config* config, uint8_t reg, uint8_t* data);
static bool MPU6050_ReadRegs(MPU6050_Config* config, uint8_t reg, 
                             uint8_t* data, uint16_t length);
static void MPU6050_UpdateScaleFactors(MPU6050_Config* config);

/**
 * @brief  写入寄存器
 */
static bool MPU6050_WriteReg(MPU6050_Config* config, uint8_t reg, uint8_t data) {
    HAL_StatusTypeDef status = HAL_I2C_Mem_Write(
        config->hi2c,
        config->address << 1,
        reg,
        I2C_MEMADD_SIZE_8BIT,
        &data,
        1,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取寄存器
 */
static bool MPU6050_ReadReg(MPU6050_Config* config, uint8_t reg, uint8_t* data) {
    HAL_StatusTypeDef status = HAL_I2C_Mem_Read(
        config->hi2c,
        config->address << 1,
        reg,
        I2C_MEMADD_SIZE_8BIT,
        data,
        1,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  读取多个寄存器
 */
static bool MPU6050_ReadRegs(MPU6050_Config* config, uint8_t reg,
                             uint8_t* data, uint16_t length) {
    HAL_StatusTypeDef status = HAL_I2C_Mem_Read(
        config->hi2c,
        config->address << 1,
        reg,
        I2C_MEMADD_SIZE_8BIT,
        data,
        length,
        HAL_MAX_DELAY
    );

    return (status == HAL_OK);
}

/**
 * @brief  更新比例因子
 */
static void MPU6050_UpdateScaleFactors(MPU6050_Config* config) {
    // 陀螺仪比例因子
    switch (config->gyro_fs) {
        case MPU6050_GYRO_FS_250:
            config->gyro_scale = 131.0f;
            break;
        case MPU6050_GYRO_FS_500:
            config->gyro_scale = 65.5f;
            break;
        case MPU6050_GYRO_FS_1000:
            config->gyro_scale = 32.8f;
            break;
        case MPU6050_GYRO_FS_2000:
            config->gyro_scale = 16.4f;
            break;
    }

    // 加速度计比例因子
    switch (config->accel_fs) {
        case MPU6050_ACCEL_FS_2:
            config->accel_scale = 16384.0f;
            break;
        case MPU6050_ACCEL_FS_4:
            config->accel_scale = 8192.0f;
            break;
        case MPU6050_ACCEL_FS_8:
            config->accel_scale = 4096.0f;
            break;
        case MPU6050_ACCEL_FS_16:
            config->accel_scale = 2048.0f;
            break;
    }
}

/**
 * @brief  初始化MPU6050
 */
bool MPU6050_Init(MPU6050_Config* config) {
    uint8_t who_am_i;

    // 读取WHO_AM_I寄存器验证通信
    if (!MPU6050_ReadReg(config, MPU6050_REG_WHO_AM_I, &who_am_i)) {
        return false;
    }

    // 检查设备ID
    if (who_am_i != 0x68) {
        return false;
    }

    // 复位设备
    if (!MPU6050_WriteReg(config, MPU6050_REG_PWR_MGMT_1, 0x80)) {
        return false;
    }
    HAL_Delay(100);

    // 唤醒设备,选择PLL时钟源
    if (!MPU6050_WriteReg(config, MPU6050_REG_PWR_MGMT_1, 0x01)) {
        return false;
    }
    HAL_Delay(10);

    // 配置采样率分频器(1kHz / (1 + 7) = 125Hz)
    if (!MPU6050_WriteReg(config, MPU6050_REG_SMPLRT_DIV, 0x07)) {
        return false;
    }

    // 配置数字低通滤波器(DLPF = 3, 带宽44Hz)
    if (!MPU6050_WriteReg(config, MPU6050_REG_CONFIG, 0x03)) {
        return false;
    }

    // 配置陀螺仪量程
    if (!MPU6050_WriteReg(config, MPU6050_REG_GYRO_CONFIG, 
                          config->gyro_fs << 3)) {
        return false;
    }

    // 配置加速度计量程
    if (!MPU6050_WriteReg(config, MPU6050_REG_ACCEL_CONFIG,
                          config->accel_fs << 3)) {
        return false;
    }

    // 更新比例因子
    MPU6050_UpdateScaleFactors(config);

    return true;
}

/**
 * @brief  读取原始数据
 */
bool MPU6050_ReadRaw(MPU6050_Config* config, MPU6050_RawData* raw) {
    uint8_t buffer[14];

    // 从0x3B开始连续读取14字节
    if (!MPU6050_ReadRegs(config, MPU6050_REG_ACCEL_XOUT_H, buffer, 14)) {
        return false;
    }

    // 解析数据(大端序)
    raw->accel_x = (int16_t)((buffer[0] << 8) | buffer[1]);
    raw->accel_y = (int16_t)((buffer[2] << 8) | buffer[3]);
    raw->accel_z = (int16_t)((buffer[4] << 8) | buffer[5]);
    raw->temp     = (int16_t)((buffer[6] << 8) | buffer[7]);
    raw->gyro_x  = (int16_t)((buffer[8] << 8) | buffer[9]);
    raw->gyro_y  = (int16_t)((buffer[10] << 8) | buffer[11]);
    raw->gyro_z  = (int16_t)((buffer[12] << 8) | buffer[13]);

    return true;
}

/**
 * @brief  读取处理后的数据
 */
bool MPU6050_ReadData(MPU6050_Config* config, MPU6050_Data* data) {
    MPU6050_RawData raw;

    // 读取原始数据
    if (!MPU6050_ReadRaw(config, &raw)) {
        return false;
    }

    // 转换加速度数据(单位:g)
    data->accel_x = (float)raw.accel_x / config->accel_scale;
    data->accel_y = (float)raw.accel_y / config->accel_scale;
    data->accel_z = (float)raw.accel_z / config->accel_scale;

    // 转换温度数据(单位:°C)
    data->temperature = (float)raw.temp / 340.0f + 36.53f;

    // 转换陀螺仪数据(单位:°/s)
    data->gyro_x = (float)raw.gyro_x / config->gyro_scale;
    data->gyro_y = (float)raw.gyro_y / config->gyro_scale;
    data->gyro_z = (float)raw.gyro_z / config->gyro_scale;

    return true;
}

/**
 * @brief  计算姿态角(简单方法)
 */
void MPU6050_CalculateAttitude(MPU6050_Data* data, MPU6050_Attitude* attitude) {
    // 使用加速度计计算横滚角和俯仰角
    // Roll (绕X轴旋转)
    attitude->roll = atan2f(data->accel_y, data->accel_z) * 180.0f / M_PI;

    // Pitch (绕Y轴旋转)
    attitude->pitch = atan2f(-data->accel_x, 
                            sqrtf(data->accel_y * data->accel_y + 
                                  data->accel_z * data->accel_z)) * 180.0f / M_PI;

    // Yaw (绕Z轴旋转) - 需要磁力计或陀螺仪积分
    // 这里暂时设为0,实际应用需要数据融合
    attitude->yaw = 0.0f;
}

/**
 * @brief  校准传感器(静态偏移校准)
 */
bool MPU6050_Calibrate(MPU6050_Config* config) {
    #define CALIBRATION_SAMPLES 100

    int32_t accel_x_sum = 0, accel_y_sum = 0, accel_z_sum = 0;
    int32_t gyro_x_sum = 0, gyro_y_sum = 0, gyro_z_sum = 0;

    MPU6050_RawData raw;

    // 采集多组数据
    for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
        if (!MPU6050_ReadRaw(config, &raw)) {
            return false;
        }

        accel_x_sum += raw.accel_x;
        accel_y_sum += raw.accel_y;
        accel_z_sum += raw.accel_z;
        gyro_x_sum += raw.gyro_x;
        gyro_y_sum += raw.gyro_y;
        gyro_z_sum += raw.gyro_z;

        HAL_Delay(10);
    }

    // 计算平均值(偏移量)
    // 注意:实际应用中需要将这些偏移量存储并在读取时减去
    int16_t accel_x_offset = accel_x_sum / CALIBRATION_SAMPLES;
    int16_t accel_y_offset = accel_y_sum / CALIBRATION_SAMPLES;
    int16_t accel_z_offset = accel_z_sum / CALIBRATION_SAMPLES;
    int16_t gyro_x_offset = gyro_x_sum / CALIBRATION_SAMPLES;
    int16_t gyro_y_offset = gyro_y_sum / CALIBRATION_SAMPLES;
    int16_t gyro_z_offset = gyro_z_sum / CALIBRATION_SAMPLES;

    // 打印校准结果
    printf("校准完成:\n");
    printf("  加速度偏移: X=%d, Y=%d, Z=%d\n", 
           accel_x_offset, accel_y_offset, accel_z_offset);
    printf("  陀螺仪偏移: X=%d, Y=%d, Z=%d\n",
           gyro_x_offset, gyro_y_offset, gyro_z_offset);

    return true;
}

第四部分:数据解析与应用

使用示例

/**
 * @file    main.c
 * @brief   MPU6050使用示例
 */

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

// I2C句柄(需要在CubeMX中配置)
extern I2C_HandleTypeDef hi2c1;

// MPU6050配置
MPU6050_Config mpu_config = {
    .hi2c = &hi2c1,
    .address = MPU6050_ADDR,
    .gyro_fs = MPU6050_GYRO_FS_250,      // ±250°/s
    .accel_fs = MPU6050_ACCEL_FS_2       // ±2g
};

/**
 * @brief  主函数
 */
int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 初始化I2C
    MX_I2C1_Init();

    // 初始化UART(用于打印)
    MX_USART1_UART_Init();

    printf("MPU6050六轴传感器测试\n");
    printf("═══════════════════════════════════════\n\n");

    // 初始化MPU6050
    if (!MPU6050_Init(&mpu_config)) {
        printf("❌ MPU6050初始化失败!\n");
        printf("请检查:\n");
        printf("  1. I2C连接是否正确\n");
        printf("  2. 上拉电阻是否存在\n");
        printf("  3. 传感器地址是否正确\n");
        while (1);
    }

    printf("✓ MPU6050初始化成功\n\n");

    // 校准传感器
    printf("开始校准(请保持传感器静止)...\n");
    if (MPU6050_Calibrate(&mpu_config)) {
        printf("✓ 校准完成\n\n");
    }

    MPU6050_Data data;
    MPU6050_Attitude attitude;
    uint32_t count = 0;

    while (1) {
        // 读取传感器数据
        if (MPU6050_ReadData(&mpu_config, &data)) {
            count++;

            // 计算姿态角
            MPU6050_CalculateAttitude(&data, &attitude);

            // 打印数据
            printf("读取 #%lu:\n", count);
            printf("  加速度: X=%.3fg, Y=%.3fg, Z=%.3fg\n",
                   data.accel_x, data.accel_y, data.accel_z);
            printf("  陀螺仪: X=%.2f°/s, Y=%.2f°/s, Z=%.2f°/s\n",
                   data.gyro_x, data.gyro_y, data.gyro_z);
            printf("  温度: %.2f°C\n", data.temperature);
            printf("  姿态角: Roll=%.2f°, Pitch=%.2f°\n\n",
                   attitude.roll, attitude.pitch);
        } else {
            printf("读取失败 #%lu\n\n", count);
        }

        HAL_Delay(100);  // 100ms更新一次
    }
}

应用场景示例

场景1:水平检测

/**
 * @brief  水平检测应用
 */
void application_level_detection(void) {
    MPU6050_Data data;
    MPU6050_Attitude attitude;

    const float LEVEL_THRESHOLD = 2.0f;  // 水平阈值(度)

    printf("水平检测应用\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            MPU6050_CalculateAttitude(&data, &attitude);

            // 检查是否水平
            bool is_level = (fabsf(attitude.roll) < LEVEL_THRESHOLD) &&
                           (fabsf(attitude.pitch) < LEVEL_THRESHOLD);

            if (is_level) {
                printf("✓ 设备已水平 (Roll=%.2f°, Pitch=%.2f°)\n",
                       attitude.roll, attitude.pitch);
                // 点亮绿色LED
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
            } else {
                printf("✗ 设备倾斜 (Roll=%.2f°, Pitch=%.2f°)\n",
                       attitude.roll, attitude.pitch);
                // 点亮红色LED
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
            }
        }

        HAL_Delay(100);
    }
}

场景2:跌倒检测

/**
 * @brief  跌倒检测应用
 */
void application_fall_detection(void) {
    MPU6050_Data data;

    const float FALL_THRESHOLD = 2.5f;   // 跌倒阈值(g)
    const float IMPACT_THRESHOLD = 3.0f; // 冲击阈值(g)

    printf("跌倒检测应用\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            // 计算加速度幅值
            float accel_magnitude = sqrtf(data.accel_x * data.accel_x +
                                         data.accel_y * data.accel_y +
                                         data.accel_z * data.accel_z);

            // 检测自由落体(加速度接近0)
            if (accel_magnitude < 0.5f) {
                printf("⚠ 检测到自由落体!\n");

                // 等待冲击
                HAL_Delay(100);

                if (MPU6050_ReadData(&mpu_config, &data)) {
                    accel_magnitude = sqrtf(data.accel_x * data.accel_x +
                                           data.accel_y * data.accel_y +
                                           data.accel_z * data.accel_z);

                    // 检测冲击
                    if (accel_magnitude > IMPACT_THRESHOLD) {
                        printf("🚨 跌倒报警!加速度=%.2fg\n", accel_magnitude);
                        // 触发报警
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
                        HAL_Delay(5000);  // 报警5秒
                        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
                    }
                }
            }

            printf("加速度幅值: %.2fg\n", accel_magnitude);
        }

        HAL_Delay(50);  // 50ms检测一次
    }
}

场景3:手势识别

/**
 * @brief  手势识别应用
 */
void application_gesture_recognition(void) {
    MPU6050_Data data;

    const float SHAKE_THRESHOLD = 2.0f;  // 摇晃阈值(g)

    printf("手势识别应用\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            // 检测X轴摇晃
            if (fabsf(data.accel_x) > SHAKE_THRESHOLD) {
                printf("👈 检测到左右摇晃\n");
            }

            // 检测Y轴摇晃
            if (fabsf(data.accel_y) > SHAKE_THRESHOLD) {
                printf("👆 检测到前后摇晃\n");
            }

            // 检测Z轴摇晃
            if (fabsf(data.accel_z - 1.0f) > SHAKE_THRESHOLD) {
                printf("🔼 检测到上下摇晃\n");
            }

            // 检测旋转(陀螺仪)
            if (fabsf(data.gyro_z) > 100.0f) {
                if (data.gyro_z > 0) {
                    printf("↻ 检测到顺时针旋转\n");
                } else {
                    printf("↺ 检测到逆时针旋转\n");
                }
            }
        }

        HAL_Delay(100);
    }
}

场景4:步数统计

/**
 * @brief  步数统计应用
 */
void application_step_counter(void) {
    MPU6050_Data data;

    const float STEP_THRESHOLD = 1.2f;   // 步伐阈值(g)
    const uint32_t STEP_INTERVAL = 300;  // 最小步伐间隔(ms)

    uint32_t step_count = 0;
    uint32_t last_step_time = 0;
    bool step_detected = false;

    printf("步数统计应用\n");
    printf("═══════════════════════════════════════\n\n");

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            // 计算垂直加速度幅值
            float vertical_accel = fabsf(data.accel_z - 1.0f);

            uint32_t current_time = HAL_GetTick();

            // 检测步伐
            if (vertical_accel > STEP_THRESHOLD && !step_detected) {
                // 检查时间间隔
                if (current_time - last_step_time > STEP_INTERVAL) {
                    step_count++;
                    last_step_time = current_time;
                    step_detected = true;

                    printf("👣 步数: %lu\n", step_count);
                }
            }

            // 重置检测标志
            if (vertical_accel < STEP_THRESHOLD) {
                step_detected = false;
            }
        }

        HAL_Delay(50);  // 50ms检测一次
    }
}

第五部分:高级应用

互补滤波算法

/**
 * @brief  互补滤波器结构
 */
typedef struct {
    float alpha;            // 滤波系数(0-1)
    float roll;             // 横滚角
    float pitch;            // 俯仰角
    float yaw;              // 偏航角
    uint32_t last_time;     // 上次更新时间
} ComplementaryFilter;

/**
 * @brief  初始化互补滤波器
 */
void ComplementaryFilter_Init(ComplementaryFilter* filter, float alpha) {
    filter->alpha = alpha;
    filter->roll = 0.0f;
    filter->pitch = 0.0f;
    filter->yaw = 0.0f;
    filter->last_time = HAL_GetTick();
}

/**
 * @brief  更新互补滤波器
 */
void ComplementaryFilter_Update(ComplementaryFilter* filter, MPU6050_Data* data) {
    // 计算时间间隔(秒)
    uint32_t current_time = HAL_GetTick();
    float dt = (current_time - filter->last_time) / 1000.0f;
    filter->last_time = current_time;

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

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

    // 互补滤波:融合加速度计和陀螺仪数据
    // 角度 = α × (角度 + 陀螺仪积分) + (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度
    if (filter->yaw > 180.0f) filter->yaw -= 360.0f;
    if (filter->yaw < -180.0f) filter->yaw += 360.0f;
}

/**
 * @brief  互补滤波示例
 */
void example_complementary_filter(void) {
    ComplementaryFilter filter;
    MPU6050_Data data;

    // 初始化滤波器(α=0.98,更信任陀螺仪)
    ComplementaryFilter_Init(&filter, 0.98f);

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

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            // 更新滤波器
            ComplementaryFilter_Update(&filter, &data);

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

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

数据可视化

/**
 * @brief  数据可视化(通过串口发送到上位机)
 */
void example_data_visualization(void) {
    MPU6050_Data data;
    ComplementaryFilter filter;

    ComplementaryFilter_Init(&filter, 0.98f);

    printf("数据可视化模式\n");
    printf("═══════════════════════════════════════\n");
    printf("格式: AX,AY,AZ,GX,GY,GZ,Roll,Pitch,Yaw\n\n");

    while (1) {
        if (MPU6050_ReadData(&mpu_config, &data)) {
            ComplementaryFilter_Update(&filter, &data);

            // 发送CSV格式数据(便于上位机解析)
            printf("%.3f,%.3f,%.3f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n",
                   data.accel_x, data.accel_y, data.accel_z,
                   data.gyro_x, data.gyro_y, data.gyro_z,
                   filter.roll, filter.pitch, filter.yaw);
        }

        HAL_Delay(20);  // 50Hz更新率
    }
}

总结

关键要点

  1. MEMS原理
  2. 加速度计测量线性加速度
  3. 陀螺仪测量角速度
  4. 两者互补,需要数据融合

  5. MPU6050特性

  6. 六轴传感器(3轴加速度+3轴陀螺仪)
  7. I2C接口,易于使用
  8. 可配置量程和采样率
  9. 内置DMP和FIFO

  10. 数据处理

  11. 原始数据需要比例转换
  12. 加速度计可计算倾角
  13. 陀螺仪需要积分得到角度
  14. 互补滤波融合两者优势

  15. 应用场景

  16. 姿态检测和稳定控制
  17. 运动追踪和手势识别
  18. 跌倒检测和步数统计
  19. 导航定位和惯性测量

常见问题

Q1:为什么加速度计读数不准确? - 振动干扰:使用低通滤波 - 未校准:进行零点校准 - 量程不合适:调整量程设置

Q2:陀螺仪为什么会漂移? - 温度影响:进行温度补偿 - 零点偏移:定期校准 - 积分累积误差:使用互补滤波

Q3:如何提高姿态角精度? - 使用互补滤波或卡尔曼滤波 - 增加磁力计(九轴传感器) - 提高采样率 - 进行传感器校准

Q4:MPU6050通信失败怎么办? - 检查I2C连接和上拉电阻 - 确认I2C地址正确 - 检查电源电压 - 降低I2C速度

进阶学习

  1. 卡尔曼滤波:更精确的数据融合算法
  2. DMP使用:利用MPU6050内置处理器
  3. 九轴传感器:增加磁力计进行航向角校正
  4. 四元数:避免万向节死锁的姿态表示方法

参考资料

  • MPU6050数据手册
  • InvenSense应用笔记
  • 《惯性导航原理》
  • 开源项目:MultiWii、Betaflight

下一步学习: - 传感器数据融合技术 - 光照与气压传感器应用 - 卡尔曼滤波算法实现