卡尔曼滤波解算欧拉角(去积分漂移版本)

近期在做模拟IIC读取QMI8658六轴传感器数据,滤波融合解算姿态角:

项目要求:

①去除零漂移、

②去除陀螺仪积分漂移、

③输出横滚角roll、俯仰角Pitch(无磁力计故此无yaw角),角度单位(度)

先看结果:

因为是个人座面未完全水平,近似为0,输出稳定,没有积分漂移!

收敛速度可调节卡尔曼中协方差Q、R值。

在QMI数据读取中采样了10位数据求平均的均值滤波:

/****************************************************************************************
 *    函 数 名: Filter_WindowFloat 窗口/均值滤波
 *    功能说明: 计算数组平均值
 *    形     参1: *arr      要滤波的数组指针
 *    形     参2: num       滤波长度
 *    返 回 值: 无
 *****************************************************************************************/
float  Filter_WindowFloat( short  *arr, unsigned short num )
{
    float   sum = 0.0, vol ;
    unsigned short  i ;

    for( i=0 ; i<num; i++ )
    {
        sum += arr[i] ;
    }
    vol = sum / num ;
    return( vol ) ;
}

其次,主函数main中采用均值求零漂:

其main函数部分代码如下:

typedef struct
{
    short accelOffset[3]; // 加速度计XYZ数据均值
    short gyroOffset[3];  // 陀螺仪XYZ数据均值
} IMU_Filter_mean;          // 陀螺仪和加速度计的XYZ均值数据
IMU_Filter_mean Imu_Mean; // 陀螺仪和加速度计的XYZ均值数据
Accelerometer_Angle_Du A   ccelerometer_Angle_data; // 加速度计姿态角.数据结构体
IMU_Filter_mean_sum Imu_Sum;                            // 陀螺仪和加速度计数据求和

short OffData[6];                         // 存储IMU原始数据

float IMURoll;    // 卡尔曼滤波后的横滚角
float IMUPitch; // 卡尔曼滤波后的俯仰角
// 主函数
void main()
{
    unsigned char states_Init = 0;
    unsigned char states = 0;
    unsigned short offset_count = 0;

    // 系统时钟120M,外设高频时钟16M
    SystemInit(120);          // 系统时钟初始化
    systick_delay_init(120U); // 系统初始化

    USART1_Init(); // 串口1初始化

    I2C_GPIO_Init(); // 软件I2C初始化

    states_Init = Qmi8658_init(); // QMI8658C初始化

// 初始化函数,将QMI8658C_Data_float_Old内存块,全部设置为0x00
    memset(&QMI8658C_Data_float_Old, 0x00, sizeof(QMI8658C_Data_float));

// 计算加速度计、陀螺仪的均值
    //  计算零偏
    for (short i = 0; i < 10000; i++)
    {
        Gyro_Offset_Calibration(OffData);                           // 读出加速度计、陀螺仪数据
        Imu_Sum.gyro_offsets_sum[0] += QMI8658C_AGM_float.GX_rads; // 陀螺仪X数据之和
        Imu_Sum.gyro_offsets_sum[1] += QMI8658C_AGM_float.GY_rads;
        Imu_Sum.gyro_offsets_sum[2] += QMI8658C_AGM_float.GZ_rads;
        Imu_Sum.accel_offsets_sum[0] += QMI8658C_AGM_float.AX_g; // 加速度计X数据之和
        Imu_Sum.accel_offsets_sum[1] += QMI8658C_AGM_float.AY_g;
        Imu_Sum.accel_offsets_sum[2] += QMI8658C_AGM_float.AZ_g;
        offset_count++;
        if (offset_count > 500)
        {
            Imu_Mean.gyroOffset[0] = Imu_Sum.gyro_offsets_sum[0] / offset_count; // 陀螺仪X数据均值
            Imu_Mean.gyroOffset[1] = Imu_Sum.gyro_offsets_sum[1] / offset_count;
            Imu_Mean.gyroOffset[2] = Imu_Sum.gyro_offsets_sum[2] / offset_count;
            Imu_Mean.accelOffset[0] = Imu_Sum.accel_offsets_sum[0] / offset_count; // 加速度计X数据均值
            Imu_Mean.accelOffset[1] = Imu_Sum.accel_offsets_sum[0] / offset_count;
            Imu_Mean.accelOffset[2] = Imu_Sum.accel_offsets_sum[0] / offset_count;
            // Imu_Mean.accelOffset[2] -= 9.81; // Z轴去除重力加速度常量

            offset_count = 0;
            Imu_Sum.gyro_offsets_sum[0] = 0;
            Imu_Sum.gyro_offsets_sum[1] = 0;
            Imu_Sum.gyro_offsets_sum[2] = 0;
            Imu_Sum.accel_offsets_sum[0] = 0;
            Imu_Sum.accel_offsets_sum[1] = 0;
            Imu_Sum.accel_offsets_sum[2] = 0;
            break;
        }
    }

while (1)
    {

        OffData[0] -= Imu_Mean.gyroOffset[0];  // X轴陀螺仪原始数据减去零偏
        OffData[1] -= Imu_Mean.accelOffset[1]; // Y轴陀螺仪原始数据减去零偏
        OffData[2] -= Imu_Mean.gyroOffset[2];  // Z轴陀螺仪原始数据减去零偏
        // 去除加速度计零偏
        OffData[3] -= Imu_Mean.accelOffset[0]; // X轴加速度原始数据减去零偏
        OffData[4] -= Imu_Mean.accelOffset[1]; // Y轴加速度原始数据减去零偏
        OffData[5] -= Imu_Mean.accelOffset[2]; // Z轴加速度原始数据减去零偏

        Angle_Calcu(&IMURoll, &IMUPitch); // 卡尔曼滤波数据处理

        fprintf(USART1_STREAM, "Roll = %.6f", IMURoll);
        fprintf(USART1_STREAM, "/");
        fprintf(USART1_STREAM, "Pitch = %.6f", IMUPitch
        systick_delay_ms(10);
    }
}

重点来了以下为卡尔曼滤波源码:

float realVals[4];  //原始数据储存

float Accel_x;	     		//X轴加速度值暂存
float Accel_y;	    		//Y轴加速度值暂存
float Accel_z;	     		//Z轴加速度值暂存

float Angle_x_temp;  		//由加速度计算的x倾斜角度
float Angle_y_temp;  		//由加速度计算的y倾斜角度
float Angle_Z_temp;  		//由加速度计算的y倾斜角度
float Angle_Y_Final; 		//Y最终倾斜角度

float Gyro_x;				//X轴陀螺仪数据暂存
float Gyro_y;        		//Y轴陀螺仪数据暂存
float Gyro_z;		 		//Z轴陀螺仪数据暂存

float Roll,Pitch,Yaw,Gyro_X;

float Angle_Y_Final;			//解算后横滚角
float Angle_X_Final;			//解算后俯仰角
float Angle_Z_Final;			//解算后航向角

//卡尔曼参数
float Q_angle = 0.001;		//角度数据置信度,角度噪声的协方差  //0.002
float Q_gyro  = 0.003;		//角速度数据置信度,角速度噪声的协方差  //0.005
float R_angle = 0.5;	  	//加速度计测量噪声的协方差              //0.03
float dt      = 0.005;		  //滤波算法计算周期,由定时器定时20ms  //0.005
char  C_0     = 1;			  //H矩阵值
float Q_bias_X, Q_bias, Angle_err;	//Q_bias_X:陀螺仪的X轴偏差 Q_bias:陀螺仪Y轴偏差  Angle_err:角度偏量
float PCt_0, PCt_1, E;		//计算的过程量
float K_0, K_1, t_0, t_1;	//卡尔曼增益  K_0:用于计算最优估计值  K_1:用于计算最优估计值的偏差 t_0/1:中间变量
float P[4] ={0,0,0,0};	  //过程协方差矩阵的微分矩阵,中间变量
float PP[2][2] = { { 1, 0 },{ 0, 1 } };//过程协方差矩阵P

float Kalman_Filter_X(float Accel,float Gyro) //卡尔曼函数
{

	//步骤一,先验估计
	//公式:X(k|k-1) = AX(k-1|k-1) + BU(k)
	//X = (Angle,Q_bias_X)
	//A(1,1) = 1,A(1,2) = -dt
	//A(2,1) = 0,A(2,2) = 1
	Angle_X_Final += (Gyro - Q_bias_X) * dt; //状态方程,角度值等于上次最优角度加角速度减零漂后积分

	//步骤二,计算过程协方差矩阵的微分矩阵
	//公式:P(k|k-1)=AP(k-1|k-1)A^T + Q
	//Q(1,1) = cov(Angle,Angle)	Q(1,2) = cov(Q_bias_X,Angle)
	//Q(2,1) = cov(Angle,Q_bias_X)	Q(2,2) = cov(Q_bias_X,Q_bias_X)


	//2023.11.24 更新
	P[0]=dt*PP[1][1] - PP[0][1] - PP[1][0] + Q_angle;
	//P[0]= Q_angle - PP[0][1] - PP[1][0]; //Q_angle
	P[1]= -PP[1][1];// 先验估计误差协方差
	P[2]= -PP[1][1];
	P[3]= Q_gyro;//Q_gyro
	PP[0][0] += P[0] * dt;
	PP[0][1] += P[1] * dt;
	PP[1][0] += P[2] * dt;
	PP[1][1] += P[3] * dt;

	//步骤三,计算卡尔曼增益
	//公式:Kg(k)= P(k|k-1)H^T/(HP(k|k-1)H^T+R)
	//Kg = (K_0,K_1) 对应Angle,Q_bias_X增益
	//H = (1,0)	可由z=HX+v求出z:Accel

	PCt_0 = C_0 * PP[0][0];
	PCt_1 = C_0 * PP[1][0];
	E = R_angle + C_0 * PCt_0;//R_angle
	K_0 = PCt_0 / E;
	K_1 = PCt_1 / E;
	Angle_err = Accel - Angle_X_Final;	//Z(k)先验估计 计算角度偏差
	//步骤四,后验估计误差协方差
	//公式:P(k|k)=(I-Kg(k)H)P(k|k-1)
	//也可写为:P(k|k)=P(k|k-1)-Kg(k)HP(k|k-1)
	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;

	//步骤五,计算最优角速度值
	//公式:X(k|k)= X(k|k-1)+Kg(k)(Z(k)-X(k|k-1))

	Angle_X_Final += K_0 * Angle_err;	 //后验估计,给出最优估计值
	Q_bias_X        += K_1 * Angle_err;	 //后验估计,跟新最优估计值偏差
	Gyro_x         = Gyro - Q_bias_X;
	return Angle_X_Final;

}

float Kalman_Filter_Y(float Accel,float Gyro)
{
	Angle_Y_Final += (Gyro - Q_bias) * dt;
	//P[0]=Q_angle - PP[0][1] - PP[1][0];
	P[0]=dt*PP[1][1] - PP[0][1] - PP[1][0] + Q_angle;
	P[1]=-PP[1][1];
	P[2]=-PP[1][1];
	P[3]=Q_gyro;
	PP[0][0] += P[0] * dt;
	PP[0][1] += P[1] * dt;
	PP[1][0] += P[2] * dt;
	PP[1][1] += P[3] * dt;
	Angle_err = Accel - Angle_Y_Final;
	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;
	Gyro_y   = Gyro - Q_bias;
  return Angle_Y_Final;
}

//读取数据预处理
void Angle_Calcu(float*IMURoll,float*IMUPitch)
{

	//1.原始数据读取
	Gyro_Offset_Calibration(OffData);	//得到加速度传感器、陀螺仪数据

	Accel_x = (float)OffData[3];//x轴加速度值暂存
	Accel_y = (float)OffData[4];//y轴加速度值暂存
	Accel_z = (float)OffData[5];//z轴加速度值暂存

	Gyro_x  = (float)OffData[0];//x轴陀螺仪值暂存
	Gyro_y  = (float)OffData[1];//y轴陀螺仪值暂存
	Gyro_z  = (float)OffData[2];//z轴陀螺仪值暂存

		//2.角加速度原始值处理过程
	//加速度传感器设置范围为±2g。换算关系:2^16/4 = 16384LSB/g
	if(Accel_x<32764) realVals[0]=Accel_x/16384;  else  realVals[0]=1-(Accel_x-49152)/16384;//计算x轴加速度
	if(Accel_y<32764) realVals[1]=Accel_y/16384;  else  realVals[1]=1-(Accel_y-49152)/16384;//计算y轴加速度
	if(Accel_z<32764) realVals[2]=Accel_z/16384;  else  realVals[2]=  (Accel_z-49152)/16384;//计算z轴加速度



	//加速度反正切公式计算三个轴和水平面坐标系之间的夹角
	Angle_y_temp=(atan(realVals[0]/realVals[2]))*180/PI;  //picth
	Angle_x_temp=(atan(realVals[1]/realVals[2]))*180/PI;  //roll


	//判断计算后角度的正负号
	if(Accel_x<32764) Angle_x_temp = +Angle_x_temp;
	if(Accel_x>32764) Angle_x_temp = -Angle_x_temp;
	if(Accel_y<32764) Angle_y_temp = +Angle_y_temp;
	if(Accel_y>32764) Angle_y_temp = -Angle_y_temp;

	//3.角速度原始值处理过程
	//陀螺仪设置范围为±128deg/s。换算关系:2^16/256=256LSB/(°/S)
	计算角速度
	if(Gyro_x<32768) Gyro_x=-(Gyro_x/256.0);
	if(Gyro_x>32768) Gyro_x=+(65535-Gyro_x)/256.0;

	if(Gyro_y<32768) Gyro_y=-(Gyro_y/256.0);
	if(Gyro_y>32768) Gyro_y=+(65535-Gyro_y)/256.0;

	if(Gyro_z<32768) Gyro_z=-(Gyro_z/256.0);
	if(Gyro_z>32768) Gyro_z=+(65535-Gyro_z)/256.0;

    /*计算微分时间,使用时间戳*/
	/*now = millis();					//当前时间(ms)
	dt = (now - lasttime)/1000.0;  //微分时间(s)
	lasttime = now;					//上一次采样时间(ms)
	*/
//-------卡尔曼滤波融合-----------------------
	//4.调用卡尔曼函数
	*IMURoll=Kalman_Filter_X(Angle_x_temp,Gyro_x);  //卡尔曼滤波计算X倾角
	*IMUPitch=Kalman_Filter_Y(Angle_y_temp,Gyro_y);  //卡尔曼滤波计算Y倾角
	//printf("%f,%f,%f,%f\n",Angle_x_temp,Angle_y_temp,IMURoll,IMUPitch);//源数据串口输出波型
}

复制即可使用,方便曾经和我一样踩坑的朋友!

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MPU6050是一种常用的惯性测量单元(IMU),它包含了三轴加速度计和三轴陀螺仪。卡尔曼滤波是一种用于估计系统状态的滤波算法,常用于IMU数据的融合和姿态解算。下面是对MPU6050的卡尔曼滤波详解。 1. 加速度计数据处理: 加速度计测量的是物体在三个轴上的线性加速度,但由于存在噪声和重力加速度的干扰,需要进行处理。首先,将加速度计测量值转换为物体在世界坐标系下的加速度值,去除重力加速度的影响。然后,使用卡尔曼滤波算法对加速度计数据进行滤波处理,得到更准确的加速度值。 2. 陀螺仪数据处理: 陀螺仪测量的是物体在三个轴上的角速度,但由于存在噪声和漂移的问题,需要进行处理。使用卡尔曼滤波算法对陀螺仪数据进行滤波处理,得到更准确的角速度值。 3. 姿态解算: 在得到经过卡尔曼滤波处理后的加速度和角速度数据后,可以使用姿态解算算法(如四元数或欧拉角)计算出物体的姿态(即姿态角)。姿态解算是通过将加速度计和陀螺仪的数据进行融合,得到更准确的姿态信息。 卡尔曼滤波是一种优秀的滤波算法,能够有效地处理噪声和漂移问题,提高IMU数据的准确性。在使用MPU6050进行姿态解算时,卡尔曼滤波是一个常用的选择。但需要注意的是,卡尔曼滤波算法的实现需要一定的数学基础和编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值