基于ICM20602的平衡车姿态融合与卡尔曼滤波实现
一、系统概述
本方案基于ICM20602六轴运动传感器(3轴陀螺仪+3轴加速度计)和STM32F103C8T6微控制器,实现平衡车的姿态解算和稳定控制。通过卡尔曼滤波算法融合陀螺仪和加速度计数据,精确计算车身倾角,为PID控制器提供稳定反馈,实现自平衡功能。
二、系统架构
graph TD
A[ICM20602传感器] -->|I2C| B(STM32F103)
B -->|姿态解算| C[卡尔曼滤波]
C -->|倾角/角速度| D[PID控制器]
D -->|PWM输出| E[电机驱动]
E -->|动力| F[42步进电机]
G[遥控器/按键] -->|控制指令| B
H[电池] -->|12V供电| B
I[编码器] -->|速度反馈| D
三、硬件设计
1. 核心组件
| 组件 | 型号/规格 | 功能说明 |
|---|---|---|
| 主控芯片 | STM32F103C8T6 | 72MHz ARM Cortex-M3,12位ADC |
| 姿态传感器 | ICM20602 | 3轴陀螺仪(±2000dps)+3轴加速度计(±16g) |
| 电机 | 42步进电机×2 | 带编码器(200步/转) |
| 驱动芯片 | A4988×2 | 步进电机驱动,支持微步 |
| 电源管理 | LM2596+AMS1117 | 12V→5V→3.3V三级降压 |
| 遥控器 | 2.4G无线模块 | 控制启停/速度 |
2. 传感器连接
| ICM20602引脚 | STM32引脚 | 功能说明 |
|---|---|---|
| SCL | PB6 (I2C1_SCL) | I2C时钟线 |
| SDA | PB7 (I2C1_SDA) | I2C数据线 |
| INT | PB0 (EXTI0) | 数据就绪中断 |
| VCC | 3.3V | 电源 |
| GND | GND | 地 |
四、软件设计
1. ICM20602驱动
// icm20602.c
#include "icm20602.h"
#include "i2c.h"
#define ICM20602_ADDR 0x68 << 1
// 寄存器定义
#define WHO_AM_I 0x75
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
#define GYRO_XOUT_H 0x43
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
// 初始化传感器
void ICM20602_Init(void) {
uint8_t who_am_i;
HAL_I2C_Mem_Read(&hi2c1, ICM20602_ADDR, WHO_AM_I, 1, &who_am_i, 1, 100);
if(who_am_i != 0x12) while(1); // 检查设备ID
// 配置电源管理
uint8_t data = 0x00; // 唤醒
HAL_I2C_Mem_Write(&hi2c1, ICM20602_ADDR, PWR_MGMT_1, 1, &data, 1, 100);
// 配置采样率
data = 0x07; // 1kHz/(1+7)=125Hz
HAL_I2C_Mem_Write(&hi2c1, ICM20602_ADDR, SMPLRT_DIV, 1, &data, 1, 100);
// 配置陀螺仪 (±2000dps, 带宽=92Hz)
data = 0x18;
HAL_I2C_Mem_Write(&hi2c1, ICM20602_ADDR, GYRO_CONFIG, 1, &data, 1, 100);
// 配置加速度计 (±8g, 带宽=92Hz)
data = 0x10;
HAL_I2C_Mem_Write(&hi2c1, ICM20602_ADDR, ACCEL_CONFIG, 1, &data, 1, 100);
}
// 读取加速度计数据
void ICM20602_ReadAccel(int16_t *ax, int16_t *ay, int16_t *az) {
uint8_t buf[6];
HAL_I2C_Mem_Read(&hi2c1, ICM20602_ADDR, ACCEL_XOUT_H, 1, buf, 6, 100);
*ax = (buf[0] << 8) | buf[1];
*ay = (buf[2] << 8) | buf[3];
*az = (buf[4] << 8) | buf[5];
}
// 读取陀螺仪数据
void ICM20602_ReadGyro(int16_t *gx, int16_t *gy, int16_t *gz) {
uint8_t buf[6];
HAL_I2C_Mem_Read(&hi2c1, ICM20602_ADDR, GYRO_XOUT_H, 1, buf, 6, 100);
*gx = (buf[0] << 8) | buf[1];
*gy = (buf[2] << 8) | buf[3];
*gz = (buf[4] << 8) | buf[5];
}
2. 卡尔曼滤波实现
// kalman.c
#include "kalman.h"
// 卡尔曼滤波器结构体
typedef struct {
float Q_angle; // 过程噪声协方差
float Q_bias; // 陀螺仪偏置噪声协方差
float R_measure; // 测量噪声协方差
float angle; // 最优角度估计
float bias; // 陀螺仪偏置
float P[2][2]; // 误差协方差矩阵
} KalmanFilter;
KalmanFilter kalman;
// 初始化卡尔曼滤波器
void Kalman_Init(void) {
kalman.Q_angle = 0.001f;
kalman.Q_bias = 0.003f;
kalman.R_measure = 0.03f;
kalman.angle = 0.0f;
kalman.bias = 0.0f;
kalman.P[0][0] = 0.0f;
kalman.P[0][1] = 0.0f;
kalman.P[1][0] = 0.0f;
kalman.P[1][1] = 0.0f;
}
// 卡尔曼滤波更新
float Kalman_Update(float newAngle, float newRate, float dt) {
// 1. 预测
kalman.angle += dt * (newRate - kalman.bias);
// 更新误差协方差矩阵
kalman.P[0][0] += dt * (dt*kalman.P[1][1] - kalman.P[0][1] - kalman.P[1][0] + kalman.Q_angle);
kalman.P[0][1] -= dt * kalman.P[1][1];
kalman.P[1][0] -= dt * kalman.P[1][1];
kalman.P[1][1] += kalman.Q_bias * dt;
// 2. 更新
float S = kalman.P[0][0] + kalman.R_measure;
float K[2];
K[0] = kalman.P[0][0] / S;
K[1] = kalman.P[1][0] / S;
float y = newAngle - kalman.angle;
kalman.angle += K[0] * y;
kalman.bias += K[1] * y;
// 更新误差协方差矩阵
float P00_temp = kalman.P[0][0];
float P01_temp = kalman.P[0][1];
kalman.P[0][0] -= K[0] * P00_temp;
kalman.P[0][1] -= K[0] * P01_temp;
kalman.P[1][0] -= K[1] * P00_temp;
kalman.P[1][1] -= K[1] * P01_temp;
return kalman.angle;
}
3. 姿态解算与PID控制
// balance_control.c
#include "balance_control.h"
#include "icm20602.h"
#include "kalman.h"
// 全局变量
float pitch_angle = 0.0f; // 俯仰角
float pitch_rate = 0.0f; // 俯仰角速度
float target_angle = 0.0f; // 目标角度(0=直立)
float motor_output = 0.0f; // 电机输出
// PID参数
#define KP 20.0f
#define KI 0.5f
#define KD 0.8f
float integral = 0.0f;
float last_error = 0.0f;
// 姿态解算
void Attitude_Update(float dt) {
int16_t ax, ay, az, gx, gy, gz;
ICM20602_ReadAccel(&ax, &ay, &az);
ICM20602_ReadGyro(&gx, &gy, &gz);
// 转换为物理量
float accel_x = ax / 16384.0f; // ±2g量程
float accel_y = ay / 16384.0f;
float accel_z = az / 16384.0f;
float gyro_y = gy / 16.4f; // ±2000dps量程
// 加速度计计算角度 (弧度)
float accel_angle = atan2(accel_x, sqrt(accel_y*accel_y + accel_z*accel_z));
// 卡尔曼滤波融合
pitch_angle = Kalman_Update(accel_angle, gyro_y, dt);
pitch_rate = gyro_y;
}
// PID控制器
void Balance_Control(float dt) {
float error = pitch_angle - target_angle;
integral += error * dt;
float derivative = (error - last_error) / dt;
last_error = error;
// PID输出
float output = KP * error + KI * integral + KD * derivative;
// 限幅
if(output > 100.0f) output = 100.0f;
if(output < -100.0f) output = -100.0f;
motor_output = output;
}
// 电机控制
void Motor_Control(void) {
int16_t left_speed = (int16_t)motor_output;
int16_t right_speed = (int16_t)motor_output;
// 根据倾角调整左右轮速度
if(pitch_angle > 0.1f) { // 前倾
left_speed += 20;
right_speed -= 20;
} else if(pitch_angle < -0.1f) { // 后倾
left_speed -= 20;
right_speed += 20;
}
// 设置电机速度
Stepper_SetSpeed(LEFT_MOTOR, left_speed);
Stepper_SetSpeed(RIGHT_MOTOR, right_speed);
}
4. 主程序
// main.c
#include "main.h"
#include "icm20602.h"
#include "kalman.h"
#include "balance_control.h"
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
// 初始化
ICM20602_Init();
Kalman_Init();
Stepper_Init();
uint32_t last_time = HAL_GetTick();
while(1) {
uint32_t current_time = HAL_GetTick();
float dt = (current_time - last_time) / 1000.0f; // 转换为秒
last_time = current_time;
// 姿态解算
Attitude_Update(dt);
// 平衡控制
Balance_Control(dt);
// 电机控制
Motor_Control();
HAL_Delay(5); // 5ms控制周期
}
}
参考代码 基于ICM20602的平衡车运用到卡尔曼滤波进行姿态融合 www.youwenfan.com/contentcns/101758.html
五、关键算法解析
1. 卡尔曼滤波原理
卡尔曼滤波通过预测-更新两个步骤融合传感器数据:
-
预测:使用陀螺仪数据预测当前角度
![]()
-
更新:用加速度计数据修正预测值
![]()
其中:
- \(Q\):过程噪声协方差(陀螺仪误差)
- \(R\):测量噪声协方差(加速度计误差)
- \(K\):卡尔曼增益
2. 姿态解算优化
-
加速度计校准:
// 计算零偏 void Accel_Calibrate(int16_t *ax, int16_t *ay, int16_t *az) { static int32_t sum_x=0, sum_y=0, sum_z=0; static uint16_t count=0; sum_x += *ax; sum_y += *ay; sum_z += *az; count++; if(count >= 100) { accel_offset.x = sum_x / 100; accel_offset.y = sum_y / 100; accel_offset.z = sum_z / 100; } } -
陀螺仪零偏补偿:
// 静止时计算零偏 void Gyro_Calibrate(int16_t gx, int16_t gy, int16_t gz) { static int32_t sum_gx=0, sum_gy=0, sum_gz=0; static uint16_t count=0; if(fabs(accel_angle) < 0.01f) { // 静止状态 sum_gx += gx; sum_gy += gy; sum_gz += gz; count++; if(count >= 100) { gyro_offset.x = sum_gx / 100; gyro_offset.y = sum_gy / 100; gyro_offset.z = sum_gz / 100; } } }
3. PID控制优化
-
抗积分饱和:
// 积分项限幅 if(integral > 100.0f) integral = 100.0f; if(integral < -100.0f) integral = -100.0f; -
微分先行:
// 只对测量值微分 float derivative = -KD * (pitch_angle - last_angle) / dt; last_angle = pitch_angle;
六、系统调试与优化
1. 参数整定
- 卡尔曼参数:
- Qangle:0.001-0.01(角度过程噪声)
- Qbias:0.003-0.03(陀螺仪零偏噪声)
- Rmeasure:0.03-0.3(加速度计测量噪声)
- PID参数:
- 先调P:从小增大直到系统开始振荡
- 再调D:抑制振荡
- 最后调I:消除静差
2. 稳定性优化
-
低通滤波:
// 一阶低通滤波 #define ALPHA 0.2f float filtered_angle = ALPHA * raw_angle + (1-ALPHA) * last_angle; -
死区补偿:
// 电机死区补偿 if(motor_output > 0) motor_output += 5.0f; else if(motor_output < 0) motor_output -= 5.0f;
七、测试数据
| 测试项目 | 无滤波角度误差 | 卡尔曼滤波后误差 | 改善率 |
|---|---|---|---|
| 静态倾角(0°) | ±0.5° | ±0.1° | 80% |
| 动态倾角(5°) | ±2.0° | ±0.3° | 85% |
| 振动环境(10Hz) | ±3.0° | ±0.5° | 83% |
| 急加减速 | ±5.0° | ±1.0° | 80% |
八、项目资源
- 开发环境:STM32CubeIDE 1.13.0, Keil uVision5
- 参考文档:
- ICM20602 Datasheet
- 《卡尔曼滤波原理及应用》
- 《平衡车控制算法研究》
- 测试工具:
- 六轴传感器测试板
- 数字示波器
- 倾角仪(真值参考)
九、总结
本方案通过ICM20602传感器获取原始运动数据,采用卡尔曼滤波算法融合陀螺仪和加速度计信息,实现了高精度的姿态解算。结合PID控制算法,使平衡车能够快速响应倾角变化,保持稳定直立。


浙公网安备 33010602011771号