示例#1
0
// *Somehow* modified by me..
// Besides mwii credit must go to Sebbi, BRM! Hopefully they condone mentioning them above my trash.
// Sebbi for his rotation of the acc vector and BRM for his normalization ideas.
static void getEstimatedAttitude(void)
{
    static t_fp_vector EstG, EstM;
    static float    accLPFALT[3], accLPFGPS[3], Tilt_25deg, INVsens1G;
    static float    INV_GYR_CMPF_FACTOR, INV_GYR_CMPFM_FACTOR, ACC_GPS_RC, ACC_ALT_RC, ACC_RC;
    static uint32_t previousT, UpsDwnTimer;
    static bool     init = false;
    float           tmp[3], scale, deltaGyroAngle[3], ACCALTFac, ACCGPSFac, ACCFac, rollRAD, pitchRAD;
    float           NormFst = 0.0f, NormScnd, NormR, A, B, cr, sr, cp, sp, cy, sy, spcy, spsy, acc_south, acc_west, acc_up, FAC;
    uint8_t         i;
    uint32_t        tmpu32, currentT = micros();
    ACCDeltaTimeINS = (float)(currentT - previousT) * 0.000001f;
    previousT       = currentT;
    if(!init)                                                                 // Setup variables & constants
    {
        init = true;
        INVsens1G            = 1.0f / cfg.sens_1G;
        Tilt_25deg           = cosf(25.0f * RADX);
        INV_GYR_CMPF_FACTOR  = 1.0f / (float)(cfg.gy_cmpf  + 1);              // Default 400
        INV_GYR_CMPFM_FACTOR = 1.0f / (float)(cfg.gy_cmpfm + 1);              // Default 200
        ACC_ALT_RC           = 0.5f / (M_PI * cfg.acc_altlpfhz);              // Default 10 Hz
        ACC_RC               = 0.5f / (M_PI * cfg.acc_lpfhz);                 // Default 0,536 Hz
        ACC_GPS_RC           = 0.5f / (M_PI * cfg.acc_gpslpfhz);              // Default 5 Hz
        for (i = 0; i < 3; i++)                                               // Preset some values to reduce runup time
        {
            tmp[0]       = accADC[i] * INVsens1G;
            accSmooth[i] = tmp[0];
            accLPFGPS[i] = tmp[0];
            accLPFALT[i] = tmp[0];
            EstG.A[i]    = tmp[0] * 0.5f;
        }
    }
    ACCALTFac = ACCDeltaTimeINS / (ACC_ALT_RC + ACCDeltaTimeINS);             // Adjust LPF to cycle time / do Hz cut off
    ACCGPSFac = ACCDeltaTimeINS / (ACC_GPS_RC + ACCDeltaTimeINS);             // Adjust LPF to cycle time / do Hz cut off
    ACCFac    = ACCDeltaTimeINS / (ACC_RC     + ACCDeltaTimeINS);
    scale     = ACCDeltaTimeINS * GyroScale;                                  // SCALE CHANGED TO RAD per SECONDS, DAMN
    for (i = 0; i < 3; i++)
    {
        tmp[0]            = accADC[i]    *  INVsens1G;                        // Reference all to 1G here
        accLPFGPS[i]     += ACCGPSFac    * (tmp[0] - accLPFGPS[i]);           // For GPS
        accLPFALT[i]     += ACCALTFac    * (tmp[0] - accLPFALT[i]);           // For Althold
        accSmooth[i]     += ACCFac       * (tmp[0] - accSmooth[i]);           // For Gyrodrift correction
        NormFst          += accSmooth[i] *  accSmooth[i];
        deltaGyroAngle[i] = gyroADC[i]   *  scale;                            // deltaGyroAngle is in RAD
    }
    RotGravAndMag(&EstG.V, &EstM.V, deltaGyroAngle);                          // Rotate Grav&Mag together to avoid doublecalculation
    NormFst  = sqrtf(NormFst);
    tmpu32   = (uint32_t)(NormFst * 1000.0f);                                 // Make it "shorter" for comparison in temp variable
    NormScnd = sqrtf(EstG.A[0] * EstG.A[0] + EstG.A[1] * EstG.A[1] + EstG.A[2] * EstG.A[2]);
    if (NormScnd) NormR = 1.0f / NormScnd;
    else          NormR = INVsens1G;                                          // Feed vanilla value in that rare case, to let angle calculation always happen
    for (i = 0; i < 3; i++) tmp[i] = EstG.A[i] * NormR;                       // tmp[0..2] contains normalized EstG
    if (850 < tmpu32 && tmpu32 < 1150)                                        // Gyro drift correction if ACC between 0.85G and 1.15G else skip filter, as EstV already rotated by Gyro
    {
        NormR = 1.0f / NormFst;                                               // We just cmp filter together normalized vectors here. Div 0 not possible here.
        for (i = 0; i < 3; i++) EstG.A[i] = (tmp[i] * (float)cfg.gy_cmpf + accSmooth[i] * NormR) * INV_GYR_CMPF_FACTOR;
    }
    rollRAD      =  atan2f(tmp[0], tmp[2]);                                   // Note: One cycle after successful cmpf is done with old values.
    pitchRAD     =  asinf(-tmp[1]);                                           // Has to have the "wrong sign" relative to angle[PITCH]
    angle[ROLL]  =  rollRAD  * RADtoDEG10;                                    // Use rounded values, eliminate jitter for main PID I and D
    angle[PITCH] = -pitchRAD * RADtoDEG10;
    TiltValue    =  cosf(rollRAD) * cosf(pitchRAD);                           // We do this correctly here
    if (TiltValue >= 0)   UpsDwnTimer = 0;
    else if(!UpsDwnTimer) UpsDwnTimer = currentT + 20000;                     // Use Timer here to make absolutely sure we are upsidedown
    if (UpsDwnTimer && currentT > UpsDwnTimer) UpsideDown = true;
    else UpsideDown = false;
    if (TiltValue > Tilt_25deg) f.SMALL_ANGLES_25 = 1;
    else f.SMALL_ANGLES_25 = 0;
    cr = cosf(rollRAD);
    sr = sinf(rollRAD);
    cp = cosf(pitchRAD);
    sp = sinf(pitchRAD);
    if (cfg.mag_calibrated)                                                   // mag_calibrated can just be true if MAG present
    {
        NormFst = sqrtf(EstM.A[0] * EstM.A[0] + EstM.A[1] * EstM.A[1] + EstM.A[2] * EstM.A[2]);
        if (NormFst) NormR = 1.0f / NormFst;
        else         NormR = 1.0f;                                            // Vanilla value for rare case
        for (i = 0; i < 3; i++) tmp[i] = EstM.A[i] * NormR;                   // tmp[0..2] contains normalized EstM
        if(HaveNewMag)                                                        // Only do Complementary filter when new MAG data are available
        {
            HaveNewMag = false;
            NormFst = sqrtf(magADCfloat[0] * magADCfloat[0] + magADCfloat[1] * magADCfloat[1] + magADCfloat[2] * magADCfloat[2]);
            if (NormFst) NormR = 1.0f / NormFst;
            else         NormR = 1.0f;
            for (i = 0; i < 3; i++) EstM.A[i] = (tmp[i] * (float)cfg.gy_cmpfm + magADCfloat[i] * NormR) * INV_GYR_CMPFM_FACTOR;
        }
        A  = tmp[1] * cp + tmp[0] * sr * sp + tmp[2] * cr * sp;
        B  = tmp[0] * cr - tmp[2] * sr;
        heading = wrap_180(atan2f(-B, A) * RADtoDEG + magneticDeclination);   // Get rad to Degree and add declination (note: without *10) // Wrap to -180 0 +180 Degree
    } else heading = 0;                                                       // if no mag or not calibrated do bodyframe below
    tmp[0]    = heading * RADX;                                               // Do GPS INS rotate ACC X/Y to earthframe no centrifugal comp. yet
    cy        = cosf(tmp[0]);
    sy        = sinf(tmp[0]);
    cos_yaw_x = cy;                                                           // Store for general use
    sin_yaw_y = sy;                                                           // Store for general use
    spcy      = sp * cy;
    spsy      = sp * sy;
    FAC       = 980.665f * ACCDeltaTimeINS;                                   // vel factor for normalized output tmp3 = (9.80665f * (float)ACCDeltaTime) / 10000.0f;
    acc_up    = ((-sp) * accLPFALT[1] + sr * cp * accLPFALT[0] + cp * cr * accLPFALT[2]) - 1;// -1G
    if(GroundAltInitialized) vario += acc_up * FAC * constrain(TiltValue + 0.05, 0.5f, 1.0f);// Positive when moving Up. Just do Vario when Baro completely initialized. Empirical hightdrop reduction on tilt.
    acc_south = cp * cy * accLPFGPS[1] + (sr * spcy - cr * sy) * accLPFGPS[0] + ( sr * sy + cr * spcy) * accLPFGPS[2];
    acc_west  = cp * sy * accLPFGPS[1] + (cr * cy + sr * spsy) * accLPFGPS[0] + (-sr * cy + cr * spsy) * accLPFGPS[2];
    ACC_speed[LAT] -= acc_south * FAC;                                        // Positive when moving North cm/sec when no MAG this is speed to the front
    ACC_speed[LON] -= acc_west  * FAC;                                        // Positive when moving East cm/sec when no MAG this is speed to the right
}
示例#2
0
static void getEstimatedAttitude(void)
{
    static t_fp_vector EstG, EstM;
    static float    cms[3] = {0.0f, 0.0f, 0.0f}, Tilt_25deg, AccScaleCMSS;
    static float    INV_GY_CMPF, INV_GY_CMPFM, ACC_GPS_RC, ACC_ALT_RC, ACC_RC;
    static uint32_t UpsDwnTimer, SQ1G;
    static bool     init = false;
    float           tmp[3], DeltGyRad[3], rollRAD, pitchRAD;
    float           Norm, A, B, cr, sr, cp, sp, spcy, spsy, accycp, CmsFac;
    uint8_t         i;
    uint32_t        tmpu32 = 0;
    if(!init)                                                                 // Setup variables & constants
    {
        init = true;
        AccScaleCMSS = OneGcmss / (float)cfg.sens_1G;                         // scale to cm/ss
        SQ1G         = (int32_t)cfg.sens_1G * (int32_t)cfg.sens_1G;
        Tilt_25deg   = cosf(25.0f * RADX);
        INV_GY_CMPF  = 1.0f / (float)(cfg.gy_gcmpf + 1);                      // Default 400
        INV_GY_CMPFM = 1.0f / (float)(cfg.gy_mcmpf + 1);                      // Default 200
        tmp[0]       = 0.5f / M_PI;
        ACC_RC       = tmp[0] / cfg.acc_lpfhz;                                // Default 0,536 Hz
        ACC_ALT_RC   = tmp[0] / (float)cfg.acc_altlpfhz;                      // Default 10 Hz
        ACC_GPS_RC   = tmp[0] / (float)cfg.acc_gpslpfhz;                      // Default 5 Hz
        for (i = 0; i < 3; i++)                                               // Preset some values to reduce runup time
        {
            accSmooth[i] = accADC[i];
            EstG.A[i]    = accSmooth[i];
            EstM.A[i]    = magADCfloat[i];                                    // Using /2 for more stability
        }
    }
    CmsFac = ACCDeltaTimeINS * AccScaleCMSS;                                  // We need that factor for INS below
    tmp[0] = ACCDeltaTimeINS / (ACC_RC + ACCDeltaTimeINS);
    for (i = 0; i < 3; i++)
    {
        accSmooth[i] += tmp[0] * (accADC[i] - accSmooth[i]);                  // For Gyrodrift correction
        DeltGyRad[i]  = (ACCDeltaTimeINS * gyroADC[i]) * 0.0625f;             // gyroADC delivered in 16 * rad/s
        tmpu32       += (int32_t)accSmooth[i] * (int32_t)accSmooth[i];        // Accumulate ACC magnitude there
    }
    RotGravAndMag(&EstG.V, &EstM.V, DeltGyRad);                               // Rotate Grav & Mag together to avoid doublecalculation
    tmpu32 = (tmpu32 * 100) / SQ1G;                                           // accMag * 100 / ((int32_t)acc_1G * acc_1G);
    if (72 < tmpu32 && tmpu32 < 133)                                          // Gyro drift correct between 0.85G - 1.15G
    {
        for (i = 0; i < 3; i++) EstG.A[i] = (EstG.A[i] * (float)cfg.gy_gcmpf + accSmooth[i]) * INV_GY_CMPF;
    }
    tmp[0]       = EstG.A[0] * EstG.A[0] + EstG.A[2] * EstG.A[2];             // Start Angle Calculation. tmp[0] is used for heading below
    Norm         = sqrtf(tmp[0] + EstG.A[1] * EstG.A[1]);
    if(!Norm) return;                                                         // Should never happen but break here to prevent div-by-zero-evil
    Norm         = 1.0f / Norm;
    rollRAD      =  atan2f(EstG.A[0] * Norm, EstG.A[2] * Norm);               // Norm seems to be obsolete, but testing shows different result :)
    pitchRAD     = -asinf(constrain(EstG.A[1] * Norm, -1.0f, 1.0f));          // Ensure range, eliminate rounding stuff that might occure.
    cr           = cosf(rollRAD);
    sr           = sinf(rollRAD);
    cp           = cosf(pitchRAD);
    sp           = sinf(pitchRAD);
    TiltValue    = cr * cp;                                                   // We do this correctly here
    angle[ROLL]  = (float)((int32_t)( rollRAD  * RADtoDEG10 + 0.5f));         // Use rounded values, eliminate jitter for main PID I and D
    angle[PITCH] = (float)((int32_t)(-pitchRAD * RADtoDEG10 + 0.5f));
    if (TiltValue >= 0)   UpsDwnTimer = 0;
    else if(!UpsDwnTimer) UpsDwnTimer = currentTime + 20000;                  // Use 20ms Timer here to make absolutely sure we are upsidedown
    if (UpsDwnTimer && currentTime > UpsDwnTimer) UpsideDown = true;
    else UpsideDown = false;
    if (TiltValue > Tilt_25deg) f.SMALL_ANGLES_25 = 1;
    else f.SMALL_ANGLES_25 = 0;
    if (cfg.mag_calibrated)                                                   // mag_calibrated can just be true if MAG present
    {
        if(HaveNewMag)                                                        // Only do Complementary filter when new MAG data are available
        {
            HaveNewMag = false;
            for (i = 0; i < 3; i++) EstM.A[i] = (EstM.A[i] * (float)cfg.gy_mcmpf + magADCfloat[i]) * INV_GY_CMPFM;
        }
        A = EstM.A[1] * tmp[0] - (EstM.A[0] * EstG.A[0] + EstM.A[2] * EstG.A[2]) * EstG.A[1];// Mwii method is more precise (less rounding errors)
        B = EstM.A[2] * EstG.A[0] - EstM.A[0] * EstG.A[2];
        heading = wrap_180(atan2f(B, A * Norm) * RADtoDEG + magneticDeclination);
        if (sensors(SENSOR_GPS) && !UpsideDown)
        {
            tmp[0]    = heading * RADX;                                       // Do GPS INS rotate ACC X/Y to earthframe no centrifugal comp. yet
            cos_yaw_x = cosf(tmp[0]);                                         // Store for general use
            sin_yaw_y = sinf(tmp[0]);
            spcy      = sp * cos_yaw_x;
            spsy      = sp * sin_yaw_y;
            accycp    = cp * accADC[1];
            tmp[0]    = accycp * cos_yaw_x + (sr * spcy - cr * sin_yaw_y) * accADC[0] + ( sr * sin_yaw_y + cr * spcy) * accADC[2]; // Rotate raw acc here
            tmp[1]    = accycp * sin_yaw_y + (cr * cos_yaw_x + sr * spsy) * accADC[0] + (-sr * cos_yaw_x + cr * spsy) * accADC[2];
            tmp[2]    = ACCDeltaTimeINS / (ACC_GPS_RC + ACCDeltaTimeINS);
            for (i = 0; i < 2; i++)
            {
                cms[i]       += tmp[2] * (tmp[i] * CmsFac - cms[i]);
                ACC_speed[i] -= cms[i];                                       //cm/s N+ E+
            }
        }
    }
    if(GroundAltInitialized && !UpsideDown)                                   // GroundAltInitialized can just be true if baro present
    {
        tmp[0]  = ((-sp) * accADC[1] + sr * cp * accADC[0] + cp * cr * accADC[2]) - (float)cfg.sens_1G;
        cms[2] += (ACCDeltaTimeINS / (ACC_ALT_RC + ACCDeltaTimeINS)) * (tmp[0] * CmsFac - cms[2]);
        vario  += cms[2];
    }
}