Exemple #1
0
void RTFusionRTQF::newIMUData(RTIMU_DATA& data, const RTIMUSettings *settings)
{
    if (m_debug) {
        HAL_INFO("\n------\n");
        HAL_INFO2("IMU update delta time: %f, sample %d\n", m_timeDelta, m_sampleNumber++);
    }
    m_sampleNumber++;

    if (m_enableGyro)
        m_gyro = data.gyro;
    else
        m_gyro = RTVector3();
    m_accel = data.accel;
    m_compass = data.compass;
    m_compassValid = data.compassValid;

    if (m_firstTime) {
        m_lastFusionTime = data.timestamp;
        calculatePose(m_accel, m_compass, settings->m_compassAdjDeclination);
        m_Fk.fill(0);

        //  initialize the poses

        m_stateQ.fromEuler(m_measuredPose);
        m_fusionQPose = m_stateQ;
        m_fusionPose = m_measuredPose;
        m_firstTime = false;
    } else {
        m_timeDelta = (RTFLOAT)(data.timestamp - m_lastFusionTime) / (RTFLOAT)1000000;
        m_lastFusionTime = data.timestamp;
        if (m_timeDelta <= 0)
            return;

        calculatePose(data.accel, data.compass, settings->m_compassAdjDeclination);

        predict();
        update();
        m_stateQ.toEuler(m_fusionPose);
        m_fusionQPose = m_stateQ;

        if (m_debug) {
            HAL_INFO(RTMath::displayRadians("Measured pose", m_measuredPose));
            HAL_INFO(RTMath::displayRadians("RTQF pose", m_fusionPose));
            HAL_INFO(RTMath::displayRadians("Measured quat", m_measuredPose));
            HAL_INFO(RTMath::display("RTQF quat", m_stateQ));
            HAL_INFO(RTMath::display("Error quat", m_stateQError));
         }
    }
    data.fusionPoseValid = true;
    data.fusionQPoseValid = true;
    data.fusionPose = m_fusionPose;
    data.fusionQPose = m_fusionQPose;
}
void RTFusionKalman4::newIMUData(RTIMU_DATA& data, const RTIMUSettings *settings)
{
    if (m_enableGyro)
        m_gyro = data.gyro;
    else
        m_gyro = RTVector3();
    
    m_accel = data.accel;
    m_compass = data.compass;
    m_compassValid = data.compassValid;

    if (m_firstTime) {
        m_lastFusionTime = data.timestamp;
        calculatePose(m_accel, m_compass, settings->m_compassAdjDeclination);
        m_Fk.fill(0);

        //  init covariance matrix to something

        m_Pkk.fill(0);
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                m_Pkk.setVal(i,j, 0.5);

        // initialize the observation model Hk
        // Note: since the model is the state vector, this is an identity matrix so it won't be used

        //  initialize the poses

        m_stateQ.fromEuler(m_measuredPose);
        m_fusionQPose = m_stateQ;
        m_fusionPose = m_measuredPose;
        m_firstTime = false;
    } else {
        m_timeDelta = (RTFLOAT)(data.timestamp - m_lastFusionTime) / (RTFLOAT)1000000;
        m_lastFusionTime = data.timestamp;
        if (m_timeDelta <= 0)
            return;

        if (m_debug) {
            HAL_INFO("\n------\n");
            HAL_INFO1("IMU update delta time: %f\n", m_timeDelta);
        }

        calculatePose(data.accel, data.compass, settings->m_compassAdjDeclination);

        predict();
        update();
        m_stateQ.toEuler(m_fusionPose);
        m_fusionQPose = m_stateQ;

        if (m_debug | settings->m_fusionDebug) {
            HAL_INFO(RTMath::displayRadians("Measured pose", m_measuredPose));
            HAL_INFO(RTMath::displayRadians("Kalman pose", m_fusionPose));
            HAL_INFO(RTMath::displayRadians("Measured quat", m_measuredPose));
            HAL_INFO(RTMath::display("Kalman quat", m_stateQ));
            HAL_INFO(RTMath::display("Error quat", m_stateQError));
         }
    }
    data.fusionPoseValid = true;
    data.fusionQPoseValid = true;
    data.fusionPose = m_fusionPose;
    data.fusionQPose = m_fusionQPose;
}
Exemple #3
0
void RTFusionRTQF::newIMUData(const RTVector3& gyro, const RTVector3& accel,
                              const RTVector3& compass, unsigned long timestamp)
{
    RTVector3 fusionGyro;

    if (m_firstTime) {
        m_lastFusionTime = timestamp;
        calculatePose(accel, compass);

        //  initialize the poses

        m_fusionQPose.fromEuler(m_measuredPose);
        m_fusionPose = m_measuredPose;
        m_firstTime = false;
    } else {
        m_timeDelta = (RTFLOAT)(timestamp - m_lastFusionTime) / (RTFLOAT)1000;
        m_lastFusionTime = timestamp;

        if (m_timeDelta <= 0) {
            return;
        }

        calculatePose(accel, compass);

        //      predict();

        RTFLOAT x2, y2, z2;
        RTFLOAT qs, qx, qy, qz;

        qs = m_fusionQPose.scalar();
        qx = m_fusionQPose.x();
        qy = m_fusionQPose.y();
        qz = m_fusionQPose.z();

        if (m_enableGyro) {
            fusionGyro = gyro;
        } else {
            fusionGyro = RTVector3();
        }

        x2 = fusionGyro.x() / (RTFLOAT)2.0;
        y2 = fusionGyro.y() / (RTFLOAT)2.0;
        z2 = fusionGyro.z() / (RTFLOAT)2.0;

        // Predict new state

        m_fusionQPose.setScalar(qs + (-x2 * qx - y2 * qy - z2 * qz) * m_timeDelta);
        m_fusionQPose.setX(qx + (x2 * qs + z2 * qy - y2 * qz) * m_timeDelta);
        m_fusionQPose.setY(qy + (y2 * qs - z2 * qx + x2 * qz) * m_timeDelta);
        m_fusionQPose.setZ(qz + (z2 * qs + y2 * qx - x2 * qy) * m_timeDelta);

        //      update();

#ifdef USE_SLERP

        if (m_enableCompass || m_enableAccel) {

            // calculate rotation delta

            m_rotationDelta = m_fusionQPose.conjugate() * m_measuredQPose;
            m_rotationDelta.normalize();

            // take it to the power (0 to 1) to give the desired amount of correction

            RTFLOAT theta = acos(m_rotationDelta.scalar());

            RTFLOAT sinPowerTheta = sin(theta * m_slerpPower);
            RTFLOAT cosPowerTheta = cos(theta * m_slerpPower);

            m_rotationUnitVector.setX(m_rotationDelta.x());
            m_rotationUnitVector.setY(m_rotationDelta.y());
            m_rotationUnitVector.setZ(m_rotationDelta.z());
            m_rotationUnitVector.normalize();

            m_rotationPower.setScalar(cosPowerTheta);
            m_rotationPower.setX(sinPowerTheta * m_rotationUnitVector.x());
            m_rotationPower.setY(sinPowerTheta * m_rotationUnitVector.y());
            m_rotationPower.setZ(sinPowerTheta * m_rotationUnitVector.z());
            m_rotationPower.normalize();

            //  multiple this by predicted value to get result

            m_fusionQPose *= m_rotationPower;
        }

#else

        if (m_enableCompass || m_enableAccel) {
            m_stateQError = m_measuredQPose - m_fusionQPose;
        } else {
            m_stateQError = RTQuaternion();
        }

        // make new state estimate

        RTFLOAT qt = m_Q * m_timeDelta;

        m_fusionQPose += m_stateQError * (qt / (qt + m_R));
#endif

        m_fusionQPose.normalize();

        m_fusionQPose.toEuler(m_fusionPose);
    }
}
void RTFusionAHRS::newIMUData(RTIMU_DATA& data, const RTIMUSettings *settings)
{

    if (m_debug) {
        HAL_INFO("\n------\n");
        HAL_INFO1("IMU update delta time: %f\n", m_timeDelta);
    }

    m_gyro = data.gyro;
    m_accel = data.accel;
    m_compass = data.compass;
    m_compassValid = data.compassValid;

    if (m_firstTime) {
        
        // adjust for compass declination, compute the correction data only at beginning
        m_sin_theta_half = sin(-settings->m_compassAdjDeclination/2.0f);
        m_cos_theta_half = cos(-settings->m_compassAdjDeclination/2.0f);
        
        m_lastFusionTime = data.timestamp;
        calculatePose(m_accel, m_compass, settings->m_compassAdjDeclination);

        //  initialize the poses

        m_stateQ.fromEuler(m_measuredPose);
        m_fusionQPose = m_stateQ;
        m_fusionPose = m_measuredPose;
        m_firstTime = false;

    } else { // not first time
        m_timeDelta = (RTFLOAT)(data.timestamp - m_lastFusionTime) / (RTFLOAT)1000000;
        m_lastFusionTime = data.timestamp;
        if (m_timeDelta <= 0)
            return;

        calculatePose(data.accel, data.compass, settings->m_compassAdjDeclination);
    
        // =================================================
        //    AHRS
        //
        // Previous Q Pose; short name local variables for readability
        float q1 = m_stateQ.scalar();
        float q2 = m_stateQ.x();
        float q3 = m_stateQ.y();
        float q4 = m_stateQ.z();   

        float ax, ay, az, mx, my, mz, gx, gy, gz;           // accelerometer, magnetometer, gyroscope
        		
        float gerrx, gerry, gerrz; // gyro bias error

        float norm;
        float hx, hy, _2bx, _2bz;
        float s1, s2, s3, s4;
        float qDot1, qDot2, qDot3, qDot4;

        float _2q1mx,_2q1my, _2q1mz,_2q2mx;
        float _4bx, _4bz;
        float _2q1, _2q2, _2q3, _2q4;
        float q1q1, q1q2, q1q3, q1q4, q2q2, q2q3, q2q4, q3q4, q3q3, q4q4;  
        float _4q1, _4q2, _4q3, _8q2, _8q3;
        float _2q3q4, _2q1q3;

        if (m_enableCompass) {
          mx = m_compass.x();
          my = m_compass.y();
          mz = m_compass.z();
	  
        } else {
          mx = 0.0f;
          my = 0.0f;
          mz = 0.0f; 
		}   

        if (m_enableAccel) {
          ax = m_accel.x();
          ay = m_accel.y();
          az = m_accel.z();
        } else {
          ax = 0.0f;
          ay = 0.0f;
          az = 0.0f; }   

       if (m_enableGyro) {
          gx=m_gyro.x();
          gy=m_gyro.y();
          gz=m_gyro.z();
        } else { return; }   // We need to have valid gyroscope data

        ////////////////////////////////////////////////////////////////////////////
        // Regular Algorithm

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

        if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
            // Use this algorithm if accelerometer is valid 
            // If accelerometer is not valid, update pose based on previous qDot

            // Normalise accelerometer measurement
            norm = sqrt(ax * ax + ay * ay + az * az);
            if (norm == 0.0) return; // handle NaN
            ax /= norm;
            ay /= norm;
            az /= norm;  

            // Auxiliary variables to avoid repeated arithmetic
             _2q1 = 2.0f * q1;
             _2q2 = 2.0f * q2;
             _2q3 = 2.0f * q3;
             _2q4 = 2.0f * q4;
             q1q1 = q1 * q1;
             q2q2 = q2 * q2;
             q3q3 = q3 * q3;
             q4q4 = q4 * q4;  

            if(((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f))) {
                // If magnetometer is invalid

                // Auxiliary variables to avoid repeated arithmetic
                _4q1 = 4.0f * q1;
                _4q2 = 4.0f * q2;
                _4q3 = 4.0f * q3;
                _8q2 = 8.0f * q2;
                _8q3 = 8.0f * q3;

                // Gradient decent algorithm corrective step
                s1 = _4q1 * q3q3 + _2q3 * ax + _4q1 * q2q2 - _2q2 * ay;
                s2 = _4q2 * q4q4 - _2q4 * ax + 4.0f * q1q1 * q2 - _2q1 * ay - _4q2 + _8q2 * q2q2 + _8q2 * q3q3 + _4q2 * az;
                s3 = 4.0f * q1q1 * q3 + _2q1 * ax + _4q3 * q4q4 - _2q4 * ay - _4q3 + _8q3 * q2q2 + _8q3 * q3q3 + _4q3 * az;
                s4 = 4.0f * q2q2 * q4 - _2q2 * ax + 4.0f * q3q3 * q4 - _2q3 * ay;

             } else { 
                // Valid magnetometer available use this code

                // Normalise magnetometer measurement
                norm = sqrt(mx * mx + my * my + mz * mz);
                if (norm == 0.0) return; // handle NaN
                mx /= norm;
                my /= norm;
                mz /= norm;

                // Auxiliary variables to avoid repeated arithmetic
                q1q2 = q1 * q2;
                q1q3 = q1 * q3;
                q1q4 = q1 * q4;
                q2q3 = q2 * q3;
                q2q4 = q2 * q4;
                q3q4 = q3 * q4;
                _2q1q3 = 2.0f * q1q3;
                _2q3q4 = 2.0f * q3q4;
                _2q1mx = 2.0f * q1 * mx;
                _2q1my = 2.0f * q1 * my;
                _2q1mz = 2.0f * q1 * mz;
                _2q2mx = 2.0f * q2 * mx;

                // Reference direction of Earth's magnetic field
                 hx = mx * q1q1 - _2q1my * q4 + _2q1mz * q3 + mx * q2q2 + _2q2 * my * q3 + _2q2 * mz * q4 - mx * q3q3 - mx * q4q4;
                 hy = _2q1mx * q4 + my * q1q1 - _2q1mz * q2 + _2q2mx * q3 - my * q2q2 + my * q3q3 + _2q3 * mz * q4 - my * q4q4;
                _2bx = sqrt(hx * hx + hy * hy);
                _2bz = -_2q1mx * q3 + _2q1my * q2 + mz * q1q1 + _2q2mx * q4 - mz * q2q2 + _2q3 * my * q4 - mz * q3q3 + mz * q4q4;
                _4bx = 2.0f * _2bx;
                _4bz = 2.0f * _2bz;  

                // Gradient decent algorithm corrective step
                s1 = -_2q3 * (2.0f * q2q4 - _2q1q3 - ax) + _2q2 * (2.0f * q1q2 + _2q3q4 - ay) - _2bz * q3 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q4 + _2bz * q2) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q3 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
                s2 =  _2q4 * (2.0f * q2q4 - _2q1q3 - ax) + _2q1 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q2 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + _2bz * q4 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q3 + _2bz * q1) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q4 - _4bz * q2) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
                s3 = -_2q1 * (2.0f * q2q4 - _2q1q3 - ax) + _2q4 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q3 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + (-_4bx * q3 - _2bz * q1) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q2 + _2bz * q4) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q1 - _4bz * q3) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
                s4 =  _2q2 * (2.0f * q2q4 - _2q1q3 - ax) + _2q3 * (2.0f * q1q2 + _2q3q4 - ay) + (-_4bx * q4 + _2bz * q2) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q1 + _2bz * q3) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q2 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);

             } // end valid magnetometer

            norm = sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4);    // normalise step magnitude
            if (norm == 0.0) return; // handle NaN
            s1 /= norm;
            s2 /= norm;
            s3 /= norm;
            s4 /= norm; 

            // Compute estimated gyroscope biases
            gerrx = _2q1 * s2 - _2q2 * s1 - _2q3 * s4 + _2q4 * s3;
            gerry = _2q1 * s3 + _2q2 * s4 - _2q3 * s1 - _2q4 * s2;
            gerrz = _2q1 * s4 - _2q2 * s3 + _2q3 * s2 - _2q4 * s1;

            // Compute and remove gyroscope biases
            m_gbiasx += gerrx * m_timeDelta * m_zeta;
            m_gbiasy += gerry * m_timeDelta * m_zeta;
            m_gbiasz += gerrz * m_timeDelta * m_zeta;

            gx -= m_gbiasx;
            gy -= m_gbiasy;
            gz -= m_gbiasz;

             // Apply feedback step
            qDot1 -= m_beta * s1;
            qDot2 -= m_beta * s2;
            qDot3 -= m_beta * s3;
            qDot4 -= m_beta * s4;

        } // end if valid accelerometer

        // Integrate to yield quaternion
        q1 += qDot1 * m_timeDelta;
        q2 += qDot2 * m_timeDelta;
        q3 += qDot3 * m_timeDelta;
        q4 += qDot4 * m_timeDelta;

        // normalise quaternion
        norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);    
        if (norm == 0.0) return; // handle NaN
        q1 /= norm;
        q2 /= norm;
        q3 /= norm;
        q4 /= norm;
	
        m_stateQ.setScalar(q1);
        m_stateQ.setX(q2);
        m_stateQ.setY(q3);
        m_stateQ.setZ(q4);

        // Rotate Quaternion by Magnetic Declination
        // m_stateQ = q_declination * m_statqQ;

        /*
        SAGE
                N.<c,d,q1,q2,q3,q4,cos_theta_half, sin_theta_half> = QQ[]
                H.<i,j,k> = QuaternionAlgebra(c,d)
                q = q1 + q2 * i + q3 * j + q4 * k
                // here rotation is around gravity vector by theta
                mag_declination = cos_theta_half + sin_theta_half * (0 * i + 0 * j+ 1*k)
                q * mag_declination

                s : -q4*sin_theta_half + q1*cos_theta_half  
                x :  q3*sin_theta_half + q2*cos_theta_half 
                y : -q2*sin_theta_half + q3*cos_theta_half
                z :  q4*cos_theta_half + q1*sin_theta_half
                */

        m_stateQdec.setScalar(q1*m_cos_theta_half - q4*m_sin_theta_half);
        m_stateQdec.setX(q3*m_sin_theta_half + q2*m_cos_theta_half);
        m_stateQdec.setY(q3*m_cos_theta_half - q2*m_sin_theta_half);
        m_stateQdec.setZ(q4*m_cos_theta_half + q1*m_sin_theta_half);

    } // end not first time

	
    // =================================================

    if (m_enableCompass || m_enableAccel) {
            m_stateQError = m_measuredQPose - m_stateQ;
    } else {
            m_stateQError = RTQuaternion();
    }

    m_stateQdec.toEuler(m_fusionPose);
    m_fusionQPose = m_stateQdec;

    if (m_debug | settings->m_fusionDebug) {
        HAL_INFO(RTMath::displayRadians("Measured pose", m_measuredPose));
        HAL_INFO(RTMath::displayRadians("AHRS pose", m_fusionPose));
        HAL_INFO(RTMath::displayRadians("Measured quat", m_measuredPose));
        HAL_INFO(RTMath::display("AHRS quat", m_stateQ));
        HAL_INFO(RTMath::display("Error quat", m_stateQError));
        HAL_INFO3("AHRS Gyro Bias: %+f, %+f, %+f\n", m_gbiasx, m_gbiasy, m_gbiasz);
     }

    data.fusionPoseValid = true;
    data.fusionQPoseValid = true;
    data.fusionPose = m_fusionPose;
    data.fusionQPose = m_fusionQPose;
} //