四元数姿态解算

四元数姿态解算——互补滤波
六轴

float q0=1, q1=0, q2=0, q3=0;
float exInt, eyInt, ezInt;
//*****************************************6轴融合*****************************************************
#define Kp 0.2f//2.0f //加速度权重,越大则向加速度测量值收敛越快
#define Ki 0.001f //误差积分增益
#define halfT (dt*0.5)
void IMUupdate(float gx, float gy, float gz, float ax, float ay, float az)
{
	float norm;
	float vx, vy, vz;
	float ex, ey, ez; 

         filted.roll_d=gy*RtoA;
         filted.pitch_d=gx*RtoA;
         filted.yaw_d=gz*RtoA;
 
	//加计值归一化处理normalise the measurements
	norm = sqrtf(ax*ax + ay*ay + az*az);      
	ax = ax / norm;
	ay = ay / norm;
	az = az / norm;      

	//建立飞控坐标系estimated direction of gravity
	vx = 2*(q1*q3 - q0*q2);
	vy = 2*(q0*q1 + q2*q3);
	vz = q0*q0 - q1*q1 - q2*q2 + q3*q3;

	//坐标系和重力叉积运算error is sum of cross product between reference direction of field and direction measured by sensor
	ex = (ay*vz - az*vy);
	ey = (az*vx - ax*vz);
	ez = (ax*vy - ay*vx);

	//比例运算integral error scaled integral gain
	exInt = exInt + ex*Ki;
	eyInt = eyInt + ey*Ki;
	ezInt = ezInt + ez*Ki;

	//陀螺仪积分融合 adjusted gyroscope measurements
	gx = gx + Kp*ex + exInt;
	gy = gy + Kp*ey + eyInt;
	gz = gz + Kp*ez + ezInt;
	//计算四元数 integrate quaternion rate and normalise
	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;  

	//四元数归一化处理normalise quaternion
	norm = sqrtf(q0*q0 + q1*q1 + q2*q2 + q3*q3);	
	q0 = q0 / norm;
	q1 = q1 / norm;
	q2 = q2 / norm;
	q3 = q3 / norm;
	
	//计算欧拉角
	filted.roll	=	-asinf(-2*q1*q3 + 2*q0*q2)*57.30f;//正负90度
	filted.pitch	=	-atan2f(2*q2*q3 + 2*q0*q1, -2*q1*q1-2*q2*q2 + 1)*57.30f;//正负180度
    filted.yaw= atan2f(2*q1*q2 - 2*q0*q3, -2 * q1 * q1 - 2 * q3 * q3 + 1) * 57.30f;  //乘以57.30是为了将弧度转化为角度
	//Current_yaw		=	Current_yaw-gz*Gyro_angle_scale;
        filted.cos_pitch=cosf(filted.pitch*AtoR);
        filted.sin_pitch=sinf(filted.pitch*AtoR);
        filted.cos_roll=cosf(filted.roll*AtoR);
        filted.sin_roll=sinf(filted.roll*AtoR);
        filted.cos_yaw=cosf(filted.yaw*AtoR);
        filted.sin_yaw=sinf(filted.yaw*AtoR);
}

九轴

//*********************************************9轴融合******************************************************
 void AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) 
 {
    float norm;
    float hx, hy, hz, bz,bx;// by;
    float vx, vy, vz, wx, wy, wz;
    float ex, ey, ez;

 /*方便之后的程序使用,减少计算时间*/
    float q0q0 = q0*q0;
    float q0q1 = q0*q1;
    float q0q2 = q0*q2;
    float q0q3 = q0*q3;
    float q1q1 = q1*q1;
    float q1q2 = q1*q2;
    float q1q3 = q1*q3;
    float q2q2 = q2*q2;   
    float q2q3 = q2*q3;
    float q3q3 = q3*q3;
           
    
    
         filted.roll_d=gy*57.295779514719953173585430909526f;
         filted.pitch_d=-gx*57.295779514719953173585430909526f;
         filted.yaw_d=-gz*57.295779514719953173585430909526;
         
 /*归一化测量值,加速度计和磁力计的单位是什么都无所谓,因为它们在此被作了归一化处理*/        
    norm = sqrtf(ax*ax + ay*ay + az*az);       
    ax = ax / norm;
    ay = ay / norm;
    az = az / norm;
    norm = sqrtf(mx*mx + my*my + mz*mz);          
    mx = mx / norm;
    my = my / norm;
    mz = mz / norm;         
         
 /*从机体坐标系的电子罗盘测到的矢量转成地理坐标系下的磁场矢量h-xyz(测量值),下面这个是从飞行器坐标系到地理坐标系的转换公式*/
    hx = 2*mx*(0.5 - q2q2 - q3q3) + 2*my*(q1q2 - q0q3) + 2*mz*(q1q3 + q0q2);
    hy = 2*mx*(q1q2 + q0q3) + 2*my*(0.5 - q1q1 - q3q3) + 2*mz*(q2q3 - q0q1);
    hz = 2*mx*(q1q3 - q0q2) + 2*my*(q2q3 + q0q1) + 2*mz*(0.5 - q1q1 - q2q2);

 /*计算地理坐标系下的磁场矢量b-xyz(参考值)。因为地理地磁水平夹角,我们已知是0度(抛去磁偏角的因素,固定向北),
 我定义b-y指向正北,所以by=某值,bx=0但地理参考地磁矢量在垂直面上也有分量bz,地球上每个地方都是不一样的。
 我们无法得知,也就无法用来融合(有更适合做垂直方向修正融合的加速度计),所以直接从测量值hz上复制过来,bz=hz。
 磁场水平分量,参考值和测量值的大小应该是一致的(bx*bx) + (by*by)) = ((hx*hx) + (hy*hy))。
 因为bx=0,所以就简化成(by*by)  = ((hx*hx) + (hy*hy))。可算出by。下面可以修改b-y和b-x那个轴指向正北。*/
 	bx = sqrtf((hx*hx) + (hy*hy));
 //   by = sqrtf((hx*hx) + (hy*hy));
 	bz = hz;        
     
 //下面的公式是从地理坐标系到飞控坐标系的转化
 	vx = 2*(q1q3 - q0q2);
 	vy = 2*(q0q1 + q2q3);
 	vz = q0q0 - q1q1 - q2q2 + q3q3;

 /*我们把地理坐标系上的磁场矢量b-xyz,转到机体上来w-xyz。
 因为bx/y=0,所以所有涉及到bx/y的部分都被省略了,这根据自己定义那个轴指北有关。
 类似上面重力v-xyz的推算,因为重力g的az=1,ax=ay=0,所以上面涉及到gxgy的部分也被省略了
 你可以看看两个公式:w-xyz的公式,把by换成ay(0),把bz换成az(1),就变成了v-xyz的公式了(其中q0q0+q1q1+q2q2+q3q3=1)。*/
 //x轴对准北方
 	wx = 2*bx*(0.5 - q2q2 - q3q3) + 2*bz*(q1q3 - q0q2);
 	wy = 2*bx*(q1q2 - q0q3) + 2*bz*(q0q1 + q2q3);
 	wz = 2*bx*(q0q2 + q1q3) + 2*bz*(0.5 - q1q1 - q2q2);
 //y轴对准北方
 //   wx = 2*by*(q1q2 + q0q3) + 2*bz*(q1q3 - q0q2);
 //   wy = 2*by*(0.5 - q1q1 - q3q3) + 2*bz*(q0q1 + q2q3);
 //   wz = 2*by*(q2q3 - q0q1) + 2*bz*(0.5 - q1q1 - q2q2);
            
 //现在把加速度的测量矢量和参考矢量做叉积,把磁场的测量矢量和参考矢量也做叉积。都拿来来修正陀螺。
 	ex = (ay*vz - az*vy) + (my*wz - mz*wy);
 	ey = (az*vx - ax*vz) + (mz*wx - mx*wz);
 	ez = (ax*vy - ay*vx) + (mx*wy - my*wx);

 //积分计算,乘以采样周期的一半

 	exInt = exInt + ex*Ki; //* halfT;			  
 	eyInt = eyInt + ey*Ki;// * halfT;
 	ezInt = ezInt + ez*Ki;// * halfT;
 			
 //补偿陀螺仪PI计算
 	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 = sqrtf(q0*q0 + q1*q1 + q2*q2 + q3*q3);
 	q0 = q0 / norm;       //w
 	q1 = q1 / norm;       //x
 	q2 = q2 / norm;       //y
 	q3 = q3 / norm;       //z
 		
          

 	filted.yaw= atan2f(2*q1*q2 - 2*q0*q3, -2 * q1 * q1 - 2 * q3 * q3 + 1) * 57.30f;  //乘以57.30是为了将弧度转化为角度
 	filted.pitch= -asinf(2*q2*q3 + 2*q0*q1) * 57.30f;																	 //正负180度	
 	filted.roll= -atan2f(-2*q0*q2 + 2*q1*q3, -2 * q1 * q1 - 2 * q2* q2 + 1) * 57.30f;
 	
        filted.yaw_mag=atan2f(my,mx)*RtoA;
 	
 }

四元数姿态解算——梯度下降法`


void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
    float recipNorm;
    float s0, s1, s2, s3;
    float qDot1, qDot2, qDot3, qDot4;
    float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;
    
#define sampleFreq  200.0f      // sample frequency in Hz
#define betaDef     0.1f        // 2 * proportional gain



    static float beta = betaDef;                              // 2 * proportional gain (Kp)
    static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;  // quaternion of sensor frame relative to auxiliary fram

    // Rate of change of quaternion from gyroscope
    qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
    qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
    qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
    qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);

    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
    if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {

        // Normalise accelerometer measurement
        recipNorm = 1/sqrt(ax * ax + ay * ay + az * az);
        ax *= recipNorm;
        ay *= recipNorm;
        az *= recipNorm;   

        // Auxiliary variables to avoid repeated arithmetic
        _2q0 = 2.0f * q0;
        _2q1 = 2.0f * q1;
        _2q2 = 2.0f * q2;
        _2q3 = 2.0f * q3;
        _4q0 = 4.0f * q0;
        _4q1 = 4.0f * q1;
        _4q2 = 4.0f * q2;
        _8q1 = 8.0f * q1;
        _8q2 = 8.0f * q2;
        q0q0 = q0 * q0;
        q1q1 = q1 * q1;
        q2q2 = q2 * q2;
        q3q3 = q3 * q3;

        // Gradient decent algorithm corrective step
        s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
        s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
        s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
        s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
        recipNorm = 1/sqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
        s0 *= recipNorm;
        s1 *= recipNorm;
        s2 *= recipNorm;
        s3 *= recipNorm;

        // Apply feedback step
        qDot1 -= beta * s0;
        qDot2 -= beta * s1;
        qDot3 -= beta * s2;
        qDot4 -= beta * s3;
    }

    // Integrate rate of change of quaternion to yield quaternion
    q0 += qDot1 * (1.0f / sampleFreq);
    q1 += qDot2 * (1.0f / sampleFreq);
    q2 += qDot3 * (1.0f / sampleFreq);
    q3 += qDot4 * (1.0f / sampleFreq);

    // Normalise quaternion
    recipNorm = 1/sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;
    
    
    //计算欧拉角
    filted.roll	=	-asinf(-2*q1*q3 + 2*q0*q2)*57.30f;//正负90度
    filted.pitch	=	-atan2f(2*q2*q3 + 2*q0*q1, -2*q1*q1-2*q2*q2 + 1)*57.30f;//正负180度
     //atan2f(2*q1*q2 - 2*q0*q3, -2 * q1 * q1 - 2 * q3 * q3 + 1) * 57.30f;  //乘以57.30是为了将弧度转化为角度
    //Current_yaw		=	Current_yaw-gz*Gyro_angle_scale;
    filted.cos_pitch=cosf(filted.pitch*AtoR);
    filted.sin_pitch=sinf(filted.pitch*AtoR);
    filted.cos_roll=cosf(filted.roll*AtoR);
    filted.sin_roll=sinf(filted.roll*AtoR);
    filted.cos_yaw=cosf(filted.yaw*AtoR);
    filted.sin_yaw=sinf(filted.yaw*AtoR);
     filted.roll_d=gy*RtoA;
     filted.pitch_d=gx*RtoA;
     filted.yaw_d=gz*RtoA;
     if(fabsf(filted.yaw_d)>0.15){
      filted.yaw+=0.005*gz*RtoA;
     }
}

上文详细描述了由Madgwick提出四元数AHRS姿态解算和IMU姿态解算方法,如需转载,请勿用于商业目的,并注明:该文章出自圆点博士无人机www.bspilot.com

你可能感兴趣的:(算法)