1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-25 17:25:20 +03:00

GPS rescue IMU, velocity iTerm and other fixes (#12900)

* GPS rescue update after merge of IMU fix

imuYawCog boost on error
initial turn radius adjustment
attitude debug changes
rescue velocity iterm fixes
earth referencing of yaw forced to on
sanity check failure time 30s

* vary IMU gain according to groundspeed

* Review suggestions implemented
This commit is contained in:
ctzsnooze 2023-07-06 01:51:28 +10:00 committed by GitHub
parent 1333771140
commit 9186d05468
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 80 additions and 53 deletions

View file

@ -1603,6 +1603,7 @@ static bool blackboxWriteSysinfo(void)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE, "%d", gpsRescueConfig()->maxRescueAngle) BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE, "%d", gpsRescueConfig()->maxRescueAngle)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_ROLL_MIX, "%d", gpsRescueConfig()->rollMix) BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_ROLL_MIX, "%d", gpsRescueConfig()->rollMix)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF, "%d", gpsRescueConfig()->pitchCutoffHz) BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF, "%d", gpsRescueConfig()->pitchCutoffHz)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_IMU_YAW_GAIN, "%d", gpsRescueConfig()->imuYawGain)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_DESCENT_DIST, "%d", gpsRescueConfig()->descentDistanceM) BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_DESCENT_DIST, "%d", gpsRescueConfig()->descentDistanceM)
BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_DESCEND_RATE, "%d", gpsRescueConfig()->descendRate) BLACKBOX_PRINT_HEADER_LINE(PARAM_NAME_GPS_RESCUE_DESCEND_RATE, "%d", gpsRescueConfig()->descendRate)

View file

@ -1035,6 +1035,7 @@ const clivalue_t valueTable[] = {
{ PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 30, 60 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, maxRescueAngle) }, { PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 30, 60 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, maxRescueAngle) },
{ PARAM_NAME_GPS_RESCUE_ROLL_MIX, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 250 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, rollMix) }, { PARAM_NAME_GPS_RESCUE_ROLL_MIX, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 0, 250 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, rollMix) },
{ PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 10, 255 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, pitchCutoffHz) }, { PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 10, 255 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, pitchCutoffHz) },
{ PARAM_NAME_GPS_RESCUE_IMU_YAW_GAIN, VAR_UINT8 | MASTER_VALUE, .config.minmaxUnsigned = { 5, 20 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, imuYawGain) },
{ PARAM_NAME_GPS_RESCUE_DESCENT_DIST, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 10, 500 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, descentDistanceM) }, { PARAM_NAME_GPS_RESCUE_DESCENT_DIST, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 10, 500 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, descentDistanceM) },
{ PARAM_NAME_GPS_RESCUE_DESCEND_RATE, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 25, 500 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, descendRate) }, { PARAM_NAME_GPS_RESCUE_DESCEND_RATE, VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 25, 500 }, PG_GPS_RESCUE, offsetof(gpsRescueConfig_t, descendRate) },

View file

@ -64,6 +64,7 @@ static uint8_t gpsRescueConfig_yawP;
static uint8_t gpsRescueConfig_velP, gpsRescueConfig_velI, gpsRescueConfig_velD; static uint8_t gpsRescueConfig_velP, gpsRescueConfig_velI, gpsRescueConfig_velD;
static uint8_t gpsRescueConfig_pitchCutoffHz; static uint8_t gpsRescueConfig_pitchCutoffHz;
static uint8_t gpsRescueConfig_imuYawGain;
static const void *cms_menuGpsRescuePidOnEnter(displayPort_t *pDisp) static const void *cms_menuGpsRescuePidOnEnter(displayPort_t *pDisp)
{ {
@ -80,6 +81,8 @@ static const void *cms_menuGpsRescuePidOnEnter(displayPort_t *pDisp)
gpsRescueConfig_velD = gpsRescueConfig()->velD; gpsRescueConfig_velD = gpsRescueConfig()->velD;
gpsRescueConfig_pitchCutoffHz = gpsRescueConfig()->pitchCutoffHz; gpsRescueConfig_pitchCutoffHz = gpsRescueConfig()->pitchCutoffHz;
gpsRescueConfig_imuYawGain = gpsRescueConfig()->imuYawGain;
return NULL; return NULL;
} }
@ -99,6 +102,7 @@ static const void *cms_menuGpsRescuePidOnExit(displayPort_t *pDisp, const OSD_En
gpsRescueConfigMutable()->velD = gpsRescueConfig_velD; gpsRescueConfigMutable()->velD = gpsRescueConfig_velD;
gpsRescueConfigMutable()->pitchCutoffHz = gpsRescueConfig_pitchCutoffHz; gpsRescueConfigMutable()->pitchCutoffHz = gpsRescueConfig_pitchCutoffHz;
gpsRescueConfigMutable()->imuYawGain = gpsRescueConfig_imuYawGain;
return NULL; return NULL;
} }
@ -118,6 +122,7 @@ const OSD_Entry cms_menuGpsRescuePidEntries[] =
{ "VELOCITY D", OME_UINT8 | REBOOT_REQUIRED, NULL, &(OSD_UINT8_t){ &gpsRescueConfig_velD, 0, 255, 1 } }, { "VELOCITY D", OME_UINT8 | REBOOT_REQUIRED, NULL, &(OSD_UINT8_t){ &gpsRescueConfig_velD, 0, 255, 1 } },
{ "SMOOTHING", OME_UINT8 | REBOOT_REQUIRED, NULL, &(OSD_UINT8_t){ &gpsRescueConfig_pitchCutoffHz, 10, 255, 1 } }, { "SMOOTHING", OME_UINT8 | REBOOT_REQUIRED, NULL, &(OSD_UINT8_t){ &gpsRescueConfig_pitchCutoffHz, 10, 255, 1 } },
{ "IMU_YAW_GAIN", OME_UINT8 | REBOOT_REQUIRED, NULL, &(OSD_UINT8_t){ &gpsRescueConfig_imuYawGain, 5, 20, 1 } },
{"BACK", OME_Back, NULL, NULL}, {"BACK", OME_Back, NULL, NULL},
{NULL, OME_END, NULL, NULL} {NULL, OME_END, NULL, NULL}

View file

@ -162,6 +162,7 @@
#define PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE "gps_rescue_max_angle" #define PARAM_NAME_GPS_RESCUE_MAX_RESCUE_ANGLE "gps_rescue_max_angle"
#define PARAM_NAME_GPS_RESCUE_ROLL_MIX "gps_rescue_roll_mix" #define PARAM_NAME_GPS_RESCUE_ROLL_MIX "gps_rescue_roll_mix"
#define PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF "gps_rescue_pitch_cutoff" #define PARAM_NAME_GPS_RESCUE_PITCH_CUTOFF "gps_rescue_pitch_cutoff"
#define PARAM_NAME_GPS_RESCUE_IMU_YAW_GAIN "gps_rescue_imu_yaw_gain"
#define PARAM_NAME_GPS_RESCUE_DESCENT_DIST "gps_rescue_descent_dist" #define PARAM_NAME_GPS_RESCUE_DESCENT_DIST "gps_rescue_descent_dist"
#define PARAM_NAME_GPS_RESCUE_DESCEND_RATE "gps_rescue_descend_rate" #define PARAM_NAME_GPS_RESCUE_DESCEND_RATE "gps_rescue_descend_rate"

View file

@ -111,7 +111,6 @@ typedef struct {
float alitutudeStepCm; float alitutudeStepCm;
float maxPitchStep; float maxPitchStep;
float absErrorAngle; float absErrorAngle;
float groundspeedPitchAttenuator;
float imuYawCogGain; float imuYawCogGain;
} rescueSensorData_s; } rescueSensorData_s;
@ -340,9 +339,9 @@ static void rescueAttainPosition(void)
// velocityItermRelax is a time-based factor, 0->1 with time constant of 1s from when we start to fly home // velocityItermRelax is a time-based factor, 0->1 with time constant of 1s from when we start to fly home
// avoids excess iTerm accumulation during the initial acceleration phase and during descent. // avoids excess iTerm accumulation during the initial acceleration phase and during descent.
// force iTerm to zero, to minimise overshoot during deceleration with descent
// and because if we over-fly the home point, we need to re-accumulate from zero, not the previously accumulated value
velocityI *= rescueState.intent.velocityItermAttenuator; velocityI *= rescueState.intent.velocityItermAttenuator;
// used to minimise iTerm windup during IMU error states and iTerm overshoot in the descent phase
// also, if we over-fly the home point, we need to re-accumulate iTerm from zero, not the previously accumulated value
const float pitchAngleLimit = rescueState.intent.pitchAngleLimitDeg * 100.0f; const float pitchAngleLimit = rescueState.intent.pitchAngleLimitDeg * 100.0f;
const float velocityILimit = 0.5f * pitchAngleLimit; const float velocityILimit = 0.5f * pitchAngleLimit;
@ -464,8 +463,8 @@ static void performSanityChecks(void)
const float velocityToHomeCmS = previousDistanceToHomeCm - rescueState.sensor.distanceToHomeCm; // cm/s const float velocityToHomeCmS = previousDistanceToHomeCm - rescueState.sensor.distanceToHomeCm; // cm/s
previousDistanceToHomeCm = rescueState.sensor.distanceToHomeCm; previousDistanceToHomeCm = rescueState.sensor.distanceToHomeCm;
rescueState.intent.secondsFailing += (velocityToHomeCmS < 0.1f * rescueState.intent.targetVelocityCmS) ? 1 : -1; rescueState.intent.secondsFailing += (velocityToHomeCmS < 0.1f * rescueState.intent.targetVelocityCmS) ? 1 : -1;
rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 15); rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 30);
if (rescueState.intent.secondsFailing == 15) { if (rescueState.intent.secondsFailing >= 30) {
#ifdef USE_MAG #ifdef USE_MAG
//If there is a mag and has not been disabled, we have to assume is healthy and has been used in imu.c //If there is a mag and has not been disabled, we have to assume is healthy and has been used in imu.c
if (sensors(SENSOR_MAG) && gpsRescueConfig()->useMag && !magForceDisable) { if (sensors(SENSOR_MAG) && gpsRescueConfig()->useMag && !magForceDisable) {
@ -499,7 +498,7 @@ static void performSanityChecks(void)
case RESCUE_LANDING: case RESCUE_LANDING:
rescueState.intent.secondsFailing += ratio > 0.5f ? -1 : 1; rescueState.intent.secondsFailing += ratio > 0.5f ? -1 : 1;
rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 10); rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 10);
if (rescueState.intent.secondsFailing == 10) { if (rescueState.intent.secondsFailing >= 10) {
rescueState.phase = RESCUE_ABORT; rescueState.phase = RESCUE_ABORT;
// Landing mode shouldn't take more than 10s // Landing mode shouldn't take more than 10s
} }
@ -508,7 +507,7 @@ static void performSanityChecks(void)
case RESCUE_DESCENT: case RESCUE_DESCENT:
rescueState.intent.secondsFailing += ratio > 0.5f ? -1 : 1; rescueState.intent.secondsFailing += ratio > 0.5f ? -1 : 1;
rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 10); rescueState.intent.secondsFailing = constrain(rescueState.intent.secondsFailing, 0, 10);
if (rescueState.intent.secondsFailing == 10) { if (rescueState.intent.secondsFailing >= 10) {
rescueState.phase = RESCUE_LANDING; rescueState.phase = RESCUE_LANDING;
rescueState.intent.secondsFailing = 0; rescueState.intent.secondsFailing = 0;
// if can't climb, or slow descending, enable impact detection and time out in 10s // if can't climb, or slow descending, enable impact detection and time out in 10s
@ -516,7 +515,7 @@ static void performSanityChecks(void)
break; break;
case RESCUE_DO_NOTHING: case RESCUE_DO_NOTHING:
secondsDoingNothing = MIN(secondsDoingNothing + 1, 20); secondsDoingNothing = MIN(secondsDoingNothing + 1, 20);
if (secondsDoingNothing == 20) { if (secondsDoingNothing >= 20) {
rescueState.phase = RESCUE_ABORT; rescueState.phase = RESCUE_ABORT;
// time-limited semi-controlled fall with impact detection // time-limited semi-controlled fall with impact detection
} }
@ -579,46 +578,57 @@ static void sensorUpdate(void)
rescueState.sensor.gpsDataIntervalSeconds = getGpsDataIntervalSeconds(); rescueState.sensor.gpsDataIntervalSeconds = getGpsDataIntervalSeconds();
// Range from 10ms (100hz) to 1000ms (1Hz). Intended to cover common GPS data rates and exclude unusual values. // Range from 10ms (100hz) to 1000ms (1Hz). Intended to cover common GPS data rates and exclude unusual values.
rescueState.sensor.velocityToHomeCmS = ((prevDistanceToHomeCm - rescueState.sensor.distanceToHomeCm) / rescueState.sensor.gpsDataIntervalSeconds);
// positive = towards home. First value is useless since prevDistanceToHomeCm was zero.
prevDistanceToHomeCm = rescueState.sensor.distanceToHomeCm;
DEBUG_SET(DEBUG_ATTITUDE, 4, rescueState.sensor.velocityToHomeCmS); // velocity to home
// when there is a flyaway due to IMU disorientation, increase IMU yaw CoG gain, and reduce max pitch angle // when there is a flyaway due to IMU disorientation, increase IMU yaw CoG gain, and reduce max pitch angle
if (gpsRescueConfig()->rescueGroundspeed) { if (gpsRescueConfig()->rescueGroundspeed) {
const float rescueGroundspeed = 1000.0f; // in cm/s, fixed 10 m/s groundspeed // calculate a factor that can reduce pitch angle when flying away
// rescueGroundspeed sets how aggressively the IMU COG Gain increases as velocity error increases const float rescueGroundspeed = gpsRescueConfig()->imuYawGain * 100.0f; // in cm/s, imuYawGain is m/s groundspeed
// rescueGroundspeed is effectively a normalising gain factor for the magnitude of the groundspeed error
// a higher value reduces groundspeedErrorRatio, making the radius wider and reducing the circling behaviour
const float groundspeedErrorRatio = 1.0f + fminf(fabsf(rescueState.sensor.groundSpeedCmS - rescueState.sensor.velocityToHomeCmS) / rescueGroundspeed, 2.0f); const float groundspeedErrorRatio = fabsf(rescueState.sensor.groundSpeedCmS - rescueState.sensor.velocityToHomeCmS) / rescueGroundspeed;
// 1 if groundspeed = velocity to home, or both are zero // 0 if groundspeed = velocity to home, or both are zero
// 2 if forward velocity is zero but sideways speed is 10m/s // 1 if forward velocity is zero but sideways speed is imuYawGain in m/s
// 3 if moving backwards at 10m/s. 3 is the maximum allowed value // 2 if moving backwards at imuYawGain m/s, 4 if moving backwards at 2* imuYawGain m/s, etc
// increase IMU COG Gain in proportion to positive pitch angle DEBUG_SET(DEBUG_ATTITUDE, 5, groundspeedErrorRatio * 100);
// pitch angle is positive early in a rescue, and associates with a nose forward ground course
float pitchAngleImuGain = (gpsRescueAngle[AI_PITCH] > 0.0f) ? gpsRescueAngle[AI_PITCH] / 2000.0f : 0.0f; rescueState.intent.velocityItermAttenuator = 4.0f / (groundspeedErrorRatio + 4.0f);
// 1 if groundspeedErrorRatio = 0, falling to 2/3 if groundspeedErrorRatio = 2, 0.5 if groundspeedErrorRatio = 4, etc
// limit (essentially prevent) velocity iTerm accumulation whenever there is a meaningful groundspeed error
// this is a crude but simple way to prevent iTerm windup when recovering from an IMU error
// the magnitude of the effect will be less at low GPS data rates, since there will be fewer multiplications per second
// but for our purposes this should not matter much, our intent is to severely attenuate iTerm
// if, for example, we had a 90 degree attitude error, groundspeedErrorRatio = 1, invGroundspeedError = 0.8,
// after 10 iterations, iTerm is 0.1 of what it would have been
// also is useful in blocking iTerm accumulation if we overshoot the landing point
const float pitchForwardAngle = (gpsRescueAngle[AI_PITCH] > 0.0f) ? fminf(gpsRescueAngle[AI_PITCH] / 3000.0f, 2.0f) : 0.0f;
// pitchForwardAngle is positive early in a rescue, and associates with a nose forward ground course
// note: gpsRescueAngle[AI_PITCH] is in degrees * 100, and is halved when the IMU is 180 wrong // note: gpsRescueAngle[AI_PITCH] is in degrees * 100, and is halved when the IMU is 180 wrong
// pitchAngleImuGain is 0 when flat // pitchForwardAngle is 0 when flat
// pitchAngleImuGain is 0.75 if pitch angle is 15 degrees (ie with rescue angle of 30 and 180deg IMU error) // pitchForwardAngle is 0.5 if pitch angle is 15 degrees (ie with rescue angle of 30 and 180deg IMU error)
// pitchAngleImuGain is 1.5 if pitch angle is 30 degrees (ie with rescue angle of 60 and 180deg IMU error) // pitchForwardAngle is 1.0 if pitch angle is 30 degrees (ie with rescue angle of 60 and 180deg IMU error)
// pitchAngleImuGain is 3.0 if pitch angle is 60 degrees towards home (unlikely to be sustained at that angle) // pitchForwardAngle is 2.0 if pitch angle is 60 degrees and flying towards home (unlikely to be sustained at that angle)
DEBUG_SET(DEBUG_ATTITUDE, 6, pitchForwardAngle * 100.0f);
if (rescueState.phase != RESCUE_FLY_HOME) { if (rescueState.phase != RESCUE_FLY_HOME) {
// prevent IMU disorientation arising from drift during climb, rotate or do nothing phases, which have zero pitch angle // prevent IMU disorientation arising from drift during climb, rotate or do nothing phases, which have zero pitch angle
// in descent, or too close, increase IMU yaw gain as pitch angle increases // in descent, or too close, increase IMU yaw gain as pitch angle increases
rescueState.sensor.imuYawCogGain = pitchAngleImuGain; rescueState.sensor.imuYawCogGain = pitchForwardAngle;
} else { } else {
// during fly home phase also consider the whether the quad is flying towards or away from home rescueState.sensor.imuYawCogGain = pitchForwardAngle + fminf(groundspeedErrorRatio, 3.5f);
// no additional increase in pitch related IMU gain when flying directly towards home // imuYawCogGain will be more positive at higher pitch angles and higher groundspeed errors
// max initial IMU gain with 180 degree disorientation is 5x at 60 deg set, and 3.75x at 30 deg set // imuYawCogGain will be lowest (close to zero) at lower pitch angles and when flying straight towards home
rescueState.sensor.imuYawCogGain = fminf((0.5f + pitchAngleImuGain) * groundspeedErrorRatio, 5.0f);
} }
// cut pitch angle by up to half when groundspeed error is high
// minimises flyaway velocity, but reduces ability to handle high wind
// groundspeedErrorRatio falls towards zero as the forward speed vector matches the correct path
rescueState.sensor.groundspeedPitchAttenuator = 1.0f / fminf(groundspeedErrorRatio, 2.0f);
} }
rescueState.sensor.velocityToHomeCmS = ((prevDistanceToHomeCm - rescueState.sensor.distanceToHomeCm) / rescueState.sensor.gpsDataIntervalSeconds);
// positive = towards home. First value is useless since prevDistanceToHomeCm was zero.
prevDistanceToHomeCm = rescueState.sensor.distanceToHomeCm;
DEBUG_SET(DEBUG_GPS_RESCUE_VELOCITY, 2, lrintf(rescueState.sensor.velocityToHomeCmS)); DEBUG_SET(DEBUG_GPS_RESCUE_VELOCITY, 2, lrintf(rescueState.sensor.velocityToHomeCmS));
DEBUG_SET(DEBUG_GPS_RESCUE_TRACKING, 0, lrintf(rescueState.sensor.velocityToHomeCmS)); DEBUG_SET(DEBUG_GPS_RESCUE_TRACKING, 0, lrintf(rescueState.sensor.velocityToHomeCmS));
} }
@ -698,10 +708,10 @@ void descend(void)
rescueState.intent.targetVelocityCmS = gpsRescueConfig()->rescueGroundspeed * proximityToLandingArea; rescueState.intent.targetVelocityCmS = gpsRescueConfig()->rescueGroundspeed * proximityToLandingArea;
// attenuate velocity iterm towards zero as we get closer to the landing area // attenuate velocity iterm towards zero as we get closer to the landing area
rescueState.intent.velocityItermAttenuator = proximityToLandingArea; rescueState.intent.velocityItermAttenuator = fminf(proximityToLandingArea, rescueState.intent.velocityItermAttenuator);
// reduce pitch angle limit if there is a significant groundspeed error - eg on overshooting home // reduce pitch angle limit if there is a significant groundspeed error - eg on overshooting home
rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle * rescueState.sensor.groundspeedPitchAttenuator; rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle;
// limit roll angle to half the allowed pitch angle and attenuate when closer to home // limit roll angle to half the allowed pitch angle and attenuate when closer to home
// keep some roll when at the landing circle distance to avoid endless circling // keep some roll when at the landing circle distance to avoid endless circling
@ -741,8 +751,8 @@ void initialiseRescueValues (void)
rescueState.intent.throttleDMultiplier = 1.0f; rescueState.intent.throttleDMultiplier = 1.0f;
rescueState.intent.velocityPidCutoffModifier = 1.0f; // normal velocity lowpass filter cutoff rescueState.intent.velocityPidCutoffModifier = 1.0f; // normal velocity lowpass filter cutoff
rescueState.intent.pitchAngleLimitDeg = 0.0f; // force pitch adjustment to zero - level mode will level out rescueState.intent.pitchAngleLimitDeg = 0.0f; // force pitch adjustment to zero - level mode will level out
rescueState.intent.velocityItermAttenuator = 0.0f; // multiply velocity iTerm by zero rescueState.intent.velocityItermAttenuator = 1.0f; // allow iTerm to accumulate normally unless constrained by IMU error or descent phase
rescueState.intent.velocityItermRelax = 0.0f; // don't accumulate any rescueState.intent.velocityItermRelax = 0.0f; // but don't accumulate any at the start, not until fly home
} }
void gpsRescueUpdate(void) void gpsRescueUpdate(void)
@ -828,10 +838,9 @@ void gpsRescueUpdate(void)
} }
if (rescueState.sensor.absErrorAngle < 30.0f) { if (rescueState.sensor.absErrorAngle < 30.0f) {
// allow pitch, limiting allowed angle if we are drifting away from home // allow pitch, limiting allowed angle if we are drifting away from home
rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle * rescueState.sensor.groundspeedPitchAttenuator; rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle;
rescueState.phase = RESCUE_FLY_HOME; // enter fly home phase rescueState.phase = RESCUE_FLY_HOME; // enter fly home phase
rescueState.intent.secondsFailing = 0; // reset sanity timer for flight home rescueState.intent.secondsFailing = 0; // reset sanity timer for flight home
rescueState.intent.velocityItermRelax = 1.0f; // velocity iTerm activated
} }
initialVelocityLow = rescueState.sensor.velocityToHomeCmS < gpsRescueConfig()->rescueGroundspeed; // used to set direction of velocity target change initialVelocityLow = rescueState.sensor.velocityToHomeCmS < gpsRescueConfig()->rescueGroundspeed; // used to set direction of velocity target change
rescueState.intent.targetVelocityCmS = rescueState.sensor.velocityToHomeCmS; rescueState.intent.targetVelocityCmS = rescueState.sensor.velocityToHomeCmS;
@ -861,7 +870,7 @@ void gpsRescueUpdate(void)
if (newGPSData) { if (newGPSData) {
// cut back on allowed angle if there is a high groundspeed error // cut back on allowed angle if there is a high groundspeed error
rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle * rescueState.sensor.groundspeedPitchAttenuator; rescueState.intent.pitchAngleLimitDeg = gpsRescueConfig()->maxRescueAngle;
// introduce roll slowly and limit to half the max pitch angle; earth referenced yaw may add more roll via angle code // introduce roll slowly and limit to half the max pitch angle; earth referenced yaw may add more roll via angle code
rescueState.intent.rollAngleLimitDeg = 0.5f * rescueState.intent.pitchAngleLimitDeg * rescueState.intent.velocityItermRelax; rescueState.intent.rollAngleLimitDeg = 0.5f * rescueState.intent.pitchAngleLimitDeg * rescueState.intent.velocityItermRelax;
if (rescueState.sensor.distanceToHomeM <= rescueState.intent.descentDistanceM) { if (rescueState.sensor.distanceToHomeM <= rescueState.intent.descentDistanceM) {

View file

@ -87,8 +87,9 @@ static bool imuUpdated = false;
#define ATTITUDE_RESET_GYRO_LIMIT 15 // 15 deg/sec - gyro limit for quiet period #define ATTITUDE_RESET_GYRO_LIMIT 15 // 15 deg/sec - gyro limit for quiet period
#define ATTITUDE_RESET_KP_GAIN 25.0 // dcmKpGain value to use during attitude reset #define ATTITUDE_RESET_KP_GAIN 25.0 // dcmKpGain value to use during attitude reset
#define ATTITUDE_RESET_ACTIVE_TIME 500000 // 500ms - Time to wait for attitude to converge at high gain #define ATTITUDE_RESET_ACTIVE_TIME 500000 // 500ms - Time to wait for attitude to converge at high gain
#define GPS_COG_MIN_GROUNDSPEED 200 // 200cm/s minimum groundspeed for a gps based IMU heading to be considered valid #define GPS_COG_MIN_GROUNDSPEED 100 // 1.0m/s - the minimum groundspeed for a gps based IMU heading to be considered valid
// Better to have some update than none for GPS Rescue at slow return speeds // Better to have some update than none for GPS Rescue at slow return speeds
#define GPS_COG_MAX_GROUNDSPEED 500 // 5.0m/s - Value for 'normal' 1.0 yaw IMU CogGain
bool canUseGPSHeading = true; bool canUseGPSHeading = true;
@ -237,12 +238,17 @@ STATIC_UNIT_TESTED void imuMahonyAHRSupdate(float dt, float gx, float gy, float
ez_ef /= sqrtf(heading_mag); ez_ef /= sqrtf(heading_mag);
#endif #endif
ez_ef *= cogYawGain; // apply gain parameter ez_ef *= cogYawGain; // apply gain parameter
// covert to body frame // convert to body frame
ex += rMat[2][0] * ez_ef; ex += rMat[2][0] * ez_ef;
ey += rMat[2][1] * ez_ef; ey += rMat[2][1] * ez_ef;
ez += rMat[2][2] * ez_ef; ez += rMat[2][2] * ez_ef;
DEBUG_SET(DEBUG_ATTITUDE, 3, (ez_ef * 100));
} }
DEBUG_SET(DEBUG_ATTITUDE, 2, cogYawGain * 100.0f);
DEBUG_SET(DEBUG_ATTITUDE, 7, (dcmKpGain * 100));
#ifdef USE_MAG #ifdef USE_MAG
// Use measured magnetic field vector // Use measured magnetic field vector
fpVector3_t mag_bf = {{mag.magADC[X], mag.magADC[Y], mag.magADC[Z]}}; fpVector3_t mag_bf = {{mag.magADC[X], mag.magADC[Y], mag.magADC[Z]}};
@ -506,8 +512,9 @@ static void imuCalculateEstimatedAttitude(timeUs_t currentTimeUs)
#if defined(USE_GPS) #if defined(USE_GPS)
if (!useMag && sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat > GPS_MIN_SAT_COUNT && gpsSol.groundSpeed >= GPS_COG_MIN_GROUNDSPEED) { if (!useMag && sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat > GPS_MIN_SAT_COUNT && gpsSol.groundSpeed >= GPS_COG_MIN_GROUNDSPEED) {
// Use GPS course over ground to correct attitude.values.yaw // Use GPS course over ground to correct attitude.values.yaw
const float imuYawGroundspeed = fminf (gpsSol.groundSpeed / GPS_COG_MAX_GROUNDSPEED, 2.0f);
courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse); courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse);
cogYawGain = (FLIGHT_MODE(GPS_RESCUE_MODE)) ? gpsRescueGetImuYawCogGain() : 1.0f; cogYawGain = (FLIGHT_MODE(GPS_RESCUE_MODE)) ? gpsRescueGetImuYawCogGain() : imuYawGroundspeed;
// normally update yaw heading with GPS data, but when in a Rescue, modify the IMU yaw gain dynamically // normally update yaw heading with GPS data, but when in a Rescue, modify the IMU yaw gain dynamically
if (shouldInitializeGPSHeading()) { if (shouldInitializeGPSHeading()) {
// Reset our reference and reinitialize quaternion. // Reset our reference and reinitialize quaternion.
@ -595,8 +602,8 @@ void imuUpdateAttitude(timeUs_t currentTimeUs)
schedulerIgnoreTaskStateTime(); schedulerIgnoreTaskStateTime();
} }
DEBUG_SET(DEBUG_ATTITUDE, X, acc.accADC[X]); DEBUG_SET(DEBUG_ATTITUDE, X, acc.accADC[X]); // roll
DEBUG_SET(DEBUG_ATTITUDE, Y, acc.accADC[Y]); DEBUG_SET(DEBUG_ATTITUDE, Y, acc.accADC[Y]); // pitch
} }
#endif // USE_ACC #endif // USE_ACC

View file

@ -386,7 +386,8 @@ STATIC_UNIT_TESTED FAST_CODE_NOINLINE float pidLevel(int axis, const pidProfile_
// earthRef code here takes about 76 cycles, if conditional on angleEarthRef it takes about 100. sin_approx costs most of those cycles. // earthRef code here takes about 76 cycles, if conditional on angleEarthRef it takes about 100. sin_approx costs most of those cycles.
float sinAngle = sin_approx(DEGREES_TO_RADIANS(pidRuntime.angleTarget[axis == FD_ROLL ? FD_PITCH : FD_ROLL])); float sinAngle = sin_approx(DEGREES_TO_RADIANS(pidRuntime.angleTarget[axis == FD_ROLL ? FD_PITCH : FD_ROLL]));
sinAngle *= (axis == FD_ROLL) ? -1.0f : 1.0f; // must be negative for Roll sinAngle *= (axis == FD_ROLL) ? -1.0f : 1.0f; // must be negative for Roll
angleRate += pidRuntime.angleYawSetpoint * sinAngle * pidRuntime.angleEarthRef; const float earthRefGain = FLIGHT_MODE(GPS_RESCUE_MODE) ? 1.0f : pidRuntime.angleEarthRef;
angleRate += pidRuntime.angleYawSetpoint * sinAngle * earthRefGain;
pidRuntime.angleTarget[axis] = angleTarget; // set target for alternate axis to current axis, for use in preceding calculation pidRuntime.angleTarget[axis] = angleTarget; // set target for alternate axis to current axis, for use in preceding calculation
// smooth final angle rate output to clean up attitude signal steps (500hz), GPS steps (10 or 100hz), RC steps etc // smooth final angle rate output to clean up attitude signal steps (500hz), GPS steps (10 or 100hz), RC steps etc
@ -889,9 +890,10 @@ void FAST_CODE pidController(const pidProfile_t *pidProfile, timeUs_t currentTim
// if earth referencing is requested, attenuate yaw axis setpoint when pitched or rolled // if earth referencing is requested, attenuate yaw axis setpoint when pitched or rolled
// and send yawSetpoint to Angle code to modulate pitch and roll // and send yawSetpoint to Angle code to modulate pitch and roll
// code cost is 107 cycles when earthRef enabled, 20 otherwise, nearly all in cos_approx // code cost is 107 cycles when earthRef enabled, 20 otherwise, nearly all in cos_approx
if (pidRuntime.angleEarthRef) { const float earthRefGain = FLIGHT_MODE(GPS_RESCUE_MODE) ? 1.0f : pidRuntime.angleEarthRef;
if (earthRefGain) {
pidRuntime.angleYawSetpoint = currentPidSetpoint; pidRuntime.angleYawSetpoint = currentPidSetpoint;
float maxAngleTargetAbs = pidRuntime.angleEarthRef * fmaxf( fabsf(pidRuntime.angleTarget[FD_ROLL]), fabsf(pidRuntime.angleTarget[FD_PITCH]) ); float maxAngleTargetAbs = earthRefGain * fmaxf( fabsf(pidRuntime.angleTarget[FD_ROLL]), fabsf(pidRuntime.angleTarget[FD_PITCH]) );
maxAngleTargetAbs *= (FLIGHT_MODE(HORIZON_MODE)) ? horizonLevelStrength : 1.0f; maxAngleTargetAbs *= (FLIGHT_MODE(HORIZON_MODE)) ? horizonLevelStrength : 1.0f;
// reduce compensation whenever Horizon uses less levelling // reduce compensation whenever Horizon uses less levelling
currentPidSetpoint *= cos_approx(DEGREES_TO_RADIANS(maxAngleTargetAbs)); currentPidSetpoint *= cos_approx(DEGREES_TO_RADIANS(maxAngleTargetAbs));

View file

@ -29,7 +29,7 @@
#include "gps_rescue.h" #include "gps_rescue.h"
PG_REGISTER_WITH_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig, PG_GPS_RESCUE, 4); PG_REGISTER_WITH_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig, PG_GPS_RESCUE, 5);
PG_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig, PG_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig,
@ -65,7 +65,8 @@ PG_RESET_TEMPLATE(gpsRescueConfig_t, gpsRescueConfig,
.velD = 12, .velD = 12,
.yawP = 20, .yawP = 20,
.useMag = GPS_RESCUE_USE_MAG .useMag = GPS_RESCUE_USE_MAG,
.imuYawGain = 10
); );
#endif // USE_GPS_RESCUE #endif // USE_GPS_RESCUE

View file

@ -49,7 +49,7 @@ typedef struct gpsRescue_s {
uint8_t rollMix; uint8_t rollMix;
uint8_t disarmThreshold; uint8_t disarmThreshold;
uint8_t pitchCutoffHz; uint8_t pitchCutoffHz;
uint8_t imuYawGain;
} gpsRescueConfig_t; } gpsRescueConfig_t;
PG_DECLARE(gpsRescueConfig_t, gpsRescueConfig); PG_DECLARE(gpsRescueConfig_t, gpsRescueConfig);