mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-02-23 00:04:02 -04:00
AP_NavEKF: Improve takeoff with optical flow and range data loss
Range finder and optical flow data can drop-out and be reliable very close to ground. these patches enable the takeoff to be more relaibly detected and constrain optical flow navigation drift in the first part of takeoff.
This commit is contained in:
parent
3e061b174e
commit
e48171ab11
@ -979,38 +979,34 @@ void NavEKF::SelectFlowFusion()
|
||||
// Perform Data Checks
|
||||
// Check if the optical flow data is still valid
|
||||
flowDataValid = ((imuSampleTime_ms - flowValidMeaTime_ms) < 1000);
|
||||
// Check if the optical flow sensor has timed out
|
||||
bool flowSensorTimeout = ((imuSampleTime_ms - flowValidMeaTime_ms) > 5000);
|
||||
// Check if the fusion has timed out (flow measurements have been rejected for too long)
|
||||
bool flowFusionTimeout = ((imuSampleTime_ms - prevFlowFuseTime_ms) > 5000);
|
||||
// check is the terrain offset estimate is still valid
|
||||
gndOffsetValid = ((imuSampleTime_ms - gndHgtValidTime_ms) < 5000);
|
||||
// Perform tilt check
|
||||
bool tiltOK = (Tnb_flow.c.z > DCM33FlowMin);
|
||||
|
||||
// if we don't have valid flow measurements and are relying on them, dead reckon using current velocity vector
|
||||
// If the flow measurements have been rejected for too long and we are relying on them, then reset the velocities to an estimate based on the flow and range data
|
||||
if (!flowDataValid && PV_AidingMode == AID_RELATIVE) {
|
||||
constVelMode = true;
|
||||
constPosMode = false; // always clear constant position mode if constant velocity mode is active
|
||||
} else if (flowDataValid && flowFusionTimeout && PV_AidingMode == AID_RELATIVE) {
|
||||
// we need to reset the velocities to a value estimated from the flow data
|
||||
// estimate the range
|
||||
float initPredRng = max((terrainState - state.position[2]),RNG_MEAS_ON_GND) / Tnb_flow.c.z;
|
||||
// multiply the range by the LOS rates to get an estimated XY velocity in body frame
|
||||
Vector3f initVel;
|
||||
initVel.x = -flowRadXYcomp[1]*initPredRng;
|
||||
initVel.y = flowRadXYcomp[0]*initPredRng;
|
||||
// rotate into earth frame
|
||||
initVel = Tbn_flow*initVel;
|
||||
// set horizontal velocity states
|
||||
state.velocity.x = initVel.x;
|
||||
state.velocity.y = initVel.y;
|
||||
// clear any hold modes
|
||||
constVelMode = false;
|
||||
lastConstVelMode = false;
|
||||
} else if (flowDataValid) {
|
||||
// clear the constant velocity mode now we have good data
|
||||
constVelMode = false;
|
||||
lastConstVelMode = false;
|
||||
// Constrain measurements to zero if we are using optical flow and are on the ground
|
||||
if (_fusionModeGPS == 3 && !takeOffDetected && vehicleArmed) {
|
||||
flowRadXYcomp[0] = 0.0f;
|
||||
flowRadXYcomp[1] = 0.0f;
|
||||
flowRadXY[0] = 0.0f;
|
||||
flowRadXY[1] = 0.0f;
|
||||
omegaAcrossFlowTime.zero();
|
||||
}
|
||||
// If the flow measurements have been rejected for too long and we are relying on them, then revert to constant position mode
|
||||
if ((flowSensorTimeout || flowFusionTimeout) && PV_AidingMode == AID_RELATIVE) {
|
||||
constVelMode = false; // always clear constant velocity mode if constant velocity mode is active
|
||||
constPosMode = true;
|
||||
PV_AidingMode = AID_NONE;
|
||||
// reset the velocity
|
||||
ResetVelocity();
|
||||
// store the current position to be used to keep reporting the last known position
|
||||
lastKnownPositionNE.x = state.position.x;
|
||||
lastKnownPositionNE.y = state.position.y;
|
||||
// reset the position
|
||||
ResetPosition();
|
||||
}
|
||||
// if we do have valid flow measurements, fuse data into a 1-state EKF to estimate terrain height
|
||||
// we don't do terrain height estimation in optical flow only mode as the ground becomes our zero height reference
|
||||
@ -4137,11 +4133,19 @@ void NavEKF::readHgtData()
|
||||
// calculate offset to baro data that enables baro to be used as a backup
|
||||
// filter offset to reduce effect of baro noise and other transient errors on estimate
|
||||
baroHgtOffset = 0.2f * (_baro.get_altitude() + state.position.z) + 0.8f * baroHgtOffset;
|
||||
} else {
|
||||
} else if (vehicleArmed && takeOffDetected) {
|
||||
// use baro measurement and correct for baro offset - failsafe use only as baro will drift
|
||||
hgtMea = max(_baro.get_altitude() - baroHgtOffset, rngOnGnd);
|
||||
// get states that were stored at the time closest to the measurement time, taking measurement delay into account
|
||||
RecallStates(statesAtHgtTime, (imuSampleTime_ms - msecHgtDelay));
|
||||
} else {
|
||||
// If we are on ground and have no range finder reading, assume the nominal on-ground height
|
||||
hgtMea = rngOnGnd;
|
||||
// get states that were stored at the time closest to the measurement time, taking measurement delay into account
|
||||
statesAtHgtTime = state;
|
||||
// calculate offset to baro data that enables baro to be used as a backup
|
||||
// filter offset to reduce effect of baro noise and other transient errors on estimate
|
||||
baroHgtOffset = 0.2f * (_baro.get_altitude() + state.position.z) + 0.8f * baroHgtOffset;
|
||||
}
|
||||
} else {
|
||||
// use baro measurement and correct for baro offset
|
||||
@ -4228,15 +4232,19 @@ void NavEKF::writeOptFlowMeas(uint8_t &rawFlowQuality, Vector2f &rawFlowRates, V
|
||||
// calculate bias errors on flow sensor gyro rates, but protect against spikes in data
|
||||
flowGyroBias.x = 0.99f * flowGyroBias.x + 0.01f * constrain_float((rawGyroRates.x - omegaAcrossFlowTime.x),-0.1f,0.1f);
|
||||
flowGyroBias.y = 0.99f * flowGyroBias.y + 0.01f * constrain_float((rawGyroRates.y - omegaAcrossFlowTime.y),-0.1f,0.1f);
|
||||
// don't use data with a low quality indicator or extreme rates (helps catch corrupt sesnor data)
|
||||
if (rawFlowQuality > 50 && rawFlowRates.length() < 4.2f && rawGyroRates.length() < 4.2f) {
|
||||
// recall vehicle states at mid sample time for flow observations allowing for delays
|
||||
RecallStates(statesAtFlowTime, imuSampleTime_ms - _msecFLowDelay - flowTimeDeltaAvg_ms/2);
|
||||
// calculate rotation matrices at mid sample time for flow observations
|
||||
statesAtFlowTime.quat.rotation_matrix(Tbn_flow);
|
||||
Tnb_flow = Tbn_flow.transposed();
|
||||
// check for takeoff if relying on optical flow and zero measurements until takeoff detected
|
||||
// if we haven't taken off - constrain position and velocity states
|
||||
if (_fusionModeGPS == 3) {
|
||||
detectOptFlowTakeoff();
|
||||
}
|
||||
// recall vehicle states at mid sample time for flow observations allowing for delays
|
||||
RecallStates(statesAtFlowTime, imuSampleTime_ms - _msecFLowDelay - flowTimeDeltaAvg_ms/2);
|
||||
// calculate rotation matrices at mid sample time for flow observations
|
||||
statesAtFlowTime.quat.rotation_matrix(Tbn_flow);
|
||||
Tnb_flow = Tbn_flow.transposed();
|
||||
// don't use data with a low quality indicator or extreme rates (helps catch corrupt sensor data)
|
||||
if ((rawFlowQuality > 0) && rawFlowRates.length() < 4.2f && rawGyroRates.length() < 4.2f) {
|
||||
// correct flow sensor rates for bias
|
||||
|
||||
omegaAcrossFlowTime.x = rawGyroRates.x - flowGyroBias.x;
|
||||
omegaAcrossFlowTime.y = rawGyroRates.y - flowGyroBias.y;
|
||||
// write uncorrected flow rate measurements that will be used by the focal length scale factor estimator
|
||||
@ -4695,13 +4703,14 @@ void NavEKF::getFilterStatus(nav_filter_status &status) const
|
||||
status.flags.attitude = !state.quat.is_nan() && filterHealthy; // attitude valid (we need a better check)
|
||||
status.flags.horiz_vel = someHorizRefData && notDeadReckoning && filterHealthy; // horizontal velocity estimate valid
|
||||
status.flags.vert_vel = someVertRefData && filterHealthy; // vertical velocity estimate valid
|
||||
status.flags.horiz_pos_rel = (doingFlowNav || doingWindRelNav || doingNormalGpsNav) && notDeadReckoning && filterHealthy; // relative horizontal position estimate valid
|
||||
status.flags.horiz_pos_rel = ((doingFlowNav && gndOffsetValid) || doingWindRelNav || doingNormalGpsNav) && notDeadReckoning && filterHealthy; // relative horizontal position estimate valid
|
||||
status.flags.horiz_pos_abs = !gpsAidingBad && doingNormalGpsNav && notDeadReckoning && filterHealthy; // absolute horizontal position estimate valid
|
||||
status.flags.vert_pos = !hgtTimeout && filterHealthy; // vertical position estimate valid
|
||||
status.flags.terrain_alt = gndOffsetValid && filterHealthy; // terrain height estimate valid
|
||||
status.flags.const_pos_mode = constPosMode && filterHealthy; // constant position mode
|
||||
status.flags.pred_horiz_pos_rel = (optFlowNavPossible || gpsNavPossible) && filterHealthy; // we should be able to estimate a relative position when we enter flight mode
|
||||
status.flags.pred_horiz_pos_abs = gpsNavPossible && filterHealthy; // we should be able to estimate an absolute position when we enter flight mode
|
||||
status.flags.takeoff_detected = takeOffDetected; // takeoff for optical flow navigation has been detected
|
||||
}
|
||||
|
||||
// send an EKF_STATUS message to GCS
|
||||
@ -4753,6 +4762,8 @@ void NavEKF::performArmingChecks()
|
||||
}
|
||||
// zero stored velocities used to do dead-reckoning
|
||||
heldVelNE.zero();
|
||||
// reset the flag that indicates takeoff for use by optical flow navigation
|
||||
takeOffDetected = false;
|
||||
// set various useage modes based on the condition at arming. These are then held until the vehicle is disarmed.
|
||||
if (!vehicleArmed) {
|
||||
PV_AidingMode = AID_NONE; // When dis-armed, we only estimate orientation & height using the constant position mode
|
||||
@ -4778,6 +4789,16 @@ void NavEKF::performArmingChecks()
|
||||
constPosMode = true;
|
||||
constVelMode = false; // always clear constant velocity mode if constant position mode is active
|
||||
}
|
||||
// Reset the last valid flow measurement time
|
||||
flowValidMeaTime_ms = imuSampleTime_ms;
|
||||
// Reset the last valid flow fusion time
|
||||
prevFlowFuseTime_ms = imuSampleTime_ms;
|
||||
// this avoids issues casued by the time delay associated with arming that can trigger short timeouts
|
||||
rngValidMeaTime_ms = imuSampleTime_ms;
|
||||
// store the range finder measurement which will be used as a reference to detect when we have taken off
|
||||
rangeAtArming = rngMea;
|
||||
// set the time at which we arm to assist with takeoff detection
|
||||
timeAtArming_ms = imuSampleTime_ms;
|
||||
} else { // arming when GPS useage is allowed
|
||||
if (gpsNotAvailable) {
|
||||
PV_AidingMode = AID_NONE; // we don't have have GPS data and will only be able to estimate orientation and height
|
||||
@ -4991,4 +5012,22 @@ void NavEKF::readRangeFinder(void)
|
||||
}
|
||||
|
||||
// Detect takeoff for optical flow navigation
|
||||
void NavEKF::detectOptFlowTakeoff(void)
|
||||
{
|
||||
if (vehicleArmed && !takeOffDetected && (imuSampleTime_ms - timeAtArming_ms) > 1000) {
|
||||
const AP_InertialSensor &ins = _ahrs->get_ins();
|
||||
Vector3f angRateVec;
|
||||
Vector3f gyroBias;
|
||||
getGyroBias(gyroBias);
|
||||
bool dual_ins = ins.get_gyro_health(0) && ins.get_gyro_health(1);
|
||||
if (dual_ins) {
|
||||
angRateVec = (ins.get_gyro(0) + ins.get_gyro(1)) * 0.5f - gyroBias;
|
||||
} else {
|
||||
angRateVec = ins.get_gyro() - gyroBias;
|
||||
}
|
||||
|
||||
takeOffDetected = (takeOffDetected || (angRateVec.length() > 0.1f) || (rngMea > (rangeAtArming + 0.1f)));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAL_CPU_CLASS
|
||||
|
@ -429,6 +429,9 @@ private:
|
||||
// Apply a median filter to range finder data
|
||||
void readRangeFinder();
|
||||
|
||||
// check if the vehicle has taken off during optical flow navigation by looking at inertial and range finder data
|
||||
void detectOptFlowTakeoff(void);
|
||||
|
||||
// EKF Mavlink Tuneable Parameters
|
||||
AP_Float _gpsHorizVelNoise; // GPS horizontal velocity measurement noise : m/s
|
||||
AP_Float _gpsVertVelNoise; // GPS vertical velocity measurement noise : m/s
|
||||
@ -703,9 +706,15 @@ private:
|
||||
AidingMode PV_AidingMode; // Defines the preferred mode for aiding of velocity and position estimates from the INS
|
||||
bool gndOffsetValid; // true when the ground offset state can still be considered valid
|
||||
bool flowXfailed; // true when the X optical flow measurement has failed the innovation consistency check
|
||||
|
||||
// Range finder
|
||||
float baroHgtOffset; // offset applied when baro height used as a backup height reference if range-finder fails
|
||||
float rngOnGnd; // Expected range finder reading in metres when vehicle is on ground
|
||||
|
||||
// Movement detector
|
||||
bool takeOffDetected; // true when takeoff for optical flow navigation has been detected
|
||||
float rangeAtArming; // range finder measurement when armed
|
||||
uint32_t timeAtArming_ms; // time in msec that the vehicle armed
|
||||
|
||||
bool haveDeltaAngles;
|
||||
|
||||
|
@ -31,6 +31,7 @@ union nav_filter_status {
|
||||
uint16_t const_pos_mode : 1; // 7 - true if we are in const position mode
|
||||
uint16_t pred_horiz_pos_rel : 1; // 8 - true if filter expects it can produce a good relative horizontal position estimate - used before takeoff
|
||||
uint16_t pred_horiz_pos_abs : 1; // 9 - true if filter expects it can produce a good absolute horizontal position estimate - used before takeoff
|
||||
uint16_t takeoff_detected : 1; // 10 - true if optical flow takeoff has been detected
|
||||
} flags;
|
||||
uint16_t value;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user