MPU6050 卡尔曼滤波算法 四元数欧拉姿态解算 STM32 CubeMX HAL库 MDKkeil5 零基础移植

1.在 cubemx 开启 IIC 并设置好对应的 IIC 引脚

2.generate code 生成代码

(记得生成单个 c. h.文件)!!!!!!

3.复制以下的全部代码 新建分别保存放到 Inc Src 文件夹中

每个字都是有用的!!!!!!

请全部复制!!!!!!!!!

每个字都是有用的!!!!!!

请全部复制!!!!!!!!

MPU6050.h

这是.h 文件中存放的内容

定义了结构体用于存放粗数据和处理后的数据 

使用时我们只需要关心 MPU6050_t 这个结构体类型

/*
 * mpu6050.h
 *
 *  Created on: Nov 13, 2019
 *      Author: Bulanov Konstantin
 */

#ifndef INC_GY521_H_
#define INC_GY521_H_

#endif /* INC_GY521_H_ */

#include <stdint.h>
#include "i2c.h"

// MPU6050 structure
typedef struct
{

    int16_t Accel_X_RAW;
    int16_t Accel_Y_RAW;
    int16_t Accel_Z_RAW;
    double Ax;
    double Ay;
    double Az;

    int16_t Gyro_X_RAW;
    int16_t Gyro_Y_RAW;
    int16_t Gyro_Z_RAW;
    double Gx;
    double Gy;
    double Gz;

    float Temperature;

    double KalmanAngleX;
    double KalmanAngleY;
} MPU6050_t;

// Kalman structure
typedef struct
{
    double Q_angle;
    double Q_bias;
    double R_measure;
    double angle;
    double bias;
    double P[2][2];
} Kalman_t;

uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx);

void MPU6050_Read_Accel(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_Gyro(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_Temp(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_All(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt);

MPU6050.c

这其中包含了数据处理的函数

每个字都是有用的!!!!!!

请全部复制!!!!!!!!

每个字都是有用的!!!!!

请全部复制!!!!!!!!

关于DEFINE 这关乎数据的读取 请全部复制!!!!!!

/*
 * mpu6050.c
 *
 *  Created on: Nov 13, 2019
 *      Author: Bulanov Konstantin
 *
 *  Contact information
 *  -------------------
 */

#include <math.h>
#include "mpu6050.h"

#define RAD_TO_DEG 57.295779513082320876798154814105

#define WHO_AM_I_REG 0x75
#define PWR_MGMT_1_REG 0x6B
#define SMPLRT_DIV_REG 0x19
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_CONFIG_REG 0x1B
#define GYRO_XOUT_H_REG 0x43

// Setup MPU6050
#define MPU6050_ADDR 0xD0
const uint16_t i2c_timeout = 100;
const double Accel_Z_corrector = 14418.0;

uint32_t timer;

Kalman_t KalmanX = {
    .Q_angle = 0.001f,
    .Q_bias = 0.003f,
    .R_measure = 0.03f};

Kalman_t KalmanY = {
    .Q_angle = 0.001f,
    .Q_bias = 0.003f,
    .R_measure = 0.03f,
};

uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx)
{
    uint8_t check;
    uint8_t Data;

    // check device ID WHO_AM_I

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, WHO_AM_I_REG, 1, &check, 1, i2c_timeout);

    if (check == 104) // 0x68 will be returned by the sensor if everything goes well
    {
        // power management register 0X6B we should write all 0's to wake the sensor up
        Data = 0;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, PWR_MGMT_1_REG, 1, &Data, 1, i2c_timeout);

        // Set DATA RATE of 1KHz by writing SMPLRT_DIV register
        Data = 0x07;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, SMPLRT_DIV_REG, 1, &Data, 1, i2c_timeout);

        // Set accelerometer configuration in ACCEL_CONFIG Register
        // XA_ST=0,YA_ST=0,ZA_ST=0, FS_SEL=0 -> ? 2g
        Data = 0x00;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, ACCEL_CONFIG_REG, 1, &Data, 1, i2c_timeout);

        // Set Gyroscopic configuration in GYRO_CONFIG Register
        // XG_ST=0,YG_ST=0,ZG_ST=0, FS_SEL=0 -> ? 250 ?/s
        Data = 0x00;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, GYRO_CONFIG_REG, 1, &Data, 1, i2c_timeout);
        return 0;
    }
    return 1;
}

void MPU6050_Read_Accel(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct)
{
    uint8_t Rec_Data[6];

    // Read 6 BYTES of data starting from ACCEL_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout);

    DataStruct->Accel_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Accel_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Accel_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]);

    /*** convert the RAW values into acceleration in 'g'
         we have to divide according to the Full scale value set in FS_SEL
         I have configured FS_SEL = 0. So I am dividing by 16384.0
         for more details check ACCEL_CONFIG Register              ****/

    DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0;
    DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0;
    DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector;
}

void MPU6050_Read_Gyro(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct)
{
    uint8_t Rec_Data[6];

    // Read 6 BYTES of data starting from GYRO_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, GYRO_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout);

    DataStruct->Gyro_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Gyro_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Gyro_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]);

    /*** convert the RAW values into dps (?/s)
         we have to divide according to the Full scale value set in FS_SEL
         I have configured FS_SEL = 0. So I am dividing by 131.0
         for more details check GYRO_CONFIG Register              ****/

    DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0;
    DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0;
    DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0;
}

void MPU6050_Read_Temp(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct)
{
    uint8_t Rec_Data[2];
    int16_t temp;

    // Read 2 BYTES of data starting from TEMP_OUT_H_REG register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, TEMP_OUT_H_REG, 1, Rec_Data, 2, i2c_timeout);

    temp = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Temperature = (float)((int16_t)temp / (float)340.0 + (float)36.53);
}

void MPU6050_Read_All(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct)
{
    uint8_t Rec_Data[14];
    int16_t temp;

    // Read 14 BYTES of data starting from ACCEL_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 14, i2c_timeout);

    DataStruct->Accel_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Accel_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Accel_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]);
    temp = (int16_t)(Rec_Data[6] << 8 | Rec_Data[7]);
    DataStruct->Gyro_X_RAW = (int16_t)(Rec_Data[8] << 8 | Rec_Data[9]);
    DataStruct->Gyro_Y_RAW = (int16_t)(Rec_Data[10] << 8 | Rec_Data[11]);
    DataStruct->Gyro_Z_RAW = (int16_t)(Rec_Data[12] << 8 | Rec_Data[13]);

    DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0;
    DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0;
    DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector;
    DataStruct->Temperature = (float)((int16_t)temp / (float)340.0 + (float)36.53);
    DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0;
    DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0;
    DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0;

    // Kalman angle solve
    double dt = (double)(HAL_GetTick() - timer) / 1000;
    timer = HAL_GetTick();
    double roll;
    double roll_sqrt = sqrt(
        DataStruct->Accel_X_RAW * DataStruct->Accel_X_RAW + DataStruct->Accel_Z_RAW * DataStruct->Accel_Z_RAW);
    if (roll_sqrt != 0.0)
    {
        roll = atan(DataStruct->Accel_Y_RAW / roll_sqrt) * RAD_TO_DEG;
    }
    else
    {
        roll = 0.0;
    }
    double pitch = atan2(-DataStruct->Accel_X_RAW, DataStruct->Accel_Z_RAW) * RAD_TO_DEG;
    if ((pitch < -90 && DataStruct->KalmanAngleY > 90) || (pitch > 90 && DataStruct->KalmanAngleY < -90))
    {
        KalmanY.angle = pitch;
        DataStruct->KalmanAngleY = pitch;
    }
    else
    {
        DataStruct->KalmanAngleY = Kalman_getAngle(&KalmanY, pitch, DataStruct->Gy, dt);
    }
    if (fabs(DataStruct->KalmanAngleY) > 90)
        DataStruct->Gx = -DataStruct->Gx;
    DataStruct->KalmanAngleX = Kalman_getAngle(&KalmanX, roll, DataStruct->Gx, dt);
}

double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt)
{
    double rate = newRate - Kalman->bias;
    Kalman->angle += dt * rate;

    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;

    double S = Kalman->P[0][0] + Kalman->R_measure;
    double K[2];
    K[0] = Kalman->P[0][0] / S;
    K[1] = Kalman->P[1][0] / S;

    double y = newAngle - Kalman->angle;
    Kalman->angle += K[0] * y;
    Kalman->bias += K[1] * y;

    double P00_temp = Kalman->P[0][0];
    double 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;
};

 5. 移植完毕代码之后

重要的是怎么去使用

(在使用之前请确保 编译时 上述文件是包含在程序中的!!!!)

我们需要创建一个全局变量 用于存放数据

这一步非常重要!!!!!!

请不要忘记创建变量!!!!

请不要忘记创建变量!!!!

请不要忘记创建变量!!!!

请按下面步骤操作

这一步也是至关重要!!!!

请放在进入while(1)死循环之前 

请放在进入while(1)死循环之前 

请放在进入while(1)死循环之前 

这一步是用于确保初始化的正确

这之后就是while(1)死循环里的操作了 

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: MPU6050是一种六轴陀螺仪,同时具有三轴加速度计和三轴陀螺仪,用于测量物体的姿态。但是,由于测量误差和噪声的存在,仅使用原始数据进行姿态解算会产生不稳定和误差较大的结果。 为了解决这个问题,可以使用卡尔曼滤波算法MPU6050采集的数据进行滤波和优化。卡尔曼滤波算法通过对多个方面的信息进行综合分析,以最小化系统误差。在姿态解算中,卡尔曼滤波算法将结合MPU6050传感器测量值、机体模型和先验估计值等信息,对姿态进行更新和优化,提高解算精度和稳定性。 具体来说,卡尔曼滤波姿态解算的过程是这样的:首先,根据MPU6050测量到的角速度和加速度,使用欧拉角公式计算当前姿态的初值。然后,将初值作为先验估计值输入到卡尔曼滤波模型中,和机体模型以及测量噪声进行卡尔曼滤波,得到最终的姿态解算结果。 总之,卡尔曼滤波算法可以提高MPU6050姿态解算的精度和稳定性,是解决传感器误差和噪声问题的重要方法。 ### 回答2: MPU6050是一种带有3轴陀螺仪和3轴加速度计的惯性测量单元(IMU),可以用于姿态解算姿态解算是确定物体相对于惯性参考系的方向的过程。卡尔曼滤波姿态解算中最常用的算法之一,能够从传感器数据中提取最精确的姿态信息。 卡尔曼滤波的基本思想是通过将先验估计与测量数据加权平均来提高估计精度。在姿态解算中,卡尔曼滤波需要模型来描述系统的状态,该模型由IMU的运动学方程以及测量方差构成。IMU的运动学方程可以描述IMU的加速度、角速度和姿态变化之间的关系。测量方差可由IMU内部噪声和传感器误差估计得到。 卡尔曼滤波的主要步骤包括预测和更新。在预测步骤中,利用IMU的运动学方程计算出先验估计的姿态信息。在更新步骤中,将测量数据与先验估计之间的误差通过卡尔曼增益加权,计算出最终姿态信息。随着时间的推移,先验估计会逐渐趋向于真实姿态,同时卡尔曼滤波也会对传感器的误差进行动态校正,从而提高姿态解算的精度。 在实际应用中,卡尔曼滤波可以与其他算法相结合,如互补滤波或自适应滤波,以进一步提高精度和鲁棒性。同时,需要注意的是IMU的校准和姿态初始化也对姿态解算的精度有着重要影响。因此,对于姿态解算的实现,还需要考虑IMU的选择、校准和环境因素等多个方面。 ### 回答3: mpu6050是一种常用的惯性测量单元(Inertial Measurement Unit, IMU),它可以通过测量三轴加速度计和三轴陀螺仪的数据来计算设备的姿态。当直接使用这些传感器数据时,由于传感器存在一定的噪声和误差,会导致姿态计算的不稳定性和不准确性。为了解决这个问题,可以采用卡尔曼滤波算法进行姿态解算卡尔曼滤波是一种基于贝叶斯概率理论的滤波算法,通过预测和更新两个步骤,将传感器数据进行滤波和处理,得到更加准确和稳定的姿态信息。 在使用mpu6050进行姿态解算时,需要按照以下步骤进行: 1.读取传感器数据 使用mpu6050读取三轴加速度计和三轴陀螺仪的测量数据,并对数据进行归一化和校准,以保证更加准确和稳定的数据。 2.预测 根据传感器数据进行预测,利用数学模型计算物体在下一个时间步的状态和误差协方差矩阵。 3.更新 将预测值和传感器测量数据进行比较,根据误差协方差矩阵计算卡尔曼增益,更新估计值和误差协方差矩阵。 4.姿态解算 根据更新后的姿态数据,计算设备的三个欧拉角(俯仰角、偏航角、横滚角),从而得到设备的姿态信息。 卡尔曼滤波算法可以有效地处理传感器的噪声和误差,提高姿态解算的准确性和稳定性,适用于各种移动设备、机器人等需要姿态信息的场合。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值