MPU6050篇——姿态解算,卡尔曼滤波

一、DMP文件的修改:

        首先我们打开inv_mpu.c文件夹,如下图所示便是第一个要修改的地方:

我们将其修改为:define定义可以改为自己使用的型号的单片机。

修改后在上面定义这个宏,并加上一个MPU6050的宏,用于源文件区别芯片:

然后我们打开inv_mpu_dmp_motion_driver.c,找到如下地方,和上面一样:   

修改为:(记得在上面加入#define  STM32F10x_MPU6050)

此时还有一些地方需要完善,但大致已经改完了,修缮一下:编译,报3处错,如下:

 这里报错:(inv_mpu.h文件中)

修改为:

 在inv_mpu_dmp_motion_driver.c中:

此时编译还有一个错误:

可以发现是inv_mpu中的问题,我们找到这给注释掉

此时再编译,无错误,一个警告,如下图:

我们点击过去给函数注释掉:(注意一定要注释全,是一整个函数,比较长)

 此时再编译:无错误。

 二、官方源码的移植

 此时,我们的DMP代码已经改完了,下面还要移植官方例子的初始化等代码:下面来转植代码,打开官方库如下文件:

下拉,在main函数复制下面方框的结构体及初始化函数,并自己创建一个函数封装起来并判断返回值:这些函数正确执行就会返回0,否则返回非0数。 

到这步你的代码应该是这样:

这几个函数是设置传感器、设置fifo、设置采样率的,复制到自己的代码:

其中DEFAULT_MPU_HZ为官方定义的,ctrl+f搜索一下复制到自己函数上面,然后在相应函数判断返回值,此时代码应该如下:

下面复制DMP初始化步骤代码并粘贴到自己的函数中并判断其返回值:

删掉两行关于注册函数的(未使用到)

再删掉结构体,不使用结构体赋值,如下效果:

这里有个函数报错,去官方库把这个函数复制过来:

即将这两个函数都复制过去并粘贴到MPU6050_DMP_Init上面:

在官方文件找到自检函数复制过来,同样判断返回值:

再将此函数从官方库找出来粘贴到我们初始化函数上方,删掉下图所示内容:

添加返回值,错误返回1,否则为0

自此,其移植已经完成了,下面经行获取姿态数据 

三、使用MPU6050获取姿态数据

 先写下获取数据的函数,由于DMP会将处理好的数据放入fifo,所以我们只需要读取fifo就可以取出其数据了。至于怎么使用这个函数可以转向定义看看:

定义好相关参数并传到读fifo函数中,再判断其返回值,此时代码应该为:

接下来就开始解析四元数了,将四元数转化为欧拉角:

fifo函数的标志位为sensors,当某个数据存在,读函数会把sensors参数某个位置置一。

我们要获取四元数,判断其标志位,如果有四元数便经行下一步解析。将读出四元数转换成浮点数,直接除2的30次方便将Q30格式换为浮点数了。在上方定义四个浮点型变量用来存储转换后的数据:2^30 = 1073741824,我们宏定义于函数上方,在转换为浮点数,第二张图:

最后将四元数转换为欧拉角,网上百度一下公式,如下:

我们根据其公式编写代码:(乘57.3目的时弧度转换成角度) 至于返回值是好查看错误时是哪步的问题。

此时编译无错误警告,移植完成

需要添加的代码我放下面:

#define DEFAULT_MPU_HZ  (100)
//q30格式,long转float时的除数.
#define q30  1073741824.0f
void mget_ms(unsigned long *time)
{
    //空函数,未使用
}

static unsigned short inv_row_2_scale(const signed char *row)
{
    unsigned short b;

    if (row[0] > 0)
        b = 0;
    else if (row[0] < 0)
        b = 4;
    else if (row[1] > 0)
        b = 1;
    else if (row[1] < 0)
        b = 5;
    else if (row[2] > 0)
        b = 2;
    else if (row[2] < 0)
        b = 6;
    else
        b = 7;      // error
    return b;
}

static unsigned short inv_orientation_matrix_to_scalar(
    const signed char *mtx)
{
    unsigned short scalar;

    /*
       XYZ  010_001_000 Identity Matrix
       XZY  001_010_000
       YXZ  010_000_001
       YZX  000_010_001
       ZXY  001_000_010
       ZYX  000_001_010
     */

    scalar = inv_row_2_scale(mtx);
    scalar |= inv_row_2_scale(mtx + 3) << 3;
    scalar |= inv_row_2_scale(mtx + 6) << 6;


    return scalar;
}

static signed char gyro_orientation[9] = {-1, 0, 0,
                                           0,-1, 0,
                                           0, 0, 1};

static int run_self_test(void)
{
    int result;
    long gyro[3], accel[3];

    result = mpu_run_self_test(gyro, accel);
    if (result == 0x3) {
        /* Test passed. We can trust the gyro data here, so let's push it down
         * to the DMP.
         */
        float sens;
        unsigned short accel_sens;
        mpu_get_gyro_sens(&sens);
        gyro[0] = (long)(gyro[0] * sens);
        gyro[1] = (long)(gyro[1] * sens);
        gyro[2] = (long)(gyro[2] * sens);
        dmp_set_gyro_bias(gyro);
        mpu_get_accel_sens(&accel_sens);
        accel[0] *= accel_sens;
        accel[1] *= accel_sens;
        accel[2] *= accel_sens;
        dmp_set_accel_bias(accel);
    }else   return 1;
    return 0;
}

int MPU6050_DMP_Init(void)
{
    int result;
    struct int_param_s int_param;
    result = mpu_init(&int_param);//mpu初始化
    if(result != 0)    return -1;
    result = mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL);//设置传感器
    if(result != 0)    return -2;
    result = mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);//设置fifo
    if(result != 0)    return -3;
    result = mpu_set_sample_rate(DEFAULT_MPU_HZ);//设置采样率
    if(result != 0)    return -4;
    result = dmp_load_motion_driver_firmware();//加载DMP固件
    if(result != 0)    return -5;
    result = dmp_set_orientation(
        inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
    if(result != 0)    return -6;
    result = dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
                                DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
                                DMP_FEATURE_GYRO_CAL);//设置dmp功能
    if(result != 0)    return -7;
    result = dmp_set_fifo_rate(DEFAULT_MPU_HZ);//设置输出速率
    if(result != 0)    return -8;
    result = run_self_test();//自检
    if(result != 0)     return -9;
    result = mpu_set_dmp_state(1);//使能DMP
    if(result != 0)    return -10;
    return result;
}

int mpu_dmp_get_data(float *pitch,float *roll,float *yaw)
{
    float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;
	unsigned long sensor_timestamp;
	short gyro[3], accel[3], sensors;
	unsigned char more;
	long quat[4]; 
    if(dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more))return 1;
    if(sensors&INV_WXYZ_QUAT) 
	{
        q0 = quat[0] / q30;	//q30格式转换为浮点数
		q1 = quat[1] / q30;
		q2 = quat[2] / q30;
		q3 = quat[3] / q30; 
		//计算得到俯仰角/横滚角/航向角
		*pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;	// pitch
		*roll  = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3;	// roll
		*yaw   = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;	//yaw
    }else return 2;
    return 0;
}

我们在主函数就可以读取数据了:

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "oled.h"
#include "stdio.h"
#include "mpu6050.h"
#include "inv_mpu.h"

int main(void)
{
    u8 string[10] = {0};
    float pitch,roll,yaw;
	MPU_Init();
    MPU6050_DMP_Init();
	while (1)
	{       
        mpu_dmp_get_data(&pitch,&roll,&yaw);
        sprintf((char *)string,"Pitch:%.2f",1.32);//0300
        OLED_ShowString(16,10,string,16,1);
        sprintf((char *)string,"Roll :%.2f",roll);//0300
        OLED_ShowString(16,26,string,16,1);
        sprintf((char *)string,"Yaw  :%.2f",yaw);//0300
        OLED_ShowString(16,46,string,16,1);
        OLED_DrawLine(5,5,125,5,1);
        OLED_DrawLine(5,5,5,62,1);
        OLED_DrawLine(5,62,125,62,1);
        OLED_DrawLine(125,5,125,62,1);
        OLED_Refresh();
        OLED_Refresh();
        if(pitch>70)pitch=70;
        if(pitch<-70)pitch=-70;
        if(roll>70)pitch=70;
        if(roll<-70)pitch=-70;
            
		OLED_Refresh();
        
	}
}

上面MPU6050_DMP_Init应该 int型,并返回负数,否则结果不对!

效果:没拍好,但是代码可用

### 回答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、付费专栏及课程。

余额充值