互补滤波算法计算加速度

发布时间:2026/7/24 22:38:46
互补滤波算法计算加速度 我们无法直接从MPU6050读取姿态角PITCH、ROLL、YAW因为MPU6050只提供原始的加速度和角速度数据。姿态角需要通过传感器融合算法如互补滤波、卡尔曼滤波等来计算。但是我们可以通过加速度计数据计算俯仰角PITCH和翻滚角ROLL而偏航角YAW由于没有磁力计无法直接计算而且陀螺仪积分会漂移。以下是如何通过加速度计数据计算俯仰角和翻滚角俯仰角PITCH通过加速度计的Y和Z轴数据计算pitch atan2(accel_y, accel_z) * 180.0 / PI;翻滚角ROLL通过加速度计的X和Z轴数据计算roll atan2(-accel_x, accel_z) * 180.0 / PI;注意这里假设传感器水平放置时Z轴指向地面X和Y轴水平。并且当传感器绕X轴旋转时得到翻滚角绕Y轴旋转时得到俯仰角。但是这种方法在动态情况下即存在线性加速度时会不准因为加速度计测量的是合加速度包括重力加速度和运动加速度。因此通常我们会结合陀螺仪的数据通过滤波算法来得到更稳定的姿态角。鉴于你的代码目前已经可以读取加速度和角速度我们可以添加一个函数来计算简单的俯仰角和翻滚角仅基于加速度计并且可以添加一个函数来通过互补滤波融合加速度计和陀螺仪的数据。以下是如何修改代码在MPU6050_t结构体中添加姿态角字段pitch, roll, yaw用于存储计算后的角度。添加一个函数通过加速度计数据计算俯仰角和翻滚角。添加一个函数通过互补滤波或卡尔曼滤波来融合加速度计和陀螺仪的数据得到更稳定的姿态角。由于你可能希望直接得到姿态角我将展示如何修改代码来实现简单的加速度计姿态计算和互补滤波。首先修改头文件中的MPU6050_t结构体添加姿态角字段ctypedef struct { AxesRaw_t accel; AxesRaw_t gyro; int16_t temp; AxesScaled_t accel_scaled; AxesScaled_t gyro_scaled; float temp_scaled; // 添加姿态角单位度 float pitch; // 俯仰角 float roll; // 翻滚角 float yaw; // 偏航角注意没有磁力计yaw会漂移 } MPU6050_t;然后添加两个函数仅通过加速度计计算姿态角pitch和roll通过互补滤波更新姿态角需要时间间隔dt我们可以在MPU6050.c中实现这两个函数。注意由于偏航角yaw无法通过加速度计计算且陀螺仪积分会漂移所以如果没有磁力计yaw角只能通过陀螺仪积分得到但会随时间漂移。以下是如何实现首先添加一个函数用于加速度计计算姿态角c// 通过加速度计数据计算俯仰角和翻滚角单位度 void MPU6050_CalculateAnglesFromAccel(MPU6050_t *data) { // 注意这里使用accel_scaled单位是g float ax data-accel_scaled.x; float ay data-accel_scaled.y; float az data-accel_scaled.z; // 计算俯仰角pitch和翻滚角roll data-pitch atan2(ay, az) * 180.0 / M_PI; data-roll atan2(-ax, az) * 180.0 / M_PI; }然后添加一个函数用于互补滤波更新姿态角包括yaw但yaw仅由陀螺仪积分c// 通过互补滤波更新姿态角 // dt: 两次调用的时间间隔单位秒 // alpha: 互补滤波系数通常取0.98左右表示陀螺仪的权重 void MPU6050_ComplementaryFilter(MPU6050_t *data, float dt, float alpha) { // 首先通过加速度计计算当前姿态角 float accel_pitch, accel_roll; float ax data-accel_scaled.x; float ay data-accel_scaled.y; float az data-accel_scaled.z; accel_pitch atan2(ay, az) * 180.0 / M_PI; accel_roll atan2(-ax, az) * 180.0 / M_PI; // 对于yaw我们无法用加速度计计算所以只能使用陀螺仪积分会漂移 // 注意这里我们假设陀螺仪的数据是角速度度/秒所以积分需要乘以dt data-yaw data-gyro_scaled.z * dt; // 互补滤波将加速度计计算的角度与陀螺仪积分的角度进行融合 data-pitch alpha * (data-pitch data-gyro_scaled.y * dt) (1 - alpha) * accel_pitch; data-roll alpha * (data-roll data-gyro_scaled.x * dt) (1 - alpha) * accel_roll; }注意互补滤波函数需要知道两次调用的时间间隔dt并且需要维护pitch和roll的状态即需要多次调用每次更新角度。你可以在主循环中定期调用MPU6050_ReadRawData、MPU6050_ScaleData然后调用MPU6050_ComplementaryFilter来更新姿态角。另外你可能需要初始化MPU6050_t结构体中的pitch、roll和yaw为0。最后你可以修改MPU6050_PrintData函数打印出姿态角。例如在MPU6050_PrintData中添加csprintf(buffer, Pitch: %.2f°, Roll: %.2f°, Yaw: %.2f°\r\n, data-pitch, data-roll, data-yaw); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100);请注意这种简单的互补滤波方法可能不够精确但对于一般应用已经足够。如果需要更精确的姿态估计可以考虑使用卡尔曼滤波或DMP数字运动处理器如果MPU6050支持的话。另外注意陀螺仪的零点漂移问题最好在启动时进行校准。由于你的代码中已经包含了读取和缩放数据的功能现在你可以通过上述修改来得到姿态角。总结修改步骤在MPU6050_t结构体中添加pitch, roll, yaw字段。实现MPU6050_CalculateAnglesFromAccel函数可选如果你只想用加速度计计算姿态角。实现MPU6050_ComplementaryFilter函数推荐用于融合加速度计和陀螺仪数据。在打印函数中添加姿态角的打印。注意使用数学函数atan2需要包含math.h头文件。如果使用互补滤波你需要在主循环中记录时间间隔dt例如使用HAL_GetTick()获取时间戳。示例主循环代码cMPU6050_t mpu_data; uint32_t last_time 0; uint32_t current_time; float dt; // 初始化mpu_data的姿态角为0 mpu_data.pitch 0; mpu_data.roll 0; mpu_data.yaw 0; while (1) { current_time HAL_GetTick(); dt (current_time - last_time) / 1000.0; // 转换为秒 last_time current_time; MPU6050_ReadRawData(hi2c1, mpu_data); MPU6050_ScaleData(mpu_data, GYRO_RANGE_250DPS, ACCEL_RANGE_2G); MPU6050_ComplementaryFilter(mpu_data, dt, 0.98); MPU6050_PrintData(mpu_data, huart1); HAL_Delay(10); // 适当延迟控制循环频率 }注意时间间隔dt的计算方法这里假设主循环周期固定但实际上可能会有变化。更好的方法是使用定时器来固定采样时间。以上代码提供了一个简单的姿态角计算和融合方法你可以根据实际需求进行调整。通过MPU6050的原始数据加速度和角速度可以计算出姿态角但需要一些数学计算。以下是修改方案1. 修改头文件在mpu6050.h中添加姿态角相关定义c#ifndef __MPU6050_H__ #define __MPU6050_H__ #include head.h #include math.h // ... 原有定义保持不变 ... // 姿态角数据结构 typedef struct { float pitch; // 俯仰角 float roll; // 翻滚角 float yaw; // 偏航角仅使用陀螺仪积分会漂移 } Attitude_t; // 更新MPU6050_t结构体 typedef struct { AxesRaw_t accel; AxesRaw_t gyro; int16_t temp; AxesScaled_t accel_scaled; AxesScaled_t gyro_scaled; float temp_scaled; Attitude_t attitude; // 新增姿态角 } MPU6050_t; // 新增函数声明 void MPU6050_CalculateAttitude(MPU6050_t *data, float dt); void MPU6050_ComplementaryFilter(MPU6050_t *data, float dt, float alpha); void MPU6050_PrintAttitude(MPU6050_t *data, UART_HandleTypeDef *huart); #endif /* __MPU6050_H__ */2. 修改源文件在mpu6050.c中添加姿态角计算函数c// ... 原有代码保持不变 ... // 计算姿态角基于加速度计 void MPU6050_CalculateAttitude(MPU6050_t *data, float dt) { // 使用加速度计计算俯仰角和翻滚角 // 注意这种方法在动态情况下会有误差 float ax data-accel_scaled.x; float ay data-accel_scaled.y; float az data-accel_scaled.z; // 计算俯仰角 (pitch) - 绕Y轴旋转 data-attitude.pitch atan2(-ax, sqrt(ay * ay az * az)) * 180.0 / M_PI; // 计算翻滚角 (roll) - 绕X轴旋转 data-attitude.roll atan2(ay, az) * 180.0 / M_PI; // Yaw角无法仅从加速度计获得需要使用陀螺仪积分或磁力计 // 这里仅用陀螺仪积分会有漂移 data-attitude.yaw data-gyro_scaled.z * dt; } // 互补滤波器融合加速度计和陀螺仪数据推荐使用 void MPU6050_ComplementaryFilter(MPU6050_t *data, float dt, float alpha) { static float pitch 0, roll 0, yaw 0; // 从加速度计计算的角度低频分量稳定但响应慢 float accel_pitch atan2(-data-accel_scaled.x, sqrt(data-accel_scaled.y * data-accel_scaled.y data-accel_scaled.z * data-accel_scaled.z)) * 180.0 / M_PI; float accel_roll atan2(data-accel_scaled.y, data-accel_scaled.z) * 180.0 / M_PI; // 从陀螺仪积分得到角度高频分量响应快但会漂移 float gyro_pitch pitch data-gyro_scaled.y * dt; float gyro_roll roll data-gyro_scaled.x * dt; yaw data-gyro_scaled.z * dt; // 互补滤波融合 pitch alpha * gyro_pitch (1 - alpha) * accel_pitch; roll alpha * gyro_roll (1 - alpha) * accel_roll; data-attitude.pitch pitch; data-attitude.roll roll; data-attitude.yaw yaw; } // 打印姿态角 void MPU6050_PrintAttitude(MPU6050_t *data, UART_HandleTypeDef *huart) { char buffer[100]; sprintf(buffer, Attitude: Pitch%.2f°, Roll%.2f°, Yaw%.2f°\r\n, data-attitude.pitch, data-attitude.roll, data-attitude.yaw); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100); } // 更新原有的打印函数 void MPU6050_PrintData(MPU6050_t *data, UART_HandleTypeDef *huart) { char buffer[200]; sprintf(buffer, Accel: X%.2fg, Y%.2fg, Z%.2fg\r\n, data-accel_scaled.x, data-accel_scaled.y, data-accel_scaled.z); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100); sprintf(buffer, Gyro: X%.2f°/s, Y%.2f°/s, Z%.2f°/s\r\n, data-gyro_scaled.x, data-gyro_scaled.y, data-gyro_scaled.z); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100); sprintf(buffer, Temperature: %.2f°C\r\n, data-temp_scaled); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100); // 添加姿态角打印 MPU6050_PrintAttitude(data, huart); sprintf(buffer, \r\n); HAL_UART_Transmit(huart, (uint8_t*)buffer, strlen(buffer), 100); }3. 使用示例在主函数中这样使用c// 全局变量 MPU6050_t mpu_data; uint32_t last_time 0; // 在主循环中 while (1) { uint32_t current_time HAL_GetTick(); float dt (current_time - last_time) / 1000.0f; // 转换为秒 last_time current_time; // 读取MPU6050数据 if (MPU6050_ReadRawData(hi2c1, mpu_data) 0) { MPU6050_ScaleData(mpu_data, GYRO_RANGE_250DPS, ACCEL_RANGE_2G); // 方法1简单计算适用于静态或缓慢运动 // MPU6050_CalculateAttitude(mpu_data, dt); // 方法2互补滤波推荐抗干扰更好 MPU6050_ComplementaryFilter(mpu_data, dt, 0.98f); // 打印数据 MPU6050_PrintData(mpu_data, huart1); } HAL_Delay(10); // 10ms采样周期 }注意事项Yaw角漂移没有磁力计的情况下yaw角会随时间漂移动态误差在剧烈运动时加速度计计算的角度会有误差滤波器参数互补滤波的alpha值需要根据应用调整通常0.95-0.98采样时间需要准确测量时间间隔dt初始化上电后需要几秒钟让滤波器稳定这样修改后你就可以直接获取MPU6050的PITCH、ROLL和YAW角度了。积分累计误差积分累积误差和数值溢出。