×
单片机 > 单片机程序设计 > 详情

MPU6050 HMC5883 Kalman 融合算法移植

发布时间:2020-06-06 发布时间:
|

一、卡尔曼滤波九轴融合算法stm32尝试



1、Kalman滤波文件[.h已经封装为结构体]



  1 /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics-> All rights reserved->

  2 

  3  This software may be distributed and modified under the terms of the GNU

  4  General Public License version 2 (GPL2) as published by the Free Software

  5  Foundation and appearing in the file GPL2->TXT included in the packaging of

  6  this file-> Please note that GPL2 Section 2[b] requires that all works based

  7  on this software must also be made publicly available under the terms of

  8  the GPL2 ("Copyleft")->

  9 

 10  Contact information

 11  -------------------

 12 

 13  Kristian Lauszus, TKJ Electronics

 14  Web      :  http://www->tkjelectronics->com

 15  e-mail   :  kristianl@tkjelectronics->com

 16  */

 17 

 18 #ifndef _Kalman_h

 19 #define _Kalman_h

 20 struct Kalman {

 21     /* Kalman filter variables */

 22     double Q_angle; // Process noise variance for the accelerometer

 23     double Q_bias; // Process noise variance for the gyro bias

 24     double R_measure; // Measurement noise variance - this is actually the variance of the measurement noise

 25 

 26     double angle; // The angle calculated by the Kalman filter - part of the 2x1 state vector

 27     double bias; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector

 28     double rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate

 29 

 30     double P[2][2]; // Error covariance matrix - This is a 2x2 matrix

 31     double K[2]; // Kalman gain - This is a 2x1 vector

 32     double y; // Angle difference

 33     double S; // Estimate error

 34 };

 35 

 36 void   Init(struct Kalman* klm){

 37     /* We will set the variables like so, these can also be tuned by the user */

 38     klm->Q_angle = 0.001;

 39     klm->Q_bias = 0.003;

 40     klm->R_measure = 0.03;

 41 

 42     klm->angle = 0; // Reset the angle

 43     klm->bias = 0; // Reset bias

 44 

 45     klm->P[0][0] = 0; // Since we assume that the bias is 0 and we know the starting angle (use setAngle), the error covariance matrix is set like so - see: http://en->wikipedia->org/wiki/Kalman_filter#Example_application->2C_technical

 46     klm->P[0][1] = 0;

 47     klm->P[1][0] = 0;

 48     klm->P[1][1] = 0;

 49 }

 50 

 51 // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds

 52 double getAngle(struct Kalman * klm, double newAngle, double newRate, double dt) {

 53     // KasBot V2  -  Kalman filter module - http://www->x-firm->com/?page_id=145

 54     // Modified by Kristian Lauszus

 55     // See my blog post for more information: http://blog->tkjelectronics->dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it

 56     

 57     // Discrete Kalman filter time update equations - Time Update ("Predict")

 58     // Update xhat - Project the state ahead

 59     /* Step 1 */

 60     klm->rate = newRate - klm->bias;

 61     klm->angle += dt * klm->rate;

 62     

 63     // Update estimation error covariance - Project the error covariance ahead

 64     /* Step 2 */

 65     klm->P[0][0] += dt * (dt*klm->P[1][1] - klm->P[0][1] - klm->P[1][0] + klm->Q_angle);

 66     klm->P[0][1] -= dt * klm->P[1][1];

 67     klm->P[1][0] -= dt * klm->P[1][1];

 68     klm->P[1][1] += klm->Q_bias * dt;

 69     

 70     // Discrete Kalman filter measurement update equations - Measurement Update ("Correct")

 71     // Calculate Kalman gain - Compute the Kalman gain

 72     /* Step 4 */

 73     klm->S = klm->P[0][0] + klm->R_measure;

 74     /* Step 5 */

 75     klm->K[0] = klm->P[0][0] / klm->S;

 76     klm->K[1] = klm->P[1][0] / klm->S;

 77     

 78     // Calculate angle and bias - Update estimate with measurement zk (newAngle)

 79     /* Step 3 */

 80     klm->y = newAngle - klm->angle;

 81     /* Step 6 */

 82     klm->angle += klm->K[0] * klm->y;

 83     klm->bias += klm->K[1] * klm->y;

 84     

 85     // Calculate estimation error covariance - Update the error covariance

 86     /* Step 7 */

 87     klm->P[0][0] -= klm->K[0] * klm->P[0][0];

 88     klm->P[0][1] -= klm->K[0] * klm->P[0][1];

 89     klm->P[1][0] -= klm->K[1] * klm->P[0][0];

 90     klm->P[1][1] -= klm->K[1] * klm->P[0][1];

 91     

 92     return klm->angle;

 93 }

 94 

 95 void setAngle(struct Kalman* klm, double newAngle) { klm->angle = newAngle; } // Used to set angle, this should be set as the starting angle

 96 double getRate(struct Kalman* klm) { return klm->rate; } // Return the unbiased rate

 97 

 98 /* These are used to tune the Kalman filter */

 99 void setQangle(struct Kalman* klm, double newQ_angle) { klm->Q_angle = newQ_angle; }

100 void setQbias(struct Kalman* klm, double newQ_bias) { klm->Q_bias = newQ_bias; }

101 void setRmeasure(struct Kalman* klm, double newR_measure) { klm->R_measure = newR_measure; }

102 

103 double getQangle(struct Kalman* klm) { return klm->Q_angle; }

104 double getQbias(struct Kalman* klm) { return klm->Q_bias; }

105 double getRmeasure(struct Kalman* klm) { return klm->R_measure; }

106 


2、I2C总线代码[这里把MPU和HMC挂接到上面,通过改变SlaveAddress的值来和不同的设备通信]



  1 #include "stm32f10x.h"

  2 

  3 /*标志是否读出数据*/

  4 char  test=0;

  5 /*I2C从设备*/

  6 unsigned char SlaveAddress;

  7 /*模拟IIC端口输出输入定义*/

  8 #define SCL_H         GPIOB->BSRR = GPIO_Pin_10

  9 #define SCL_L         GPIOB->BRR  = GPIO_Pin_10 

 10 #define SDA_H         GPIOB->BSRR = GPIO_Pin_11

 11 #define SDA_L         GPIOB->BRR  = GPIO_Pin_11

 12 #define SCL_read      GPIOB->IDR  & GPIO_Pin_10

 13 #define SDA_read      GPIOB->IDR  & GPIO_Pin_11

 14 

 15 /*I2C的端口初始化---------------------------------------*/

 16 void I2C_GPIO_Config(void)

 17 {

 18     GPIO_InitTypeDef  GPIO_InitStructure; 

 19     

 20     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10;

 21     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 22     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  

 23     GPIO_Init(GPIOB, &GPIO_InitStructure);

 24     

 25     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11;

 26     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 27     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;

 28     GPIO_Init(GPIOB, &GPIO_InitStructure);

 29 }

 30 

 31 /*I2C的延时函数-----------------------------------------*/

 32 void I2C_delay(void)

 33 {

 34     u8 i=30; //这里可以优化速度    ,经测试最低到5还能写入

 35     while(i) 

 36     { 

 37         i--; 

 38     }  

 39 }

 40 

 41 /*I2C的等待5ms函数--------------------------------------*/

 42 void delay5ms(void)

 43 {

 44     int i=5000;  

 45     while(i) 

 46     { 

 47         i--; 

 48     }  

 49 }

 50 

 51 /*I2C启动函数-------------------------------------------*/

 52 bool I2C_Start(void)

 53 {

 54     SDA_H;

 55     SCL_H;

 56     I2C_delay();

 57     if(!SDA_read)return FALSE;    //SDA线为低电平则总线忙,退出

 58     SDA_L;

 59     I2C_delay();

 60     if(SDA_read) return FALSE;    //SDA线为高电平则总线出错,退出

 61     SDA_L;

 62     I2C_delay();

 63     return TRUE;

 64 }

 65 

 66 /*I2C停止函数-------------------------------------------*/

 67 void I2C_Stop(void)

 68 {

 69     SCL_L;

 70     I2C_delay();

 71     SDA_L;

 72     I2C_delay();

 73     SCL_H;

 74     I2C_delay();

 75     SDA_H;

 76     I2C_delay();

 77 } 

 78 

 79 /*I2C的ACK函数------------------------------------------*/

 80 void I2C_Ack(void)

 81 {    

 82     SCL_L;

 83     I2C_delay();

 84     SDA_L;

 85     I2C_delay();

 86     SCL_H;

 87     I2C_delay();

 88     SCL_L;

 89     I2C_delay();

 90 }   

 91 

 92 /*I2C的NoACK函数----------------------------------------*/

 93 void I2C_NoAck(void)

 94 {    

 95     SCL_L;

 96     I2C_delay();

 97     SDA_H;

 98     I2C_delay();

 99     SCL_H;

100     I2C_delay();

101     SCL_L;

102     I2C_delay();

103 } 

104 

105 /*I2C等待ACK函数----------------------------------------*/

106 bool I2C_WaitAck(void)      //返回为:=1有ACK,=0无ACK

107 {

108     SCL_L;

109     I2C_delay();

110     SDA_H;            

111     I2C_delay();

112     SCL_H;

113     I2C_delay();

114     if(SDA_read)

115     {

116         SCL_L;

117         I2C_delay();

118         return FALSE;

119     }

120     SCL_L;

121     I2C_delay();

122     return TRUE;

123 }

124 

125 /*I2C发送一个u8数据函数---------------------------------*/

126 void I2C_SendByte(u8 SendByte) //数据从高位到低位//

127 {

128     u8 i=8;

129     while(i--)

130     {

131         SCL_L;

132         I2C_delay();

133         if(SendByte&0x80)

134             SDA_H;  

135         else 

136             SDA_L;   

137         SendByte<<=1;

138         I2C_delay();

139         SCL_H;

140         I2C_delay();

141     }

142     SCL_L;

143 }  

144 

145 /*I2C读取一个u8数据函数---------------------------------*/

146 unsigned char I2C_RadeByte(void)  //数据从高位到低位//

147 { 

148     u8 i=8;

149     u8 ReceiveByte=0;

150     

151     SDA_H;                

152     while(i--)

153     {

154         ReceiveByte<<=1;      

155         SCL_L;

156         I2C_delay();

157         SCL_H;

158         I2C_delay();    

159         if(SDA_read)

160         {

161             ReceiveByte|=0x01;

162         }

163     }

164     SCL_L;

165     return ReceiveByte;

166 }  

167 

168 /*I2C向指定设备指定地址写入u8数据-----------------------*/

169 void Single_WriteI2C(unsigned char REG_Address,unsigned char REG_data)//单字节写入

170 {

171     if(!I2C_Start())return;

172     I2C_SendByte(SlaveAddress);   //发送设备地址+写信号//I2C_SendByte(((REG_Address & 0x0700) >>7) | SlaveAddress & 0xFFFE);//设置高起始地址+器件地址 

173     if(!I2C_WaitAck()){I2C_Stop(); return;}

174     I2C_SendByte(REG_Address );   //设置低起始地址      

175     I2C_WaitAck();    

176     I2C_SendByte(REG_data);

177     I2C_WaitAck();   

178     I2C_Stop(); 

179     delay5ms();

180 }

181 

182 /*I2C向指定设备指定地址读出u8数据-----------------------*/

183 unsigned char Single_ReadI2C(unsigned char REG_Address)//读取单字节

184 {   

185     unsigned char REG_data;         

186     if(!I2C_Start())return FALSE;

187     I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址 

188     if(!I2C_WaitAck()){I2C_Stop();test=1; return FALSE;}

189     I2C_SendByte((u8) REG_Address);   //设置低起始地址      

190     I2C_WaitAck();

191     I2C_Start();

192     I2C_SendByte(SlaveAddress+1);

193     I2C_WaitAck();

194     

195     REG_data= I2C_RadeByte();

196     I2C_NoAck();

197     I2C_Stop();

198     //return TRUE;

199     return REG_data;

200 }


3、MPU6050的驱动代码[updataMPU6050中获取数据为什么一正一负不是很清楚,是按照GitHub上arduino版本改的]



 1 #define    SlaveAddressMPU   0x68      //定义器件5883在IIC总线中的从地址

 2 

 3 typedef unsigned char  uchar;

 4 

 5 extern int accX, accY, accZ;

 6 extern int gyroX, gyroY, gyroZ;

 7 extern uchar    SlaveAddress;       //IIC写入时的地址字节数据,+1为读取

 8 extern uchar Single_ReadI2C(uchar REG_Address);                        //读取I2C数据

 9 extern void  Single_WriteI2C(uchar REG_Address,uchar REG_data);        //向I2C写入数据

10 

11 //****************************************

12 // 定义MPU6050内部地址

13 //****************************************

14 #define    SMPLRT_DIV        0x19    //陀螺仪采样率,典型值:0x07(125Hz)

15 #define    CONFIG            0x1A    //低通滤波频率,典型值:0x06(5Hz)

16 #define    GYRO_CONFIG        0x1B    //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)

17 #define    ACCEL_CONFIG    0x1C    //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)

18 #define    ACCEL_XOUT_H    0x3B

19 #define    ACCEL_XOUT_L    0x3C

20 #define    ACCEL_YOUT_H    0x3D

21 #define    ACCEL_YOUT_L    0x3E

22 #define    ACCEL_ZOUT_H    0x3F

23 #define    ACCEL_ZOUT_L    0x40

24 #define    TEMP_OUT_H        0x41

25 #define    TEMP_OUT_L        0x42

26 #define    GYRO_XOUT_H        0x43

27 #define    GYRO_XOUT_L        0x44    

28 #define    GYRO_YOUT_H        0x45

29 #define    GYRO_YOUT_L        0x46

30 #define    GYRO_ZOUT_H        0x47

31 #define    GYRO_ZOUT_L        0x48

32 #define    PWR_MGMT_1        0x6B    //电源管理,典型值:0x00(正常启用)

33 #define    WHO_AM_I        0x75    //IIC地址寄存器(默认数值0x68,只读)

34 #define    MPU6050_Addr    0xD0    //IIC写入时的地址字节数据,+1为读取

35 //**************************************

36 //初始化MPU6050

37 //**************************************

38 void InitMPU6050()

39 {

40     SlaveAddress=MPU6050_Addr;

41     Single_WriteI2C(PWR_MGMT_1, 0x00);    //解除休眠状态

42     Single_WriteI2C(SMPLRT_DIV, 0x07);// Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz

43     Single_WriteI2C(CONFIG, 0x00);// Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling

44     Single_WriteI2C(GYRO_CONFIG, 0x00);// Set Gyro Full Scale Range to ±250deg/s

45     Single_WriteI2C(ACCEL_CONFIG, 0x00);// Set Accelerometer Full Scale Range to ±2g

46     Single_WriteI2C(PWR_MGMT_1, 0x01);// PLL with X axis gyroscope reference and disable sleep mode

47 }

48 //**************************************

49 //// Get accelerometer and gyroscope values

50 //**************************************

51 void updateMPU6050()

52 {

53     SlaveAddress=MPU6050_Addr;// Get accelerometer and gyroscope values

54 

55     accX=((Single_ReadI2C(ACCEL_XOUT_H)<<8)+Single_ReadI2C(ACCEL_XOUT_L));

56     accY=-((Single_ReadI2C(ACCEL_YOUT_H)<<8)+Single_ReadI2C(ACCEL_YOUT_L));

57     accZ=((Single_ReadI2C(ACCEL_ZOUT_H)<<8)+Single_ReadI2C(ACCEL_ZOUT_L));

58     

59     gyroX=-((Single_ReadI2C(GYRO_XOUT_H)<<8)+Single_ReadI2C(GYRO_XOUT_L));

60     gyroY=((Single_ReadI2C(GYRO_YOUT_H)<<8)+Single_ReadI2C(GYRO_YOUT_L));

61     gyroZ=-((Single_ReadI2C(GYRO_ZOUT_H)<<8)+Single_ReadI2C(GYRO_ZOUT_L));    

62 }


4、HMC5883的驱动代码[updataHMC5883直接获取源数据,并未做大的处理]



 1 #define   uchar unsigned char

 2 #define   uint unsigned int    

 3 

 4 //定义器件在IIC总线中的从地址,根据ALT  ADDRESS地址引脚不同修改

 5 #define    HMC5883_Addr   0x3C    //磁场传感器器件地址

 6 

 7 unsigned char BUF[8];                         //接收数据缓存区                   

 8 extern uchar    SlaveAddress;               //IIC写入时的地址字节数据,+1为读取

 9 

10 extern int magX, magY, magZ;    //hmc最原始数据

11 extern uchar SlaveAddress;       //IIC写入时的地址字节数据,+1为读取

12 extern uchar Single_ReadI2C(uchar REG_Address);                        //读取I2C数据

13 extern void  Single_WriteI2C(uchar REG_Address,uchar REG_data);        //向I2C写入数据

14 //**************************************

15 //初始化HMC5883,根据需要请参考pdf进行修改

16 //**************************************

17 void InitHMC5883()

18 {

19     SlaveAddress=HMC5883_Addr;

20     Single_WriteI2C(0x02,0x00);  //

21     Single_WriteI2C(0x01,0xE0);  //

22 }

23 //**************************************

24 //从HMC5883连续读取6个数据放在BUF中

25 //**************************************

26 void updateHMC5883()

27 {

28     SlaveAddress=HMC5883_Addr;

29     Single_WriteI2C(0x00,0x14); 

30     Single_WriteI2C(0x02,0x00); 

31 //    Delayms(10);

32     

33     BUF[1]=Single_ReadI2C(0x03);//OUT_X_L_A

34     BUF[2]=Single_ReadI2C(0x04);//OUT_X_H_A

35     BUF[3]=Single_ReadI2C(0x07);//OUT_Y_L_A

36     BUF[4]=Single_ReadI2C(0x08);//OUT_Y_H_A

37     BUF[5]=Single_ReadI2C(0x05);//OUT_Z_L_A

38     BUF[6]=Single_ReadI2C(0x06);//OUT_Y_H_A

39     

40     magX=(BUF[1] << 8) | BUF[2]; //Combine MSB and LSB of X Data output register

41     magY=(BUF[3] << 8) | BUF[4]; //Combine MSB and LSB of Y Data output register

42     magZ=(BUF[5] << 8) | BUF[6]; //Combine MSB and LSB of Z Data output register

43 

44 //    if(magX>0x7fff)magX-=0xffff;//补码表示滴~所以要转化一下      

45 //    if(magY>0x7fff)magY-=0xffff;    

46 //     if(magZ>0x7fff)magZ-=0xffff;

47 }


5、USART简单的单字节发送的串口驱动文件



 1 #include "stm32f10x.h"

 2 

 3 void USART1_Configuration(void);

 4 void USART1_SendData(u8 SendData);

 5 extern void Delayms(vu32 m);

 6 

 7 void USART1_Configuration()

 8 {

 9     GPIO_InitTypeDef GPIO_InitStructure;

10     USART_InitTypeDef USART_InitStructure;

11     USART_ClockInitTypeDef  USART_ClockInitStructure;

12 

13     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB ,ENABLE );//| RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE  );

14     RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 |RCC_APB2Periph_USART1, ENABLE  );

15 

16     /* Configure USART1 Tx (PA.09) as alternate function push-pull */

17     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                 //    选中管脚9

18     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         // 复用推挽输出

19     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         // 最高输出速率50MHz

20     GPIO_Init(GPIOA, &GPIO_InitStructure);                 // 选择A端口

21     

22     /* Configure USART1 Rx (PA.10) as input floating */

23     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;              //选中管脚10

24     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //浮空输入

25     GPIO_Init(GPIOA, &GPIO_InitStructure);                  //选择A端口

26 

27 

28     USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;            // 时钟低电平活动

29     USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;                // 时钟低电平

30     USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                // 时钟第二个边沿进行数据捕获

31     USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;        // 最后一位数据的时钟脉冲不从SCLK输出

32     /* Configure the USART1 synchronous paramters */

33     USART_ClockInit(USART1, &USART_ClockInitStructure);                    // 时钟参数初始化设置

34     

35     USART_InitStructure.USART_BaudRate = 9600;                          // 波特率为:115200

36     USART_InitStructure.USART_WordLength = USART_WordLength_8b;              // 8位数据

37     USART_InitStructure.USART_StopBits = USART_StopBits_1;                  // 在帧结尾传输1个停止位

38     USART_InitStructure.USART_Parity = USART_Parity_No ;                  // 奇偶失能

39     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    // 硬件流控制失能

40     

41     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;          // 发送使能+接收使能

42     /* Configure USART1 basic and asynchronous paramters */

43     USART_Init(USART1, &USART_InitStructure);

44     

45     /* Enable USART1 */

46     USART_ClearFlag(USART1, USART_IT_RXNE);             //清中断,以免一启用中断后立即产生中断

47     USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);        //使能USART1中断源

48     USART_Cmd(USART1, ENABLE);                            //USART1总开关:开启 

49 }

50 void  USART1_SendData(u8 SendData)

51 {

52     USART_SendData(USART1, SendData);

53     while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);

54 }


6、非精确延时函数集[其他文件所需的一些延时放在这里]



 1 #include "stm32f10x.h"

 2 

 3 

 4 void Delay(vu32 nCount)

 5 {

 6     for(; nCount != 0; nCount--);

 7 }

 8 void Delayms(vu32 m)

 9 {

10     u32 i;    

11     for(; m != 0; m--)    

12         for (i=0; i<50000; i++);

13 }


7、main函数文件


  1 #include "stm32f10x.h"

  2 #include "Kalman.h" 

  3 #include

  4 #define RESTRICT_PITCH // Comment out to restrict roll to ±90deg instead - please read: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf

  5 

  6 struct Kalman kalmanX, kalmanY, kalmanZ; // Create the Kalman instances

  7 

  8 /* IMU Data MPU6050 AND HMC5883 Data*/

  9 int accX, accY, accZ;

 10 int gyroX, gyroY, gyroZ;

 11 int magX, magY, magZ;

 12 

 13 

 14 double roll, pitch, yaw; // Roll and pitch are calculated using the accelerometer while yaw is calculated using the magnetometer

 15 

 16 double gyroXangle, gyroYangle, gyroZangle; // Angle calculate using the gyro only 只用陀螺仪计算角度

 17 double compAngleX, compAngleY, compAngleZ; // Calculated angle using a complementary filter  用电磁计计算角度

 18 double kalAngleX, kalAngleY, kalAngleZ; // Calculated angle using a Kalman filter    用kalman计算角度

 19 

 20 //uint32_t timer,micros; //上一次时间与当前时间

 21 uint8_t i2cData[14]; // Buffer for I2C data

 22 

 23 #define MAG0MAX 603

 24 #define MAG0MIN -578

 25 

 26 #define MAG1MAX 542

 27 #define MAG1MIN -701

 28 

 29 #define MAG2MAX 547

 30 #define MAG2MIN -556

 31 

 32 #define RAD_TO_DEG 57.295779513082320876798154814105  // 弧度转角度的转换率

 33 #define DEG_TO_RAD 0.01745329251994329576923690768489 // 角度转弧度的转换率

 34 

 35 float magOffset[3] = { (MAG0MAX + MAG0MIN) / 2, (MAG1MAX + MAG1MIN) / 2, (MAG2MAX + MAG2MIN) / 2 };

 36 double magGain[3];

 37 

 38 void  SYSTICK_Configuration(void);                                //系统滴答中断配置

 39 void  RCC_Configuration(void);

 40 void  updatePitchRoll(void);                                    //根据加速计刷新Pitch和Roll数据

 41 void  updateYaw(void);                                            //根据磁力计刷新Yaw角

 42 void  InitAll(void);                                            //循环前的初始化

 43 void  func(void);                                                //循环里的内容

 44 extern void InitMPU6050(void);                                    //初始化MPU6050

 45 extern void InitHMC5883(void);                                    //初始化HMC5883

 46 extern void updateMPU6050(void);                                //Get accelerometer and gyroscope values

 47 extern void updateHMC5883(void);                                //Get magnetometer values

 48 extern void USART1_Configuration(void);                            //串口初始化

 49 extern void USART1_SendData(u8 SendData);                        //串口发送函数

 50 extern void I2C_GPIO_Config(void);                                //I2C初始化函数

 51 /****************************************************************************

 52 * 名    称:int main(void)

 53 * 功    能:主函数

 54 * 入口参数:无

 55 * 出口参数:无

 56 * 说    明:

 57 * 调用方法:无 

 58 ****************************************************************************/ 

 59 int main(void)

 60 {

 61       RCC_Configuration();                   //系统时钟配置    

 62       USART1_Configuration();

 63       I2C_GPIO_Config();

 64       InitHMC5883();

 65     InitMPU6050();

 66     InitAll();    

 67 //    SYSTICK_Configuration();                

 68      while(1)

 69     {

 70         func();

 71       }

 72 }

 73 ///*

 74 //系统滴答中断配置

 75 //*/

 76 //void SYSTICK_Configuration(void)

 77 //{

 78 //    micros=0;//全局计数时间归零

 79 //     if (SysTick_Config(72000))            //时钟节拍中断时1000ms一次  用于定时 

 80 //       { 

 81 //        /* Capture error */ 

 82 ////        while (1);

 83 //       }

 84 //}

 85 ///*

 86 //当前时间++.为了防止溢出当其大于2^20时,令其归零

 87 //*/

 88 //void SysTickHandler(void)

 89 //{

 90 //     micros++;

 91 //    if(micros>(1<<20))

 92 //          micros=0;

 93 //}

 94 /****************************************************************************

 95 * 名    称:void RCC_Configuration(void)

 96 * 功    能:系统时钟配置为72MHZ

 97 * 入口参数:无

 98 * 出口参数:无

 99 * 说    明:

100 * 调用方法:无 

101 ****************************************************************************/ 

102 void RCC_Configuration(void)

103 {   

104     SystemInit();

105 }

106 

107 void InitAll()

108 {

109     /* Set Kalman and gyro starting angle */

110     updateMPU6050();

111     updateHMC5883();

112     updatePitchRoll();

113     updateYaw();

114     

115     setAngle(&kalmanX,roll); // First set roll starting angle

116     gyroXangle = roll;

117     compAngleX = roll;

118     

119     setAngle(&kalmanY,pitch); // Then pitch

120     gyroYangle = pitch;

121     compAngleY = pitch;

122     

123     setAngle(&kalmanZ,yaw); // And finally yaw

124     gyroZangle = yaw;

125     compAngleZ = yaw;

126     

127 //    timer = micros; // Initialize the timer    

128 }

129 

130 void send(double xx,double yy,double zz)

131 {

132     int    a[3];

133      u8 i,sendData[12];       

134     a[0]=(int)xx;a[1]=(int)yy;a[2]=(int)zz;

135     for(i=0;i<3;i++)

136     {

137         if(a[i]<0){

138             sendData[i*4]='-';

139             a[i]=-a[i];

140         }

141         else sendData[i*4]=' ';

142         sendData[i*4+1]=(u8)(a[i]%1000/100+0x30);

143         sendData[i*4+2]=(u8)(a[i]%100/10+0x30);

144         sendData[i*4+3]=(u8)(a[i]%10+0x30);

145     }

146     for(i=0;i<12;i++)

147     {

148         USART1_SendData(sendData[i]);

149     }

150     USART1_SendData(0x0D);

151     USART1_SendData(0x0A);

152 }

153 

154 void func()

155 {

156     double gyroXrate,gyroYrate,gyroZrate,dt=0.01;

157     /* Update all the IMU values */

158     updateMPU6050();

159     updateHMC5883();

160     

161 //    dt = (double)(micros - timer) / 1000; // Calculate delta time

162 //    timer = micros;

163 //    if(dt<0)dt+=(1<<20);    //时间是周期性的,有可能当前时间小于上次时间,因为这个周期远大于两次积分时间,所以最多相差1<<20

164 

165     /* Roll and pitch estimation */

166     updatePitchRoll();             //用采集的加速计的值计算roll和pitch的值

167     gyroXrate = gyroX / 131.0;     // Convert to deg/s    把陀螺仪的角加速度按照当初设定的量程转换为°/s

168     gyroYrate = gyroY / 131.0;     // Convert to deg/s

169     

170     #ifdef RESTRICT_PITCH        //如果上面有#define RESTRICT_PITCH就采用这种方法计算,防止出现-180和180之间的跳跃

171     // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees

172     if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) {

173         setAngle(&kalmanX,roll);

174         compAngleX = roll;

175         kalAngleX = roll;

176         gyroXangle = roll;

177     } else

178     kalAngleX = getAngle(&kalmanX, roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

179     

180     if (fabs(kalAngleX) > 90)

181         gyroYrate = -gyroYrate; // Invert rate, so it fits the restricted accelerometer reading

182     kalAngleY = getAngle(&kalmanY,pitch, gyroYrate, dt);

183     #else

184     // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees

185     if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {

186         kalmanY.setAngle(pitch);

187         compAngleY = pitch;

188         kalAngleY = pitch;

189         gyroYangle = pitch;

190     } else

191     kalAngleY = getAngle(&kalmanY, pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter

192     

193     if (abs(kalAngleY) > 90)

194         gyroXrate = -gyroXrate; // Invert rate, so it fits the restricted accelerometer reading

195     kalAngleX = getAngle(&kalmanX, roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

196     #endif

197     

198     

199     /* Yaw estimation */

200     updateYaw();

201     gyroZrate = gyroZ / 131.0; // Convert to deg/s

202     // This fixes the transition problem when the yaw angle jumps between -180 and 180 degrees

203     if ((yaw < -90 && kalAngleZ > 90) || (yaw > 90 && kalAngleZ < -90)) {

204         setAngle(&kalmanZ,yaw);

205         compAngleZ = yaw;

206         kalAngleZ = yaw;

207         gyroZangle = yaw;

208     } else

209     kalAngleZ = getAngle(&kalmanZ, yaw, gyroZrate, dt); // Calculate the angle using a Kalman filter

210     

211     

212     /* Estimate angles using gyro only */

213     gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter

214     gyroYangle += gyroYrate * dt;

215     gyroZangle += gyroZrate * dt;

216     //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate from the Kalman filter

217     //gyroYangle += kalmanY.getRate() * dt;

218     //gyroZangle += kalmanZ.getRate() * dt;

219     

220     /* Estimate angles using complimentary filter */

221     compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter

222     compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

223     compAngleZ = 0.93 * (compAngleZ + gyroZrate * dt) + 0.07 * yaw;

224     

225     // Reset the gyro angles when they has drifted too much

226     if (gyroXangle < -180 || gyroXangle > 180)

227         gyroXangle = kalAngleX;

228     if (gyroYangle < -180 || gyroYangle > 180)

229         gyroYangle = kalAngleY;

230     if (gyroZangle < -180 || gyroZangle > 180)

231         gyroZangle = kalAngleZ;

232     

233     

234     send(roll,pitch,yaw);

235 //    send(gyroXangle,gyroYangle,gyroZangle);

236 //    send(compAngleX,compAngleY,compAngleZ);

237 //    send(kalAngleX,kalAngleY,kalAngleZ);

238 //    send(kalAngleY,compAngleY,gyroYangle);

239 

240 

241     /* Print Data */

242 //    //#if 1

243 //    printf("%lf %lf %lf %lf\n",roll,gyroXangle,compAngleX,kalAngleX);

244 //    printf("%lf %lf %lf %lf\n",pitch,gyroYangle,compAngleY,kalAngleY);

245 //    printf("%lf %lf %lf %lf\n",yaw,gyroZangle,compAngleZ,kalAngleZ);

246     //#endif

247     

248 //    //#if 0 // Set to 1 to print the IMU data

249 //    printf("%lf %lf %lf\n",accX / 16384.0,accY / 16384.0,accZ / 16384.0);

250 //    printf("%lf %lf %lf\n",gyroXrate,gyroYrate,gyroZrate);

251 //    printf("%lf %lf %lf\n",magX,magY,magZ);

252     //#endif

253     

254     //#if 0 // Set to 1 to print the temperature

255     //Serial.print("\t");

256     //

257     //double temperature = (double)tempRaw / 340.0 + 36.53;

258     //Serial.print(temperature); Serial.print("\t");

259     //#endif

260 //    delay(10);

261 } 

262 

263 //****************************************

264 //根据加速计刷新Pitch和Roll数据

265 //这里采用两种方法计算roll和pitch,如果最上面没有#define RESTRICT_PITCH就采用第二种计算方法

266 //****************************************

267 void updatePitchRoll() {

268     // Source: http://www.freescale.com/files/sensors/doc/app_note/AN3461.pdf eq. 25 and eq. 26

269     // atan2 outputs the value of -π to π (radians) - see http://en.wikipedia.org/wiki/Atan2

270     // It is then converted from radians to degrees

271     #ifdef RESTRICT_PITCH // Eq. 25 and 26

272     roll = atan2(accY,accZ) * RAD_TO_DEG;

273     pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;

274     #else // Eq. 28 and 29

275     roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;

276     pitch = atan2(-accX, accZ) * RAD_TO_DEG;

277     #endif

278 }

279 //****************************************

280 //根据磁力计刷新Yaw角

281 //****************************************

282 void updateYaw() { // See: http://www.freescale.com/files/sensors/doc/app_note/AN4248.pdf

283     double rollAngle,pitchAngle,Bfy,Bfx;  

284     

285     magX *= -1; // Invert axis - this it done here, as it should be done after the calibration

286     magZ *= -1;

287     

288     magX *= magGain[0];

289     magY *= magGain[1];

290     magZ *= magGain[2];

291     

292     magX -= magOffset[0];

293     magY -= magOffset[1];

294     magZ -= magOffset[2];

295     

296     

297     rollAngle  = kalAngleX * DEG_TO_RAD;

298     pitchAngle = kalAngleY * DEG_TO_RAD;

299     

300     Bfy = magZ * sin(rollAngle) - magY * cos(rollAngle);

301     Bfx = magX * cos(pitchAngle) + magY * sin(pitchAngle) * sin(rollAngle) + magZ * sin(pitchAngle) * cos(rollAngle);

302     yaw = atan2(-Bfy, Bfx) * RAD_TO_DEG;

303     

304     yaw *= -1;

305 }



程序说明:



 1 int main(void)

 2 {

 3       RCC_Configuration();                   //系统时钟配置    

 4       USART1_Configuration();

 5       I2C_GPIO_Config();

 6       InitHMC5883();

 7     InitMPU6050();

 8     InitAll();    

 9 //    SYSTICK_Configuration();                

10      while(1)

11     {

12         func();

13       }

14 }


主函数首先初始化系统时钟、串口、I2C总线、HMC5883磁力计和MPU6050加速计&陀螺仪,这里重点讲InitAll()函数和func()函数


 1 void InitAll()

 2 {

 3     /* Set Kalman and gyro starting angle */

 4     updateMPU6050();

 5     updateHMC5883();

 6     updatePitchRoll();

 7     updateYaw();

 8     

 9     setAngle(&kalmanX,roll); // First set roll starting angle

10     gyroXangle = roll;

11     compAngleX = roll;

12     

13     setAngle(&kalmanY,pitch); // Then pitch

14     gyroYangle = pitch;

15     compAngleY = pitch;

16     

17     setAngle(&kalmanZ,yaw); // And finally yaw

18     gyroZangle = yaw;

19     compAngleZ = yaw;

20     

21 //    timer = micros; // Initialize the timer    

22 }


第4、5两行从传感器中读取原数据,第6行函数根据加速计的值由空间几何的知识刷新Pitch和Roll数据,第7行函数根据复杂计算(这个实在看不懂,大概是磁力计有偏差,一方面进行误差校正,另一方面还用到了kalman滤波的数据,挺麻烦的)其实就是刷新yaw的值。

后面把kalman滤波值、陀螺仪计量值、磁力计计算值都赋值为上面计算的roll、pitch、yaw的值。


 1 void func()

 2 {

 3     double gyroXrate,gyroYrate,gyroZrate,dt=0.01;

 4     /* Update all the IMU values */

 5     updateMPU6050();

 6     updateHMC5883();

 7     

 8 //    dt = (double)(micros - timer) / 1000; // Calculate delta time

 9 //    timer = micros;

10 //    if(dt<0)dt+=(1<<20);    //时间是周期性的,有可能当前时间小于上次时间,因为这个周期远大于两次积分时间,所以最多相差1<<20

11 

12     /* Roll and pitch estimation */

13     updatePitchRoll();             //用采集的加速计的值计算roll和pitch的值

14     gyroXrate = gyroX / 131.0;     // Convert to deg/s    把陀螺仪的角加速度按照当初设定的量程转换为°/s

15     gyroYrate = gyroY / 131.0;     // Convert to deg/s

16     

17     #ifdef RESTRICT_PITCH        //如果上面有#define RESTRICT_PITCH就采用这种方法计算,防止出现-180和180之间的跳跃

18     // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees

19     if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) {

20         setAngle(&kalmanX,roll);

21         compAngleX = roll;

22         kalAngleX = roll;

23         gyroXangle = roll;

24     } else

25     kalAngleX = getAngle(&kalmanX, roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

26     

27     if (fabs(kalAngleX) > 90)

28         gyroYrate = -gyroYrate; // Invert rate, so it fits the restricted accelerometer reading

29     kalAngleY = getAngle(&kalmanY,pitch, gyroYrate, dt);

30     #else

31     // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees

32     if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {

33         kalmanY.setAngle(pitch);

34         compAngleY = pitch;

35         kalAngleY = pitch;

36         gyroYangle = pitch;

37     } else

38     kalAngleY = getAngle(&kalmanY, pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter

39     

40     if (abs(kalAngleY) > 90)

41         gyroXrate = -gyroXrate; // Invert rate, so it fits the restricted accelerometer reading

42     kalAngleX = getAngle(&kalmanX, roll, gyroXrate, dt); // Calculate the angle using a Kalman filter

43     #endif

44     

45     

46     /* Yaw estimation */

47     updateYaw();

48     gyroZrate = gyroZ / 131.0; // Convert to deg/s

49     // This fixes the transition problem when the yaw angle jumps between -180 and 180 degrees

50     if ((yaw < -90 && kalAngleZ > 90) || (yaw > 90 && kalAngleZ < -90)) {

51         setAngle(&kalmanZ,yaw);

52         compAngleZ = yaw;

53         kalAngleZ = yaw;

54         gyroZangle = yaw;

55     } else

56     kalAngleZ = getAngle(&kalmanZ, yaw, gyroZrate, dt); // Calculate the angle using a Kalman filter

57     

58     

59     /* Estimate angles using gyro only */

60     gyroXangle += gyroXrate * dt; // Calculate gyro angle without any filter

61     gyroYangle += gyroYrate * dt;

62     gyroZangle += gyroZrate * dt;

63     //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate from the Kalman filter

64     //gyroYangle += kalmanY.getRate() * dt;

65     //gyroZangle += kalmanZ.getRate() * dt;

66     

67     /* Estimate angles using complimentary filter */互补滤波算法

68     compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Calculate the angle using a Complimentary filter

69     compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

70     compAngleZ = 0.93 * (compAngleZ + gyroZrate * dt) + 0.07 * yaw;

71     

72     // Reset the gyro angles when they has drifted too much

73     if (gyroXangle < -180 || gyroXangle > 180)

74         gyroXangle = kalAngleX;

75     if (gyroYangle < -180 || gyroYangle > 180)

76         gyroYangle = kalAngleY;

77     if (gyroZangle < -180 || gyroZangle > 180)

78         gyroZangle = kalAngleZ;

79     

80     

81     send(roll,pitch,yaw);

82 //    send(gyroXangle,gyroYangle,gyroZangle);

83 //    send(compAngleX,compAngleY,compAngleZ);

84 //    send(kalAngleX,kalAngleY,kalAngleZ);

85 //    send(kalAngleY,compAngleY,gyroYangle);

86 } 


5、6两行获取传感器原数据

8~10行计算两次测量的时间差dt[因为我采用很多方法试验来计算时间差都不奏效,所以最终还是放弃了这种算法,还是用我原来的DMP算法,DMP对水平方向的很好,z方向的不好,要用磁力计来纠正!可以参考这里面的算法!]

13~56行是用kalman滤波来求当前的3个角并稳值

60~62行是用陀螺仪的角速度积分获得当前陀螺仪测量的3个角度值

67~70行使用互补滤波算法对磁力计当前测量3个角的值进行计算

72~78行是稳值

81行是串口发送

PS:总的来说按照arduino的代码进行照抄移植成c语言版的,当前卡在了如何较为准确的计算dt,即:两次测量的时间差(这里为了测试我给了dt一个定值0.01s,这是很不准确的做法!!!)[我采用定时器的方法总是会莫名的跑偏,我想可能是中断的影响,好吧,还是用原来实验的DMP吧,这个算法看似高大上,其实比较占MCU的资源啦,自带的DMP也存在一些缺陷,对水平方向的偏角测量较为精准,误差在1°左右,而在z轴方向上的误差较大,容易跑偏,所以还要用磁力计进行纠正Z轴的测量值~]





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机的函数发生器的设计