BaseFlight IMU - FabLabSeoul/WingProject GitHub Wiki

IMU (Inertia Measurement Unit)

이 글은 Multiwii Baseflight μ˜€ν”ˆμ†ŒμŠ€λ₯Ό λΆ„μ„ν•œ 글이닀.

Multiwii Baseflightλ₯Ό Stm32 Value line discovery λ³΄λ“œμ— 맞게 μ½”λ“œλ₯Ό μž¬μž‘μ„±ν•˜μ˜€λ‹€.

Stm32 Value line discovery λ³΄λ“œλŠ” 24MHz 속도가 μ΅œλŒ€μ΄κΈ° λ•Œλ¬Έμ—, 72MHz μ†λ„μ˜ Naze32λ³΄λ“œ ν™˜κ²½μ—μ„œ ν”„λ‘œκ·Έλž˜λ°λœ Baseflight μ†ŒμŠ€κ°€ λ¬Έμ œκ°€ λ°œμƒν•  여지가 μžˆλ‹€. 특히 μ‹œμŠ€ν…œ ν΄λŸ­μ„ μ΄μš©ν•˜λŠ” micros() ν•¨μˆ˜μ™€ millis()ν•¨μˆ˜κ°€ 문제인데, ν˜„μž¬ μ‹œμ μ—μ„œ micros() ν•¨μˆ˜κ°€ 잘 μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” κ±Έ 확인 ν–ˆλ‹€. μ‘°λ§Œκ°„ 문제λ₯Ό ν•΄κ²°ν•˜κ³ , 글을 μ“Έ 생각이닀.

Baseflight μ—μ„œ imu.c λΌμ΄λΈŒλŸ¬λ¦¬λŠ” Sensor 라이브러리λ₯Ό μ΄μš©ν•΄, λ©€ν‹°μ½₯ν„°μ˜ Roll,Pitch,Yaw각을 κ³„μ‚°ν•œλ‹€. imu 라이브러리의 μ£Όμš”ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • imuInit()
    • IMU μ΄ˆκΈ°ν™”
  • computeIMU()
    • 가속도,자이둜,μ§€μžκΈ° μ„Όμ„œ 값을 μ½μ–΄μ˜€κ³ , getEstimatedAttitude() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.
  • getEstimatedAttitude()
    • 가속도, μžμ΄λ‘œμŠ€μ½”ν”„, μ§€μžκΈ° μ„Όμ„œ 값을 μ΄μš©ν•΄, λ©€ν‹°μ½₯ν„°μ˜ Roll,Pitch,Yaw각을 κ³„μ‚°ν•œλ‹€.

imuInit() ν•¨μˆ˜

void imuInit(void)
{
    smallAngle = lrintf(acc_1G * cosf(RAD * cfg.small_angle));
    accVelScale = 9.80665f / acc_1G / 10000.0f;
    throttleAngleScale = (1800.0f / M_PI) * (900.0f / cfg.throttle_correction_angle);

    fc_acc = 0.5f / (M_PI * cfg.accz_lpf_cutoff); // calculate RC time constant used in the accZ lpf

    Mag_init();
}

lrintf()ν•¨μˆ˜λŠ” float 값을 μ†Œμˆ˜μ μ΄ν•˜ 값을 μž˜λΌμ„œ intν˜•μœΌλ‘œ λ³€ν™˜ ν•œλ‹€.

computeIMU() ν•¨μˆ˜

Baseflight imu::computeIMU() ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

void computeIMU(void)
{
    Gyro_getADC();
    ACC_getADC();
    getEstimatedAttitude();

    gyroData[YAW] = gyroADC[YAW];
    gyroData[ROLL] = gyroADC[ROLL];
    gyroData[PITCH] = gyroADC[PITCH];
}

가속도, μžμ΄λ‘œμŠ€μ½”ν”„ μ„Όμ„œ 값을 μ½μ–΄μ˜¨ ν›„, getEstimatedAttitude()ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.

getEstimatedAttitude() ν•¨μˆ˜

getEstimatedAttitude() ν•¨μˆ˜μ—μ„œλŠ” accADC[], gyroADC[], magADC[] λ³€μˆ˜λ₯Ό ν† λŒ€λ‘œ λ©€ν‹°μ½₯ν„°μ˜ Roll,Pitch,Yaw 각을 κ³„μ‚°ν•œλ‹€.

전체 μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€. ν•˜λ‚˜ν•˜λ‚˜μ”© λΆ„μ„ν•΄λ³΄μž.

static void getEstimatedAttitude(void)
{
    int32_t axis;
    int32_t accMag = 0;
    static t_fp_vector EstM;
    static t_fp_vector EstN = { .A = { 1.0f, 0.0f, 0.0f } };
    static float accLPF[3];
    static uint32_t previousT;
    uint32_t currentT = micros();
    uint32_t deltaT;
    float scale, deltaGyroAngle[3];
    deltaT = currentT - previousT;
    scale = deltaT * gyro.scale;
	
	// TO: dont understand
	// μ‹œκ°„μ΄ μ΄ˆκΈ°ν™” λ˜λŠ” 버그 λ•Œλ¬Έμ— 좔가함.
	// μ΄μœ λŠ” 아직 λͺ¨λ₯΄κ² μŒ.
	// ν˜„μž¬μ‹œκ°„κ³Ό μ „ μ‹œκ°„ 차이가 λ„ˆλ¬΄ 크면 μ—°μ‚°ν•˜μ§€ μ•ŠλŠ”λ‹€.
	if (currentT - previousT > 10000) // 10ms over
	{
	    previousT = currentT;
		return;
	}

    previousT = currentT;	
	
    // Initialization
	for (axis = 0; axis < 3; axis++)
	{
        deltaGyroAngle[axis] = gyroADC[axis] * scale;
        if (cfg.acc_lpf_factor > 0) 
		{
            accLPF[axis] = accLPF[axis] * (1.0f - (1.0f / cfg.acc_lpf_factor)) + accADC[axis] * (1.0f / cfg.acc_lpf_factor);
            accSmooth[axis] = accLPF[axis];
        } 
		else 
		{
            accSmooth[axis] = accADC[axis];
        }
				
        accMag += (int32_t)accSmooth[axis] * accSmooth[axis];
    }
    accMag = accMag * 100 / ((int32_t)acc_1G * acc_1G);

    rotateV(&EstG.V, deltaGyroAngle);

    // Apply complimentary filter (Gyro drift correction)
    // If accel magnitude >1.15G or <0.85G and ACC vector outside of the limit range => we neutralize the effect of accelerometers in the angle estimation.
    // To do that, we just skip filter, as EstV already rotated by Gyro
    if (72 < (uint16_t)accMag && (uint16_t)accMag < 133) {
        for (axis = 0; axis < 3; axis++)
            EstG.A[axis] = (EstG.A[axis] * (float)mcfg.gyro_cmpf_factor + accSmooth[axis]) * INV_GYR_CMPF_FACTOR;
    }

    f.SMALL_ANGLE = (EstG.A[Z] > smallAngle);

    // Attitude of the estimated vector
    anglerad[ROLL] = atan2f(EstG.V.Y, EstG.V.Z);
    anglerad[PITCH] = atan2f(-EstG.V.X, sqrtf(EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z));
    angle[ROLL] = lrintf(anglerad[ROLL] * (1800.0f / M_PI));
    angle[PITCH] = lrintf(anglerad[PITCH] * (1800.0f / M_PI));

	if (0)
	{
        rotateV(&EstM.V, deltaGyroAngle);
       for (axis = 0; axis < 3; axis++)
            EstM.A[axis] = (EstM.A[axis] * (float)mcfg.gyro_cmpfm_factor + magADC[axis]) * INV_GYR_CMPFM_FACTOR;
       heading = calculateHeading(&EstM);
    } 
	else 
	{
        rotateV(&EstN.V, deltaGyroAngle);
        normalizeV(&EstN.V, &EstN.V);
        heading = calculateHeading(&EstN);
	}

    acc_calc(deltaT); // rotate acc vector into earth frame

    if (cfg.throttle_correction_value) 
	{
        float cosZ = EstG.V.Z / sqrtf(EstG.V.X * EstG.V.X + EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z);

        if (cosZ <= 0.015f) 
		{ // we are inverted, vertical or with a small angle < 0.86 deg
            throttleAngleCorrection = 0;
        } 
		else 
		{
            int deg = lrintf(acosf(cosZ) * throttleAngleScale);
            if (deg > 900)
                deg = 900;
            throttleAngleCorrection = lrintf(cfg.throttle_correction_value * sinf(deg / (900.0f * M_PI / 2.0f)));
        }
    }		
}

###1. deltaT 계산 Stme32 Value line discovery λ³΄λ“œμ˜ 속도 λ•Œλ¬Έμ—, micros()ν•¨μˆ˜κ°€ 쀑간에 μ΄ˆκΈ°ν™” λ˜λŠ” λ¬Έμ œκ°€ μžˆλ‹€. κ·Έλž˜μ„œ μ „ μ‹œκ°„κ³Ό λ„ˆλ¬΄ 많이 μ°¨μ΄λ‚˜λ©΄ λ¬΄μ‹œν•˜κ²Œ ν•˜λŠ” μ½”λ“œκ°€ μΆ”κ°€ λ˜μ—ˆλ‹€. μ–΄λ””κΉŒμ§€λ‚˜ μž„μ‹œλ°©νŽΈ μ½”λ“œμ΄κΈ° λ•Œλ¬Έμ—, μˆ˜μ •ν•΄μ•Ό ν•  μ½”λ“œλ‹€.

uint32_t currentT = micros();
uint32_t deltaT;
deltaT = currentT - previousT;

if (currentT - previousT > 10000) // 10ms over
{
    previousT = currentT;
    return;
}

###2. 가속도, μžμ΄λ‘œμŠ€μ½”ν”„ μ„Όμ„œ 값을 κ°€μ Έμ˜¨λ‹€. gyroADC[axis] * scale; μ½”λ“œλ₯Ό λ‹€μ‹œ μ“°λ©΄ gyroADC[axis] * (4.0f / 16.4f) * (M_PI / 180.0f) * 0.000001f * deltaT; 와 κ°™λ‹€. 각속도 값이 μžμ΄λ‘œμŠ€μ½”ν”„ μ„Όμ„œ 값을 micro seconds λ‹¨μœ„μ˜ λΌλ””μ•ˆ 속도 κ°’μœΌλ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” μ½”λ“œλ‹€. 이제 μžμ΄λ‘œμŠ€μ½”ν”„ μ„Όμ„œ 값은 deltaGyroAngle[axis]에 μ €μž₯λœλ‹€.

cfg.acc_lpf_factor μ „μ—­λ³€μˆ˜λŠ” 4둜 μ΄ˆκΈ°ν™” λ˜μ–΄ μžˆλŠ” μƒνƒœμ΄κΈ° λ•Œλ¬Έμ—, if (cfg.acc_lpf_factor > 0) 쑰건은 μ„±λ¦½ν•œλ‹€. λΈ”λŸ­ μ•ˆμ— μžˆλŠ” μ½”λ“œλŠ” Low Pass Filterλ₯Ό λœ»ν•˜λ©°, 가속도 κ°’μ˜ 평균을 κ΅¬ν•˜λ˜, 1/cfg.acc_lpf_factor크기 만큼의 성뢄을 κ°€μž₯ 졜근 값에 비쀑을 두고, λ‚˜λ¨Έμ§€ 1 - (1/cfg.acc_lpf_factor) κ°’λ§ŒνΌ κ·Έ μ „ 평균값에 비쀑을 λ‘”λ‹€λŠ” λœ»μ΄λ‹€.

Low Pass Filtering 된 값은 accLPF[] μŠ€νƒœν‹± λ³€μˆ˜μ™€ accSmooth[]λ³€μˆ˜μ— μ €μž₯λœλ‹€. 둜우 패슀 ν•„ν„°λ₯Ό μ“°λ“  μ“°μ§€μ•Šλ“ , μ΅œμ’… 가속도 μ„Όμ„œ 값은 accSmooth[]λ³€μˆ˜μ— μ €μž₯λœλ‹€.

accMag λ³€μˆ˜λŠ” (가속도 λ²‘ν„°μ˜ 크기 X 100) 을 μ €μž₯ν•œλ‹€. float값을 int16_t둜 λ³€ν™˜ν•  λ•Œ, μ†Œμˆ˜μ  두 번째 μžλ¦¬κΉŒμ§€ ν‘œν˜„ν•˜κΈ° μœ„ν•΄μ„œ X100 이 λ˜μ—ˆλ‹€.

μ •λ¦¬ν•˜λ©΄

  • deltaGyroAngle[] = μ΄ˆλ‹Ή (x,y,z) 각속도 -> 마이크둜 μ΄ˆλ‹Ή (x,y,z) λΌλ””μ•ˆ 속도λ₯Ό μ €μž₯ν•œλ‹€.
  • accSmooth[] = 둜우 패슀 ν•„ν„°κ°€ 적용된 x,y,z 가속도λ₯Ό μ €μž₯ν•œλ‹€.
  • accMag = 가속도 벑터 크기 X 100 값을 μ €μž₯ν•œλ‹€.
int32_t accMag = 0;
static float accLPF[3];
float scale, deltaGyroAngle[3];
deltaT = currentT - previousT;
scale = deltaT * gyro.scale;

// Initialization
for (axis = 0; axis < 3; axis++)
{
    deltaGyroAngle[axis] = gyroADC[axis] * scale;
    if (cfg.acc_lpf_factor > 0) 
    {
        accLPF[axis] = accLPF[axis] * (1.0f - (1.0f / cfg.acc_lpf_factor)) + accADC[axis] * (1.0f / cfg.acc_lpf_factor);
        accSmooth[axis] = accLPF[axis];
    } 
	else 
	{
        accSmooth[axis] = accADC[axis];
    }
				
    accMag += (int32_t)accSmooth[axis] * accSmooth[axis];
}
accMag = accMag * 100 / ((int32_t)acc_1G * acc_1G);

###3. x,y,zμΆ• λ§ˆμ΄ν¬λ‘œμ΄ˆλ‹Ή λΌλ””μ•ˆ 속도 κ°’μœΌλ‘œ 벑터λ₯Ό νšŒμ „μ‹œν‚¨λ‹€.

rotateV()ν•¨μˆ˜λŠ” 인자둜 λ„˜μ–΄μ˜¨ 벑터λ₯Ό x,y,zμΆ• 각속도 값을 μ μš©ν•΄ νšŒμ „μ‹œν‚¨λ‹€.

rotateV()ν•¨μˆ˜λŠ” μ •ν™•νžˆ x,y,zμΆ• 였일러 νšŒμ „κ°’μœΌλ‘œ νšŒμ „ν–‰λ ¬μ„ λ§Œλ“  ν›„, 벑터λ₯Ό νšŒμ „μ‹œν‚€λŠ” μ½”λ“œλ‹€.

κ²°κ΅­ EstG λ²‘ν„°λŠ” λ©€ν‹°μ½₯ν„°κ°€ λ°”λΌλ³΄λŠ” λ°©ν–₯을 λ‚˜νƒ€λ‚Έλ‹€.

t_fp_vector EstG; // μ „μ—­λ³€μˆ˜

rotateV(&EstG.V, deltaGyroAngle);


// Rotate Estimated vector(s) with small angle approximation, according to the gyro data
void rotateV(struct fp_vector *v, float *delta)
{
    struct fp_vector v_tmp = *v;

    // This does a  "proper" matrix rotation using gyro deltas without small-angle approximation
    float mat[3][3];
    float cosx, sinx, cosy, siny, cosz, sinz;
    float coszcosx, sinzcosx, coszsinx, sinzsinx;

    cosx = cosf(delta[ROLL]);
    sinx = sinf(delta[ROLL]);
    cosy = cosf(delta[PITCH]);
    siny = sinf(delta[PITCH]);
    cosz = cosf(delta[YAW]);
    sinz = sinf(delta[YAW]);

    coszcosx = cosz * cosx;
    sinzcosx = sinz * cosx;
    coszsinx = sinx * cosz;
    sinzsinx = sinx * sinz;

    mat[0][0] = cosz * cosy;
    mat[0][1] = -cosy * sinz;
    mat[0][2] = siny;
    mat[1][0] = sinzcosx + (coszsinx * siny);
    mat[1][1] = coszcosx - (sinzsinx * siny);
    mat[1][2] = -sinx * cosy;
    mat[2][0] = (sinzsinx) - (coszcosx * siny);
    mat[2][1] = (coszsinx) + (sinzcosx * siny);
    mat[2][2] = cosy * cosx;

    v->X = v_tmp.X * mat[0][0] + v_tmp.Y * mat[1][0] + v_tmp.Z * mat[2][0];
    v->Y = v_tmp.X * mat[0][1] + v_tmp.Y * mat[1][1] + v_tmp.Z * mat[2][1];
    v->Z = v_tmp.X * mat[0][2] + v_tmp.Y * mat[1][2] + v_tmp.Z * mat[2][2];
}

###4. 상보필터 적용

accMag은 가속도벑터크기 X 100 이 된 μƒνƒœμ΄λ―€λ‘œ, 100이 1Gκ°€ λœλ‹€. μ½”λ“œ μƒμ—μ„œ 72 < accMag < 133 μ΄λ―€λ‘œ, 가속도벑터 크기가 0.72G 보닀 ν¬λ©΄μ„œ, 1.33G보닀 μž‘λ‹€λ©΄, 상보필터λ₯Ό μ μš©ν•œλ‹€. 즉, μ λ‹Ήνžˆ 움직일 λ•Œλ§Œ, 상보필터λ₯Ό μ μš©ν•˜κ³ , 그렇지 μ•Šμ€ κ²½μš°μ—λŠ” μžμ΄λ‘œμŠ€μ½”ν”„ μ„Όμ„œκ°’μ„ κ·ΈλŒ€λ‘œ λ”°λ₯΄κ² λ‹€λŠ” λœ»μ΄λ‹€.

상보필터 κ°’ mcfg.gyro_cmpf_factorλŠ” 600이 μ„€μ •λ˜μ–΄μžˆλ‹€. μ½”λ“œκ°€ μ•½κ°„ λ³΅μž‘ν•˜κ²Œ λ˜μ–΄μžˆμ§€λ§Œ, 상보필터 곡식을 κ·ΈλŒ€λ‘œ λ”°λ₯Έλ‹€. EstG.A[axis] = EstG.A[axis] * (600/601) + accSmooth[axis]/601 둜 ν‘œν˜„ ν•  수 μžˆλ‹€.

더 κ°„λ‹¨νžˆ ν•˜μžλ©΄,

EstG.A[axis] = EstG.A[axis] * 0.99833f + accSmooth[axis]*0.00166f 둜 ν‘œν˜„λœλ‹€.

mcfg.gyro_cmpf_factor값은 micro second λ‹¨μœ„ 인지, milli second λ‹¨μœ„ 인지, ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜λŠ” 주기에 따라 κ²°μ •λœλ‹€.

// Apply complimentary filter (Gyro drift correction)
// If accel magnitude >1.15G or <0.85G and ACC vector outside of the limit range => we neutralize the effect of accelerometers in the angle estimation.
// To do that, we just skip filter, as EstV already rotated by Gyro
if (72 < (uint16_t)accMag && (uint16_t)accMag < 133) {
    for (axis = 0; axis < 3; axis++)
        EstG.A[axis] = (EstG.A[axis] * (float)mcfg.gyro_cmpf_factor + accSmooth[axis]) * INV_GYR_CMPF_FACTOR;
}

###5. λ©€ν‹°μ½₯ν„°μ˜ μžμ„Έ Roll, Pitch, Yaw 각을 κ³„μ‚°ν•œλ‹€. ZμΆ• νšŒμ „κ°μ΄ smallAngle값을 λ„˜μ–΄κ°€λ©΄ f.SMALL_ANGLE ν”Œλž˜κ·Έκ°€ ν™œμ„±ν™” λœλ‹€.

μ΅œμ’… κ³„μ‚°λœ EstGλ²‘ν„°λ‘œ λ©€ν‹°μ½₯ν„°μ˜ Roll, Pitch νšŒμ „κ°μ„ κ³„μ‚°ν•œλ‹€. 이 μˆ˜μ‹μ€ x,y,z 벑터λ₯Ό μ΄μš©ν•΄ Roll, Pitch 각을 κ΅¬ν•˜λŠ” 곡식 κ·ΈλŒ€λ‘œ λ”°λ₯Έλ‹€.

anglerad[]λ³€μˆ˜λŠ” λΌλ””μ•ˆμœΌλ‘œ νšŒμ „κ°μ„ μ €μž₯ν•˜κ³ , angle[]은 각도 κ°’μœΌλ‘œ μ €μž₯ν•œλ‹€.

angle[]을 ꡬ할 λ•Œ, (1800.0f / M_PI) κ³±ν•˜λŠ” μ΄μœ λŠ”, μ‹€μˆ˜ 값을 μ •μˆ˜ κ°’μœΌλ‘œ λ³€ν™˜ 될 λ•Œ, μ†Œμˆ˜μ μžλ¦¬κ°€ 없어지기 λ•Œλ¬Έμ—, 적어도 μ†Œμˆ˜μ  첫 번째 μžλ¦¬κΉŒμ§€ κ³„μ‚°ν•˜κΈ° μœ„ν•΄ X10 을 ν•œλ‹€. κ·Έλž˜μ„œ μ‹€μ œλ‘œ angle[]λ³€μˆ˜λŠ” 45도 일 λ•ŒλŠ” 450값을 κ°€μ§€κ²Œλœλ‹€.

f.SMALL_ANGLE = (EstG.A[Z] > smallAngle);

// Attitude of the estimated vector
anglerad[ROLL] = atan2f(EstG.V.Y, EstG.V.Z);
anglerad[PITCH] = atan2f(-EstG.V.X, sqrtf(EstG.V.Y * EstG.V.Y + EstG.V.Z * EstG.V.Z));
angle[ROLL] = lrintf(anglerad[ROLL] * (1800.0f / M_PI));
angle[PITCH] = lrintf(anglerad[PITCH] * (1800.0f / M_PI));

###6. λ©€ν‹°μ½₯ν„°κ°€ λ°”λΌλ³΄λŠ” λ°©ν–₯을 κ³„μ‚°ν•œλ‹€. μ§€μžκΈ° μ„Όμ„œκ°€ μžˆμ„ λ•Œμ™€ 그렇지 μ•Šμ„ λ•Œ, λ”°λ‘œ λΆ„κΈ° λ˜μ—ˆλ‹€.

μ§€μžκΈ° μ„Όμ„œκ°€ μžˆμ„ 경우, 상보필터λ₯Ό μ΄μš©ν•΄ EstM벑터λ₯Ό κ΅¬ν•œ ν›„, calculateHeading() ν•¨μˆ˜λ‘œ μ΅œμ’… Yaw νšŒμ „κ°μ„ κ³„μ‚°ν•œλ‹€. μ§€μžκΈ° μ„Όμ„œ 상보필터 κ³„μˆ˜ mcfg.gyro_cmpfm_factor`λŠ” 250으둜 μ„€μ •λœλ‹€.

μ§€μžκΈ° μ„Όμ„œκ°€ 없을 경우, ν˜„μž¬ x,y,zμΆ• 각속도 값을 λ‚˜νƒ€λ‚΄λŠ” deltaGyroAngle[] λ³€μˆ˜λ‘œ EstN벑터λ₯Ό κ΅¬ν•˜κ³ , calculateHeading() ν•¨μˆ˜λ‘œ μ΅œμ’… Yaw νšŒμ „κ°μ„ κ³„μ‚°ν•œλ‹€.

//    if (sensors(SENSOR_MAG)) 
if (0)
{
    rotateV(&EstM.V, deltaGyroAngle);
    for (axis = 0; axis < 3; axis++)
        EstM.A[axis] = (EstM.A[axis] * (float)mcfg.gyro_cmpfm_factor + magADC[axis]) * INV_GYR_CMPFM_FACTOR;
   heading = calculateHeading(&EstM);
} 
else 
{
    rotateV(&EstN.V, deltaGyroAngle);
    normalizeV(&EstN.V, &EstN.V);
    heading = calculateHeading(&EstN);
}


// baseflight calculation by Luggi09 originates from arducopter
static int16_t calculateHeading(t_fp_vector *vec)
{
    int16_t head;

    float cosineRoll = cosf(anglerad[ROLL]);
    float sineRoll = sinf(anglerad[ROLL]);
    float cosinePitch = cosf(anglerad[PITCH]);
    float sinePitch = sinf(anglerad[PITCH]);
    float Xh = vec->A[X] * cosinePitch + vec->A[Y] * sineRoll * sinePitch + vec->A[Z] * sinePitch * cosineRoll;
    float Yh = vec->A[Y] * cosineRoll - vec->A[Z] * sineRoll;
    float hd = (atan2f(Yh, Xh) * 1800.0f / M_PI + magneticDeclination) / 10.0f;
    head = lrintf(hd);
    if (head < 0)
        head += 360;

    return head;
}