DMP解算

1.硬件:mpu9250, stm32f103zet6。

2.inv_mpu.h文件,一种基于i2c的直觉陀螺仪的驱动器,所含函数有(移植官方MSP430的DMP驱动过来):

1.static int set_int_enable(unsigned char enable) 模块中断使能函数
2.int mpu_reg_dump(void) 测试打印函数
3.int mpu_read_reg(unsigned char reg, unsigned char *data) 3.向芯片读寄存器值,除了.MEMERY和FIFO
4.int mpu_init(void) MPU9250的初始化
5.int mpu_lp_accel_mode(unsigned char rate) 进入低功耗模式
6.int mpu_get_gyro_reg(short *data, unsigned long *timestamp) 获取新的原始陀螺仪数据
7.int mpu_get_accel_reg(short *data, unsigned long *timestamp获取新的原始加速度数据
8.int mpu_get_temperature(long *data, unsigned long *timestamp) 获取新的温度数据
9.int mpu_set_accel_bias(const long *accel_bias) 偏差配置函数
10.int mpu_reset_fifo(void) 重置FIFO函数
11.int mpu_get_gyro_fsr(unsigned short *fsr) 获得陀螺仪全尺寸范围函数
12.int mpu_set_gyro_fsr(unsigned short fsr) 设置陀螺仪全尺寸范围函数
13.int mpu_get_accel_fsr(unsigned char *fsr) 获得加速度全尺寸范围函数
14.int mpu_set_accel_fsr(unsigned char fsr) 配置加速度全尺寸范围函数
15.int mpu_get_lpf(unsigned short *lpf) .获得DLPF范围函数
16.int mpu_set_lpf(unsigned short lpf) 配置DLPF范围函数
17.int mpu_get_sample_rate(unsigned short *rate) 获得采样频率范围函数
18.int mpu_set_sample_rate(unsigned short rate) 配置采样频率范围函数
19.int mpu_get_compass_sample_rate(unsigned short *rate) 获得罗盘采样频率范围函数
20.int mpu_set_compass_sample_rate(unsigned short rate) 配置罗盘采样频率范围函数
21.int mpu_get_gyro_sens(float *sens) 获得陀螺仪灵敏度比例因子函数
22.int mpu_get_accel_sens(unsigned short *sens) 获得加速计灵敏度比例因子函数
23.int mpu_get_fifo_config(unsigned char *sensors) 获得开启的FIFO通道函数
24.int mpu_configure_fifo(unsigned char sensors) 配置开启FIFO通道函数
25.int mpu_get_power_state(unsigned char *power_on) 获得芯片工作状态
26.int mpu_set_sensors(unsigned char sensors) 配置传感器的时钟和工作状态函数
27.int mpu_get_int_status(short *status).获得中断状态函数
28.int mpu_read_fifo(short *gyro, short *accel, unsigned long *timestamp,unsigned char *sensors, unsigned char *more) 获得FIFO数据函数
29.int mpu_read_fifo_stream(unsigned short length, unsigned char *data,unsigned char *more) 获得FIFO数据长度函数
30.int mpu_set_bypass(unsigned char bypass_on) 设置旁路模式函数
31.int mpu_set_int_level(unsigned char active_low) 设置中断优先级函数
32.int mpu_set_int_latched(unsigned char enable) 设置中断锁存函数-
设置自检函数
33.static int get_st_biases(long *gyro, long *accel, unsigned char hw_test) 获取所有的偏差值函数
34.int mpu_run_self_test(long *gyro, long *accel) 行自检值函数
35.int mpu_write_mem(unsigned short mem_addr, unsigned short length,unsigned char *data) 向DMP写记忆函数
36.int mpu_read_mem(unsigned short mem_addr, unsigned short length,unsigned char *data) 向DMP读记忆函数
37.int mpu_load_firmware(unsigned short length, const unsigned char*firmware,unsigned short start_addr, unsigned short sample_rate) 加载并验证DMP映像函数
38.int mpu_set_dmp_state(unsigned char enable) DMP状态控制函数
39.int mpu_get_dmp_state(unsigned char *enabled) DMP状态读取函数

2.创建IIC协议。可实现包括 IIC 的初始化(IO 口)、IIC 开始、IIC 结束、ACK、IIC读写等功能即可,在其他函数里面,只需要调用相关的 IIC 函数就可以和外部 IIC 器件通信。部分代码如下:

//IIC 初始化
void IIC_Init(void)
{
 GPIO_InitTypeDef GPIO_Initure;
 __HAL_RCC_GPIOB_CLK_ENABLE(); //使能 GPIOB 时钟
 //PH4,5 初始化设置
 GPIO_Initure.Pin=GPIO_PIN_10|GPIO_PIN_11;
 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
 GPIO_Initure.Pull=GPIO_PULLUP; //上拉
 GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
 HAL_GPIO_Init(GPIOB,&GPIO_Initure);
 IIC_SDA=1;
 IIC_SCL=1; 
}
//产生 IIC 起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda 线输出
IIC_SDA=1; 
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
delay_us(4);
IIC_SCL=0;//钳住 I2C 总线,准备发送或接收数据
}
 
//产生 IIC 停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda 线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1; 
IIC_SDA=1;//发送 I2C 总线结束信号
delay_us(4);
 
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA 设置为输入 
IIC_SDA=1;delay_us(1);
 
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出 0 
return 0; 
} 
//产生 ACK 应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生 ACK 应答
 
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
 
//IIC 发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
 
void IIC_Send_Byte(u8 txd)
{ 
 u8 t; 
SDA_OUT(); 
 IIC_SCL=0;//拉低时钟开始数据传输
 for(t=0;t<8;t++)
 { 
 IIC_SDA=(txd&0x80)>>7;
 txd<<=1; 
delay_us(2);
IIC_SCL=1;
delay_us(2); 
IIC_SCL=0;
delay_us(2);
 }
} 
//读 1 个字节,ack=1 时,发送 ACK,ack=0,发送 nACK 
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA 设置为输入
 for(i=0;i<8;i++ )
{
 IIC_SCL=0; 
 delay_us(2);
IIC_SCL=1;
 receive<<=1;
 if(READ_SDA)receive++; 
delay_us(1); 
 }
 if (!ack)
 IIC_NAck();//发送 nACK
 else
 IIC_Ack(); //发送 ACK 
 return receive;
}

可以运用stm32cubemx建立iic,这里可以借鉴别人的,不多赘述。

4.四元函数解算

short gryox,gyroy,gyroz;
short accx,accy,accz;
void IMU_GetData(void)
{
    MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz);
    MPU_Get_Accelerometer(&accx,&accy,&accz);

}

//定义各变量

#define RtA         57.295779f    //弧度->角度            
#define AtR            0.0174533f    //角度->弧度
#define Acc_G         0.0011963f    
#define Gyro_G         0.0610351f                
#define Gyro_Gr        0.0010653f    

#define Kp 18.0f                        
#define Ki 0.008f                         
#define halfT 0.008f     
        
float q0 = 1, q1 = 0, q2 = 0, q3 = 0;   
float exInt = 0, eyInt = 0, ezInt = 0;  
float Angle[3]={0};//最终角度


void IMU_Update(short gyrox,short gyroy,short gyroz,short accx,short accy,short accz)
{
    float ax=accx,ay=accy,az=accz;
    float gx=gyrox,gy=gyroy,gz=gyroz;
      float norm;
      float vx, vy, vz;
      float ex, ey, ez;
      float q0q0 = q0*q0;
      float q0q1 = q0*q1;
      float q0q2 = q0*q2;
      float q1q1 = q1*q1;
      float q1q3 = q1*q3;
      float q2q2 = q2*q2;
      float q2q3 = q2*q3;
      float q3q3 = q3*q3;
    if(ax*ay*az==0)//此时任意方向角加速度为0
         return; 
     gx *= Gyro_Gr;
    gy *= Gyro_Gr;
    gz *= Gyro_Gr;
    
  //对速度值归一化    

norm = sqrt(ax*ax + ay*ay + az*az);       
      ax = ax /norm;
      ay = ay / norm;
      az = az / norm;

      // 提取姿态矩阵的重力分量          
      vx = 2*(q1q3 - q0q2);                                                
      vy = 2*(q0q1 + q2q3);
      vz = q0q0 - q1q1 - q2q2 + q3q3 ;

      // 得到误差向量,并进行积分而消除
      ex = (ay*vz - az*vy) ;                                               
      ey = (az*vx - ax*vz) ;
      ez = (ax*vy - ay*vx) ;

      exInt = exInt + ex * Ki;                                  
      eyInt = eyInt + ey * Ki;
      ezInt = ezInt + ez * Ki;

  // 互补滤波,将误差输入Pid控制器与本次姿态更新中陀螺仪测得的角速度相加,得到一个修正的角速度值,获得的修正的角速度值去更新四元素,从而获得准确的姿态角信息。
      gx = gx + Kp*ex + exInt;                                                   
      gy = gy + Kp*ey + eyInt;
      gz = gz + Kp*ez + ezInt;                                               

      // 解四元数                          
      q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT;
      q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT;
      q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT;
      q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT;

      // 四元数归一化
      norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
      
      q0 = q0 / norm;
      q1 = q1 / norm;
      q2 = q2 / norm;
      q3 = q3 / norm;
      
  //求解姿态角(欧拉角)
ANgle[0]    = asin(-2 * q1 * q3 + 2 * q0* q2)* RtA ; //pitch
ANgle[1]    = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* RtA;      //roll                     ANgle[2]  =atan2(2 * q1 * q2 + 2 * q0 * q3, -2 * q2*q2 - 2 * q3* q3 + 1)* RtA;             // yaw
    }

4.“inv_mpu.c”和“inv_mpu_dmp_motion_driver.c”的文件的调用

        现移植mpu9250的官方DMP库中eMpl。

关于inv_mpu.c:

1.因为所要移植的DMP库原先是基于STM32F4 的,可将关于msp430的文件注释掉。

2.加上#include mpu9250.h,#include usart.h(用于串口输出)。

3.定义所用传感器以及驱动(可用MSP430驱动)

4.添加DMP_读取角度等

short gyro[3], accel[3], sensors;
    unsigned char more;
    long quat[4];
    float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;
    float pitch,roll,yaw;
     u8 DMP_DATA_UPDATA(void)
    {     
        unsigned long sensor_timestamp;
        
        gyro_data_ready_cb();                   //数据采集结束标志位
                    
        if (hal.new_gyro && hal.dmp_on)         //用于计算四元数函数
            {
                if(dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more))
                    {return 1;}    
                    
                if (!more)
                        hal.new_gyro = 0;
                        if(sensors & INV_WXYZ_QUAT)
                            {
                                    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;
                                    yaw=atan2(2*(q1*q2+q0*q3),q0*q0+q1*q1-q2*q2-q3*q3)*57.3;
                            }
            }
                    

关于inv_mpu_dmp_motion_driver.c:

1.添加dmpKey.h和dmpmap.h文件以及串口通信文件。

2.将相关msp430的文件注释掉。

3.定义目标版采用MSP430,对f103无影响。

5.添加MPU9250的初始化程序

可根据正点原子MPU6050驱动程序进行修改:

5.1.初始化MPU9250(时钟、端口、引脚、iic总线、传感器)

5.2.设置各传感器量程

5.3.获取数字滤波器、温度以及陀螺仪原始值等功能

5.4.实现IIC连续写读,添加函数MPU_Write/Read_Len, MPU_IIC_Start/Stop等,即跟上面所诉一样进行调用iic即可

6.main.c主函数进行补充

主要完成系统初始化以及主循环功能(可对正点原子MPU6050工程main.c文件的基础上进行修改):

while(1)                                       
	{
		error = mpu_dmp_init();                      //初始化mpu_dmp库
		printf("ERROR:%d
",error);                //串口显示初始化错误值
		switch (error)                               //对不同错误值类型判断
		{
			case 0:printf("DMP库初始化正常
");break;
			case 1:printf("设置传感器失败
");break;
			case 2:printf("设置FIFO失败
");break;
			case 3:printf("设置采样率失败
");break;
			case 4:printf("加载dmp固件失败
");break;
			case 5:printf("设置陀螺仪方向失败
");break;
			case 6:printf("设置dmp功能失败
");break;
			case 7:printf("设置DMP输出速率失败
");break;
			case 8:printf("自检失败
");break;
			case 9:printf("使能DMP失败
");break;
			case 10:printf("初始化MPU6050失败
");break;
			default :printf("未知错误
");break;
		}
		if(error == 0)break;                        //如果没有错误直接退出循环
		void MPU_Read(void)
{
	
	if(mpu_dmp_get_data(&yaw,&pitch,&roll)==0)      //dmp处理得到数据,对返回值进行判断
	{ 
		printf("Pitch:%f    Roll:%f    Yaw:%f
",pitch,roll,yaw);//串口输出三轴角度
		mpu9250.speed++;                            //显示速度自加
		if(mpu9250.speed == 1)						//显示速度阈值设置
		{
			mpu9250.flag = 1;						//采集成功标志位设置为有效
			mpu9250.speed = 0;						//显示速度归零
		}	
	}
	else 											//采集不成功										
	{
		mpu9250.flag = 0;							//采集成功标志位设置为无效
	}	
}
/**
  * @brief  MPU6050数据上报
  * @param  无
  * @retval 无
  */
void DATA_Report(void)
{
	if(mpu9250.flag == 1)						    //采集成功时
	{ 
		temp=pitch*100;							    //赋temp为pitch
		if(temp<0)								    //对数据正负判断,判断为负时
		{
			temp=-temp;						        //对负数据取反	
			sprintf((char *)tmp_buf," Pitch:-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		else                                        //判断为正时 
		{
			sprintf((char *)tmp_buf," Pitch: %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		temp=roll*100;							    //赋temp为pitch
		if(temp<0)								    //对数据正负判断,判断为负时
		{
			temp=-temp;						        //对负数据取反	
			sprintf((char *)tmp_buf," Roll :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		else                                        //判断为正时 
		{
			sprintf((char *)tmp_buf," Roll : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		
		temp=yaw*100;							    //赋temp为pitch
		if(temp<0)								    //对数据正负判断,判断为负时
		{
			temp=-temp;						        //对负数据取反	
			sprintf((char *)tmp_buf," Yaw  :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		else                                        //判断为正时 
		{
			sprintf((char *)tmp_buf," Yaw  : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
		}
		mpu9250.flag = 0;							//采集成功标志位设置为无效
	}
	else ;											//防卡死
}

DMP相关文件工程在下面链接:

链接:https://pan.baidu.com/s/1BJWJsD-4n0pl7FQ_z2Du0Q?pwd=zhr6 提取码:zhr6

 

 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值