这里使用的型号是Gy521,图片如下
引脚说明:
VCC:接3.3V电源
GND:电源负极
SCL:I2C时钟线
SDA:I2C数据线
XDA:连接其他i2c设备的主机数据口
XCL:给i2c设备提供时钟
AD0:i2c地址选择位
INT:中断引脚
这里只使用VCC,GND,SCL,SDA4个引脚
方法一、使用卡尔曼滤波进行姿态解算,不过好像只能计算出翻滚角和俯仰角。代码如下
//初始化 MPU6050 以进入可用状态。
void MPU6050_initialize(void) {
IIC_Init();
MPU6050_setClockSource(MPU6050_CLOCK_PLL_YGYRO); //设置时钟
MPU6050_setFullScaleGyroRange(MPU6050_GYRO_FS_2000);//陀螺仪最大量程 +-1000度每秒
MPU6050_setFullScaleAccelRange(MPU6050_ACCEL_FS_2); //加速度度最大量程 +-2G
MPU6050_setSleepEnabled(0); //进入工作状态
MPU6050_setI2CMasterModeEnabled(0); //不让MPU6050 控制AUXI2C
MPU6050_setI2CBypassEnabled(0); //主控制器的I2C与 MPU6050的AUXI2C 直通。控制器可以直接访问HMC5883L
}
//读取MPU6050内置温度传感器数据
float Read_Temperature(void)
{
float Temp;
Temp=(I2C_ReadOneByte(devAddr,MPU6050_RA_TEMP_OUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_TEMP_OUT_L);
if(Temp>32768) Temp-=65536;
return (36.53+Temp/340);
}
//得到陀螺仪值(原始值)
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=i2cRead(MPU_ADDR,MPU6050_RA_ACCEL_XOUT_H,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;
}
//得到加速度值(原始值)
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=i2cRead(MPU_ADDR,MPU6050_RA_GYRO_XOUT_H,6,buf);
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;
}
short aacx,aacy,aacz; //加速度传感器原始数据
short gyrox,gyroy,gyroz; //陀螺仪原始数据
short temp; //温度
short aacx_0=0,aacy_0=0,aacz_0=0; //加速度计零偏
short gyrox_0=0,gyroy_0=0,gyroz_0=0; //陀螺仪零偏
#define RAD2DEG 57.295779513
float Angle_X_Final,Angle_Y_Final;
#define SYS_CLOCK 16//系统时钟
//延时函数
static void delay_ms(uint32_t ms)
{
int i=0;
for(i=0;i<ms;i++)
__delay_cycles(SYS_CLOCK*1000); // 延迟1微秒,该函数为MSP430内置的延迟函数
}
//获取陀螺仪加速度计零飘平均值
void Aac_Gyro_Zero_Shift_Init(void)
{
u16 i;
long compensate[6]={0};
delay_ms(100);
//累计100次,计算平均值
for(i=0;i<100;i++)
{
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据
compensate[0]+=aacx;
compensate[1]+=aacy;
compensate[2]+=aacz-16384;
compensate[3]+=gyrox;
compensate[4]+=gyroy;
compensate[5]+=gyroz;
}
aacx_0=compensate[0]/100;
aacy_0=compensate[1]/100;
aacz_0=compensate[2]/100;
gyrox_0=compensate[3]/100;
gyroy_0=compensate[4]/100;
gyroz_0=compensate[5]/100;
}
//陀螺仪加速度计零飘补偿
/*获取MPU6050数据后调用补偿*/
void Aac_Gyro_Zero_Shift_compensate(void)
{
aacx-=aacx_0;
aacy-=aacy_0;
aacz-=aacz_0;
gyrox-=gyrox_0;
gyroy-=gyroy_0;
gyroz-=gyroz_0;
}
//kalman参数
static float Q_angle=0.001;// 过程噪声的协方差
static float Q_gyro=0.003;//0.003 过程噪声的协方差 过程噪声的协方差为一个一行两列矩阵
static float R_angle=0.5;// 测量噪声的协方差 既测量偏差
static float dt=0.005;//计算周期
void Kalman_Filter_X()
{
static float Accel;
static float Gyro;
static char C_0 = 1;
static float Q_bias, Angle_err;
static float PCt_0, PCt_1, E;
static float K_0, K_1, t_0, t_1;
static float Pdot[4] ={0,0,0,0};
static float PP[2][2] = { { 1, 0 },{ 0, 1 } };
Accel=atan2(aacy,aacz)*RAD2DEG;
Gyro=gyrox/16.4;
Angle_X_Final+=(Gyro - Q_bias) * dt; //先验估计
Pdot[0]=Q_angle - PP[0][1] - PP[1][0]; // Pk-先验估计误差协方差的微分
Pdot[1]=-PP[1][1];
Pdot[2]=-PP[1][1];
Pdot[3]=Q_gyro;
PP[0][0] += Pdot[0] * dt; // Pk-先验估计误差协方差微分的积分
PP[0][1] += Pdot[1] * dt; // =先验估计误差协方差
PP[1][0] += Pdot[2] * dt;
PP[1][1] += Pdot[3] * dt;
Angle_err = Accel - Angle_X_Final; //zk-先验估计
PCt_0 = C_0 * PP[0][0];
PCt_1 = C_0 * PP[1][0];
E = R_angle + C_0 * PCt_0;
K_0 = PCt_0 / E;
K_1 = PCt_1 / E;
t_0 = PCt_0;
t_1 = C_0 * PP[0][1];
PP[0][0] -= K_0 * t_0; //后验估计误差协方差
PP[0][1] -= K_0 * t_1;
PP[1][0] -= K_1 * t_0;
PP[1][1] -= K_1 * t_1;
Angle_X_Final += K_0 * Angle_err; //后验估计
Q_bias += K_1 * Angle_err; //后验估计
}
void Kalman_Filter_Y()
{
static float Accel;
static float Gyro;
static char C_0 = 1;
static float Q_bias, Angle_err;
static float PCt_0, PCt_1, E;
static float K_0, K_1, t_0, t_1;
static float Pdot[4] ={0,0,0,0};
static float PP[2][2] = { { 1, 0 },{ 0, 1 } };
Accel=-atan2(aacx,aacz)*RAD2DEG;
Gyro=gyroy/16.4;
Angle_Y_Final+=(Gyro - Q_bias) * dt; //先验估计
Pdot[0]=Q_angle - PP[0][1] - PP[1][0]; // Pk-先验估计误差协方差的微分
Pdot[1]=-PP[1][1];
Pdot[2]=-PP[1][1];
Pdot[3]=Q_gyro;
PP[0][0] += Pdot[0] * dt; // Pk-先验估计误差协方差微分的积分
PP[0][1] += Pdot[1] * dt; // =先验估计误差协方差
PP[1][0] += Pdot[2] * dt;
PP[1][1] += Pdot[3] * dt;
Angle_err = Accel - Angle_Y_Final; //zk-先验估计
PCt_0 = C_0 * PP[0][0];
PCt_1 = C_0 * PP[1][0];
E = R_angle + C_0 * PCt_0;
K_0 = PCt_0 / E;
K_1 = PCt_1 / E;
t_0 = PCt_0;
t_1 = C_0 * PP[0][1];
PP[0][0] -= K_0 * t_0; //后验估计误差协方差
PP[0][1] -= K_0 * t_1;
PP[1][0] -= K_1 * t_0;
PP[1][1] -= K_1 * t_1;
Angle_Y_Final += K_0 * Angle_err; //后验估计
Q_bias += K_1 * Angle_err; //后验估计
}
//卡尔曼处理数据
void Angle_Calcu(void)
{
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据
Aac_Gyro_Zero_Shift_compensate(); //陀螺仪加速度计零飘补偿
Kalman_Filter_X();
Kalman_Filter_Y();
UART_printf(USCI_A0_BASE,"X:%f,Y:%f\r\n",Angle_X_Final,Angle_Y_Final);
}
效果如图
方法二、移植官方dmp库,可以得到偏航角,翻滚角,俯仰角。
主要代码如下:
#define PRINT_ACCEL (0x01)
#define PRINT_GYRO (0x02)
#define PRINT_QUAT (0x04)
#define ACCEL_ON (0x01)
#define GYRO_ON (0x02)
#define MOTION (0)
#define NO_MOTION (1)
#define DEFAULT_MPU_HZ (200)
#define FLASH_SIZE (512)
#define FLASH_MEM_START ((void*)0x1800)
#define q30 1073741824.0f
short gyro[3], accel[3], sensors;
float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;
static signed char gyro_orientation[9] = {-1, 0, 0,
0,-1, 0,
0, 0, 1};
//设置 MPU6050 的时钟源
void MPU6050_setClockSource(uint8_t source){
IICwriteBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, source);
}
// 设置满量程陀螺仪范围.
void MPU6050_setFullScaleGyroRange(uint8_t range) {
IICwriteBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, range);
}
//设置 MPU6050 加速度计的最大量程
void MPU6050_setFullScaleAccelRange(uint8_t range) {
IICwriteBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, range);
}
//设置 MPU6050 是否进入睡眠模式
void MPU6050_setSleepEnabled(uint8_t enabled) {
IICwriteBit(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, enabled);
}
//读取 MPU6050 WHO_AM_I 标识 将返回 0x68
uint8_t MPU6050_getDeviceID(void) {
IICreadBytes(devAddr, MPU6050_RA_WHO_AM_I, 1, buffer);
return buffer[0];
}
//检测MPU6050 是否已经连接
uint8_t MPU6050_testConnection(void) {
if(MPU6050_getDeviceID() == 0x68) //0b01101000;
return 1;
else
return 0;
}
//设置 MPU6050 是否为AUX I2C线的主机
void MPU6050_setI2CMasterModeEnabled(uint8_t enabled) {
IICwriteBit(devAddr, MPU6050_RA_USER_CTRL, MPU6050_USERCTRL_I2C_MST_EN_BIT, enabled);
}
//设置 MPU6050 是否为AUX I2C线的主机
void MPU6050_setI2CBypassEnabled(uint8_t enabled) {
IICwriteBit(devAddr, MPU6050_RA_INT_PIN_CFG, MPU6050_INTCFG_I2C_BYPASS_EN_BIT, enabled);
}
//初始化 MPU6050 以进入可用状态。
void MPU6050_initialize(void) {
IIC_Init();
MPU6050_setClockSource(MPU6050_CLOCK_PLL_YGYRO); //设置时钟
MPU6050_setFullScaleGyroRange(MPU6050_GYRO_FS_2000);//陀螺仪最大量程 +-1000度每秒
MPU6050_setFullScaleAccelRange(MPU6050_ACCEL_FS_2); //加速度度最大量程 +-2G
MPU6050_setSleepEnabled(0); //进入工作状态
MPU6050_setI2CMasterModeEnabled(0); //不让MPU6050 控制AUXI2C
MPU6050_setI2CBypassEnabled(0); //主控制器的I2C与 MPU6050的AUXI2C 直通。控制器可以直接访问HMC5883L
}
//MPU6050内置DMP的初始化
void DMP_Init(void)
{
u8 temp[1]={0};
i2cRead(0x68,0x75,1,temp);
UART_printf(USCI_A0_BASE,"mpu_set_sensor complete ......\r\n");
if(temp[0]!=0x68)
PMMCTL0 = PMMPW | PMMSWPOR;
if(!mpu_init())
{
if(!mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL))
UART_printf(USCI_A0_BASE,"mpu_set_sensor complete ......\r\n");
if(!mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL))
UART_printf(USCI_A0_BASE,"mpu_configure_fifo complete ......\r\n");
if(!mpu_set_sample_rate(DEFAULT_MPU_HZ))
UART_printf(USCI_A0_BASE,"mpu_set_sample_rate complete ......\r\n");
if(!dmp_load_motion_driver_firmware())
UART_printf(USCI_A0_BASE,"dmp_load_motion_driver_firmware complete ......\r\n");
if(!dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation)))
UART_printf(USCI_A0_BASE,"dmp_set_orientation complete ......\r\n");
if(!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))
UART_printf(USCI_A0_BASE,"dmp_enable_feature complete ......\r\n");
if(!dmp_set_fifo_rate(DEFAULT_MPU_HZ))
UART_printf(USCI_A0_BASE,"dmp_set_fifo_rate complete ......\r\n");
run_self_test();
if(!mpu_set_dmp_state(1))
UART_printf(USCI_A0_BASE,"mpu_set_dmp_state complete ......\r\n");
}
}
int Read_Date(float *Pitch,float *Roll,float *Yaw)
{
uint8_t data[16];
long quat[4];
long quat_q14[4], quat_mag_sq;
//获取数据
if (i2cRead(MPU_ADDR,MPU6050_RA_FIFO_R_W,16,data))
return -1;
//处理数据
quat[0] = ((long)data[0] << 24) | ((long)data[1] << 16) |
((long)data[2] << 8) | data[3];
quat[1] = ((long)data[4] << 24) | ((long)data[5] << 16) |
((long)data[6] << 8) | data[7];
quat[2] = ((long)data[8] << 24) | ((long)data[9] << 16) |
((long)data[10] << 8) | data[11];
quat[3] = ((long)data[12] << 24) | ((long)data[13] << 16) |
((long)data[14] << 8) | data[15];
//数据校验
quat_q14[0] = quat[0] >> 16;
quat_q14[1] = quat[1] >> 16;
quat_q14[2] = quat[2] >> 16;
quat_q14[3] = quat[3] >> 16;
quat_mag_sq = quat_q14[0] * quat_q14[0] + quat_q14[1] * quat_q14[1] +
quat_q14[2] * quat_q14[2] + quat_q14[3] * quat_q14[3];
if ((quat_mag_sq < QUAT_MAG_SQ_MIN) ||
(quat_mag_sq > QUAT_MAG_SQ_MAX)) {
/* Quaternion is outside of the acceptable threshold. */
mpu_reset_fifo();
return -1;
}
//计算数据
q0=quat[0] / q30;
q1=quat[1] / q30;
q2=quat[2] / q30;
q3=quat[3] / q30;
//得到结果
*Pitch = asin(-2 * q1 * q3 + 2 * q0* q2)*57.3;
*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
return 0;
}
效果如下