mirror of https://github.com/ArduPilot/ardupilot
AP_NavEKF2: Improve ground based magnetic anomaly protection for copter
The toilet bowling check during early flight has been removed. This check caused problems where bad compass calibration was the cause of the toilet bowling and resetting to the compass was a bad option. The handling of simultaneous failed mag and velocity innovations is already handled outside the EKF by the failsafe. A check for yaw errors due to a ground based magnetic anomaly has been introduced. The logic for in-flight yaw and magnetic field resets has been cleaned up and variable names improved.
This commit is contained in:
parent
703f56908f
commit
dc6836988c
|
@ -98,7 +98,7 @@ void NavEKF2_core::setWindMagStateLearningMode()
|
|||
bool magCalRequested =
|
||||
((magCal == 0) && inFlight) || // when flying
|
||||
((magCal == 1) && manoeuvring) || // when manoeuvring
|
||||
((magCal == 3) && firstInflightYawInit && firstInflightMagInit) || // when initial in-air yaw and mag field reset is complete
|
||||
((magCal == 3) && finalInflightYawInit && finalInflightMagInit) || // when initial in-air yaw and mag field reset is complete
|
||||
(magCal == 4); // all the time
|
||||
|
||||
// Deny mag calibration request if we aren't using the compass, it has been inhibited by the user,
|
||||
|
@ -116,7 +116,7 @@ void NavEKF2_core::setWindMagStateLearningMode()
|
|||
P[index][index] = sq(frontend->_magNoise);
|
||||
}
|
||||
// request a reset of the yaw and magnetic field states if not done before
|
||||
if (!magStateInitComplete || (!firstInflightMagInit && inFlight)) {
|
||||
if (!magStateInitComplete || (!finalInflightMagInit && inFlight)) {
|
||||
magYawResetRequest = true;
|
||||
}
|
||||
}
|
||||
|
@ -124,8 +124,8 @@ void NavEKF2_core::setWindMagStateLearningMode()
|
|||
// If on ground we clear the flag indicating that the magnetic field in-flight initialisation has been completed
|
||||
// because we want it re-done for each takeoff
|
||||
if (onGround) {
|
||||
firstInflightYawInit = false;
|
||||
firstInflightMagInit = false;
|
||||
finalInflightYawInit = false;
|
||||
finalInflightMagInit = false;
|
||||
}
|
||||
|
||||
// Adjust the indexing limits used to address the covariance, states and other EKF arrays to avoid unnecessary operations
|
||||
|
@ -304,7 +304,7 @@ void NavEKF2_core::recordYawReset()
|
|||
{
|
||||
yawAlignComplete = true;
|
||||
if (inFlight) {
|
||||
firstInflightYawInit = true;
|
||||
finalInflightYawInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,42 +20,68 @@ extern const AP_HAL::HAL& hal;
|
|||
// Control reset of yaw and magnetic field states
|
||||
void NavEKF2_core::controlMagYawReset()
|
||||
{
|
||||
// Use a quaternion division to calcualte the delta quaternion between the rotation at the current and last time
|
||||
Quaternion deltaQuat = stateStruct.quat / prevQuatMagReset;
|
||||
prevQuatMagReset = stateStruct.quat;
|
||||
// Quaternion and delta rotation vector that are re-used for different calculations
|
||||
Vector3f deltaRotVecTemp;
|
||||
Quaternion deltaQuatTemp;
|
||||
|
||||
// convert the quaternion to a rotation vector and find its length
|
||||
Vector3f deltaRotVec;
|
||||
deltaQuat.to_axis_angle(deltaRotVec);
|
||||
bool flightResetAllowed = false;
|
||||
bool initialResetAllowed = false;
|
||||
if (!finalInflightYawInit) {
|
||||
// Use a quaternion division to calculate the delta quaternion between the rotation at the current and last time
|
||||
deltaQuatTemp = stateStruct.quat / prevQuatMagReset;
|
||||
prevQuatMagReset = stateStruct.quat;
|
||||
|
||||
// check if the spin rate is OK - high spin rates can cause angular alignment errors
|
||||
bool angRateOK = deltaRotVec.length() < 0.1745f;
|
||||
// convert the quaternion to a rotation vector and find its length
|
||||
deltaQuatTemp.to_axis_angle(deltaRotVecTemp);
|
||||
|
||||
// a flight reset is allowed if we are not spinning too fast and are in flight
|
||||
bool flightResetAllowed = angRateOK && inFlight;
|
||||
// check if the spin rate is OK - high spin rates can cause angular alignment errors
|
||||
bool angRateOK = deltaRotVecTemp.length() < 0.1745f;
|
||||
|
||||
initialResetAllowed = angRateOK;
|
||||
flightResetAllowed = angRateOK && !onGround;
|
||||
|
||||
}
|
||||
|
||||
// check that we have reached a height where ground magnetic interference effects are insignificant
|
||||
bool hgtCheckPassed = (stateStruct.position.z - posDownAtTakeoff) < -5.0f;
|
||||
// and can perform a final reset of the yaw and field states
|
||||
bool finalResetRequest = false;
|
||||
if (flightResetAllowed && !assume_zero_sideslip()) {
|
||||
finalResetRequest = (stateStruct.position.z - posDownAtTakeoff) < -5.0f;
|
||||
}
|
||||
|
||||
// check for 'toilet bowling' which is characterised by large yaw and velocity innovations and caused by bad yaw alignment
|
||||
// this can occur if there is severe magnetic interference on the ground
|
||||
bool toiletBowling = (yawTestRatio > 1.0f) && (velTestRatio > 1.0f);
|
||||
// Check for bad yaw casued by ground based magnetic anomaly
|
||||
bool interimResetRequest = false;
|
||||
if (flightResetAllowed && !assume_zero_sideslip()) {
|
||||
// check for increasing height
|
||||
bool hgtIncreasing = (posDownAtLastMagReset-stateStruct.position.z) > 0.5f;
|
||||
float yawInnovIncrease = fabsf(innovYaw) - fabsf(yawInnovAtLastMagReset);
|
||||
|
||||
// calculate if an in flight reset is required
|
||||
bool flightResetRequired = (!firstInflightYawInit && (hgtCheckPassed || toiletBowling));
|
||||
// check for increasing yaw innovations
|
||||
bool yawInnovIncreasing = yawInnovIncrease > 0.25f;
|
||||
|
||||
// check that the yaw innovations haven't been caused by a large change in attitude
|
||||
deltaQuatTemp = quatAtLastMagReset / stateStruct.quat;
|
||||
deltaQuatTemp.to_axis_angle(deltaRotVecTemp);
|
||||
bool largeAngleChange = deltaRotVecTemp.length() > yawInnovIncrease;
|
||||
|
||||
// if yaw innovations and height have increased and we haven't rotated much
|
||||
// then we are climbing away from a ground based magnetic anomaly and need to reset
|
||||
interimResetRequest = hgtIncreasing && yawInnovIncreasing && !largeAngleChange;
|
||||
}
|
||||
|
||||
// an initial reset is required if we have not yet aligned the yaw angle
|
||||
bool initialResetRequired = !yawAlignComplete;
|
||||
bool initialResetRequest = initialResetAllowed && !yawAlignComplete;
|
||||
|
||||
// a combined yaw angle and magnetic field reset can be initiated by:
|
||||
magYawResetRequest = magYawResetRequest || // an external request
|
||||
(initialResetRequired && angRateOK) || // we need to do an initial alignment and aren't rotating too fast
|
||||
(flightResetRequired && flightResetAllowed && !assume_zero_sideslip()); // conditions are right to do the in-flight re-alignment
|
||||
initialResetRequest || // an initial alignment performed by all vehicle types using magnetometer
|
||||
interimResetRequest || // an interim alignment required to recover from ground based magnetic anomaly
|
||||
finalResetRequest; // the final reset when we have acheived enough height to be in stable magnetic field environment
|
||||
|
||||
// Perform a reset of magnetic field states and reset yaw to corrected magnetic heading
|
||||
if (magYawResetRequest || magStateResetRequest) {
|
||||
|
||||
// gett he eeruler angles from the current state estimate
|
||||
// get the euler angles from the current state estimate
|
||||
Vector3f eulerAngles;
|
||||
stateStruct.quat.to_euler(eulerAngles.x, eulerAngles.y, eulerAngles.z);
|
||||
|
||||
|
@ -80,9 +106,11 @@ void NavEKF2_core::controlMagYawReset()
|
|||
hal.console->printf("EKF2 IMU%u initial yaw alignment complete\n",(unsigned)imu_index);
|
||||
}
|
||||
|
||||
// send initial in-flight yaw alignment status to console
|
||||
if (!firstInflightYawInit && inFlight) {
|
||||
// send in-flight yaw alignment status to console
|
||||
if (finalResetRequest) {
|
||||
hal.console->printf("EKF2 IMU%u in-flight yaw alignment complete\n",(unsigned)imu_index);
|
||||
} else if (interimResetRequest) {
|
||||
hal.console->printf("EKF2 IMU%u ground mag anomaly, yaw re-aligned\n",(unsigned)imu_index);
|
||||
}
|
||||
|
||||
// update the yaw reset completed status
|
||||
|
@ -90,13 +118,20 @@ void NavEKF2_core::controlMagYawReset()
|
|||
|
||||
// clear the yaw reset request flag
|
||||
magYawResetRequest = false;
|
||||
|
||||
// clear the complete flags if an interim reset has been performed to allow subsequent
|
||||
// and final reset to occur
|
||||
if (interimResetRequest) {
|
||||
finalInflightYawInit = false;
|
||||
finalInflightMagInit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request an in-flight check of heading against GPS and reset if necessary
|
||||
// this can only be used by vehicles that can use a zero sideslip assumption (Planes)
|
||||
// this allows recovery for heading alignment errors due to compass faults
|
||||
if (!firstInflightYawInit && inFlight && assume_zero_sideslip()) {
|
||||
if (!finalInflightYawInit && inFlight && assume_zero_sideslip()) {
|
||||
gpsYawResetRequest = true;
|
||||
}
|
||||
|
||||
|
@ -1044,8 +1079,13 @@ void NavEKF2_core::recordMagReset()
|
|||
{
|
||||
magStateInitComplete = true;
|
||||
if (inFlight) {
|
||||
firstInflightMagInit = true;
|
||||
finalInflightMagInit = true;
|
||||
}
|
||||
// take a snap-shot of the vertical position, quaternion and yaw innovation to use as a reference
|
||||
// for post alignment checks
|
||||
posDownAtLastMagReset = stateStruct.position.z;
|
||||
quatAtLastMagReset = stateStruct.quat;
|
||||
yawInnovAtLastMagReset = innovYaw;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -350,7 +350,7 @@ void NavEKF2_core::getMagXYZ(Vector3f &magXYZ) const
|
|||
bool NavEKF2_core::getMagOffsets(uint8_t mag_idx, Vector3f &magOffsets) const
|
||||
{
|
||||
// compass offsets are valid if we have finalised magnetic field initialisation and magnetic field learning is not prohibited and primary compass is valid
|
||||
if (mag_idx == magSelectIndex && firstInflightMagInit && (effective_magCal() != 2) && _ahrs->get_compass()->healthy(magSelectIndex)) {
|
||||
if (mag_idx == magSelectIndex && finalInflightMagInit && (effective_magCal() != 2) && _ahrs->get_compass()->healthy(magSelectIndex)) {
|
||||
magOffsets = _ahrs->get_compass()->get_offsets(magSelectIndex) - stateStruct.body_magfield*1000.0f;
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -184,7 +184,7 @@ bool NavEKF2_core::calcGpsGoodToAlign(void)
|
|||
// fail if magnetometer innovations are outside limits indicating bad yaw
|
||||
// with bad yaw we are unable to use GPS
|
||||
bool yawFail;
|
||||
if ((magTestRatio.x > 1.0f || magTestRatio.y > 1.0f) && (frontend->_gpsCheck & MASK_GPS_YAW_ERR)) {
|
||||
if ((magTestRatio.x > 1.0f || magTestRatio.y > 1.0f || yawTestRatio > 1.0f) && (frontend->_gpsCheck & MASK_GPS_YAW_ERR)) {
|
||||
yawFail = true;
|
||||
} else {
|
||||
yawFail = false;
|
||||
|
@ -365,6 +365,13 @@ void NavEKF2_core::detectFlight()
|
|||
posDownAtTakeoff = stateStruct.position.z;
|
||||
// store the range finder measurement which will be used as a reference to detect when we have taken off
|
||||
rngAtStartOfFlight = rangeDataNew.rng;
|
||||
// if the magnetic field states have been set, then continue to update the vertical position
|
||||
// quaternion and yaw innovation snapshots to use as a reference when we start to fly.
|
||||
if (magStateInitComplete) {
|
||||
posDownAtLastMagReset = stateStruct.position.z;
|
||||
quatAtLastMagReset = stateStruct.quat;
|
||||
yawInnovAtLastMagReset = innovYaw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -153,8 +153,8 @@ void NavEKF2_core::InitialiseVariables()
|
|||
tasTimeout = true;
|
||||
badMagYaw = false;
|
||||
badIMUdata = false;
|
||||
firstInflightYawInit = false;
|
||||
firstInflightMagInit = false;
|
||||
finalInflightYawInit = false;
|
||||
finalInflightMagInit = false;
|
||||
dtIMUavg = 0.0025f;
|
||||
dtEkfAvg = 0.01f;
|
||||
dt = 0;
|
||||
|
@ -258,6 +258,9 @@ void NavEKF2_core::InitialiseVariables()
|
|||
magStateInitComplete = false;
|
||||
magYawResetRequest = false;
|
||||
gpsYawResetRequest = false;
|
||||
posDownAtLastMagReset = stateStruct.position.z;
|
||||
yawInnovAtLastMagReset = 0.0f;
|
||||
quatAtLastMagReset = stateStruct.quat;
|
||||
|
||||
// zero data buffers
|
||||
storedIMU.reset();
|
||||
|
@ -1387,6 +1390,7 @@ Quaternion NavEKF2_core::calcQuatAndFieldStates(float roll, float pitch)
|
|||
|
||||
// record the fact we have initialised the magnetic field states
|
||||
recordMagReset();
|
||||
|
||||
} else {
|
||||
// this function should not be called if there is no compass data but if is is, return the
|
||||
// current attitude
|
||||
|
|
|
@ -715,8 +715,6 @@ private:
|
|||
float tasTestRatio; // sum of squares of true airspeed innovation divided by fail threshold
|
||||
bool inhibitWindStates; // true when wind states and covariances are to remain constant
|
||||
bool inhibitMagStates; // true when magnetic field states and covariances are to remain constant
|
||||
bool firstInflightYawInit; // true when the first post takeoff initialisation of yaw angle has been performed
|
||||
bool firstInflightMagInit; // true when the first post takeoff initialisation of magnetic field states been performed
|
||||
bool gpsNotAvailable; // bool true when valid GPS data is not available
|
||||
bool isAiding; // true when the filter is fusing position, velocity or flow measurements
|
||||
bool prevIsAiding; // isAiding from previous frame
|
||||
|
@ -789,9 +787,6 @@ private:
|
|||
bool startPredictEnabled; // boolean true when the frontend has given permission to start a new state prediciton cycele
|
||||
uint8_t localFilterTimeStep_ms; // average number of msec between filter updates
|
||||
float posDownObsNoise; // observation noise variance on the vertical position used by the state and covariance update step (m^2)
|
||||
bool magStateResetRequest; // true if magnetic field states need to be reset using the magneteomter measurements
|
||||
bool magYawResetRequest; // true if the vehicle yaw and magnetic field states need to be reset using the magnetometer measurements
|
||||
bool gpsYawResetRequest; // true if the vehicle yaw needs to be reset to the GPS course
|
||||
|
||||
// variables used to calculate a vertical velocity that is kinematically consistent with the verical position
|
||||
float posDownDerivative; // Rate of chage of vertical position (dPosD/dt) in m/s. This is the first time derivative of PosD.
|
||||
|
@ -887,6 +882,16 @@ private:
|
|||
uint32_t touchdownExpectedSet_ms; // system time at which expectGndEffectTouchdown was set
|
||||
float meaHgtAtTakeOff; // height measured at commencement of takeoff
|
||||
|
||||
// control of post takeoff magentic field and heading resets
|
||||
bool finalInflightYawInit; // true when the final post takeoff initialisation of yaw angle has been performed
|
||||
bool finalInflightMagInit; // true when the final post takeoff initialisation of magnetic field states been performed
|
||||
bool magStateResetRequest; // true if magnetic field states need to be reset using the magneteomter measurements
|
||||
bool magYawResetRequest; // true if the vehicle yaw and magnetic field states need to be reset using the magnetometer measurements
|
||||
bool gpsYawResetRequest; // true if the vehicle yaw needs to be reset to the GPS course
|
||||
float posDownAtLastMagReset; // vertical position last time the mag states were reset (m)
|
||||
float yawInnovAtLastMagReset; // magnetic yaw innovation last time the yaw and mag field states were reset (rad)
|
||||
Quaternion quatAtLastMagReset; // quaternion states last time the mag states were reset
|
||||
|
||||
// flags indicating severe numerical errors in innovation variance calculation for different fusion operations
|
||||
struct {
|
||||
bool bad_xmag:1;
|
||||
|
|
Loading…
Reference in New Issue