Skip to content

Commit

Permalink
Merge pull request #9905 from iNavFlight/dzikuvx-fixed-wing-pid-dampener
Browse files Browse the repository at this point in the history
I-Term Lock for Fixed Wing
  • Loading branch information
DzikuVx authored May 20, 2024
2 parents 2f2330e + fc9457f commit 4d5207e
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 37 deletions.
26 changes: 23 additions & 3 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -1302,13 +1302,33 @@ Fixed-wing rate stabilisation I-gain for YAW

---

### fw_iterm_limit_stick_position
### fw_iterm_lock_engage_threshold

Iterm is not allowed to grow when stick position is above threshold. This solves the problem of bounceback or followthrough when full stick deflection is applied on poorely tuned fixed wings. In other words, stabilization is partialy disabled when pilot is actively controlling the aircraft and active when sticks are not touched. `0` mean stick is in center position, `1` means it is fully deflected to either side
Defines error rate (in percents of max rate) when Iterm Lock is engaged when sticks are release. Iterm Lock will stay active until error drops below this number

| Default | Min | Max |
| --- | --- | --- |
| 0.5 | 0 | 1 |
| 10 | 5 | 25 |

---

### fw_iterm_lock_rate_threshold

Defines rate percentage when full P I and D attenuation should happen. 100 disables Iterm Lock for P and D term

| Default | Min | Max |
| --- | --- | --- |
| 40 | 10 | 100 |

---

### fw_iterm_lock_time_max_ms

Defines max time in milliseconds for how long ITerm Lock will shut down Iterm after sticks are release

| Default | Min | Max |
| --- | --- | --- |
| 500 | 100 | 1000 |

---

Expand Down
18 changes: 17 additions & 1 deletion src/main/common/maths.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,9 +516,25 @@ bool sensorCalibrationSolveForScale(sensorCalibrationState_t * state, float resu
return sensorCalibrationValidateResult(result);
}

float gaussian(const float x, const float mu, const float sigma) {
return exp(-pow((double)(x - mu), 2) / (2 * pow((double)sigma, 2)));
}

float bellCurve(const float x, const float curveWidth)
{
return powf(M_Ef, -sq(x) / (2.0f * sq(curveWidth)));
return gaussian(x, 0.0f, curveWidth);
}

/**
* @brief Calculate the attenuation of a value using a Gaussian function.
* Retuns 1 for input 0 and ~0 for input width.
* @param input The input value.
* @param width The width of the Gaussian function.
* @return The attenuation of the input value.
*/
float attenuation(const float input, const float width) {
const float sigma = width / 2.35482f; // Approximately width / sqrt(2 * ln(2))
return gaussian(input, 0.0f, sigma);
}

float fast_fsqrtf(const float value) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/common/maths.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ float acos_approx(float x);
void arraySubInt32(int32_t *dest, int32_t *array1, int32_t *array2, int count);

float bellCurve(const float x, const float curveWidth);
float attenuation(const float input, const float width);
float gaussian(const float x, const float mu, const float sigma);
float fast_fsqrtf(const float value);
float calc_length_pythagorean_2D(const float firstElement, const float secondElement);
float calc_length_pythagorean_3D(const float firstElement, const float secondElement, const float thirdElement);
Expand Down
24 changes: 18 additions & 6 deletions src/main/fc/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1934,12 +1934,6 @@ groups:
field: fixedWingCoordinatedPitchGain
min: 0
max: 2
- name: fw_iterm_limit_stick_position
description: "Iterm is not allowed to grow when stick position is above threshold. This solves the problem of bounceback or followthrough when full stick deflection is applied on poorely tuned fixed wings. In other words, stabilization is partialy disabled when pilot is actively controlling the aircraft and active when sticks are not touched. `0` mean stick is in center position, `1` means it is fully deflected to either side"
default_value: 0.5
field: fixedWingItermLimitOnStickPosition
min: 0
max: 1
- name: fw_yaw_iterm_freeze_bank_angle
description: "Yaw Iterm is frozen when bank angle is above this threshold [degrees]. This solves the problem of the rudder counteracting turns by partially disabling yaw stabilization when making banked turns. Setting to 0 (the default) disables this feature. Only applies when autopilot is not active and TURN ASSIST is disabled."
default_value: 0
Expand Down Expand Up @@ -2233,6 +2227,24 @@ groups:
field: fixedWingLevelTrimGain
min: 0
max: 20
- name: fw_iterm_lock_time_max_ms
description: Defines max time in milliseconds for how long ITerm Lock will shut down Iterm after sticks are release
default_value: 500
field: fwItermLockTimeMaxMs
min: 100
max: 1000
- name: fw_iterm_lock_rate_threshold
description: Defines rate percentage when full P I and D attenuation should happen. 100 disables Iterm Lock for P and D term
field: fwItermLockRateLimit
default_value: 40
min: 10
max: 100
- name: fw_iterm_lock_engage_threshold
description: Defines error rate (in percents of max rate) when Iterm Lock is engaged when sticks are release. Iterm Lock will stay active until error drops below this number
default_value: 10
min: 5
max: 25
field: fwItermLockEngageThreshold

- name: PG_PID_AUTOTUNE_CONFIG
type: pidAutotuneConfig_t
Expand Down
87 changes: 61 additions & 26 deletions src/main/flight/pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@

#include "programming/logic_condition.h"

typedef struct {
float aP;
float aI;
float aD;
float aFF;
timeMs_t targetOverThresholdTimeMs;
} fwPidAttenuation_t;

typedef struct {
uint8_t axis;
float kP; // Proportional gain
Expand Down Expand Up @@ -106,6 +114,8 @@ typedef struct {
pt3Filter_t rateTargetFilter;

smithPredictor_t smithPredictor;

fwPidAttenuation_t attenuation;
} pidState_t;

STATIC_FASTRAM bool pidFiltersConfigured = false;
Expand Down Expand Up @@ -157,7 +167,6 @@ static EXTENDED_FASTRAM uint8_t usedPidControllerType;
typedef void (*pidControllerFnPtr)(pidState_t *pidState, float dT, float dT_inv);
static EXTENDED_FASTRAM pidControllerFnPtr pidControllerApplyFn;
static EXTENDED_FASTRAM filterApplyFnPtr dTermLpfFilterApplyFn;
static EXTENDED_FASTRAM bool levelingEnabled = false;
static EXTENDED_FASTRAM bool restartAngleHoldMode = true;
static EXTENDED_FASTRAM bool angleHoldIsLevel = false;

Expand All @@ -169,7 +178,7 @@ static EXTENDED_FASTRAM bool angleHoldIsLevel = false;
static EXTENDED_FASTRAM float fixedWingLevelTrim;
static EXTENDED_FASTRAM pidController_t fixedWingLevelTrimController;

PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 8);
PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 9);

PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.bank_mc = {
Expand Down Expand Up @@ -268,7 +277,6 @@ PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.fixedWingReferenceAirspeed = SETTING_FW_REFERENCE_AIRSPEED_DEFAULT,
.fixedWingCoordinatedYawGain = SETTING_FW_TURN_ASSIST_YAW_GAIN_DEFAULT,
.fixedWingCoordinatedPitchGain = SETTING_FW_TURN_ASSIST_PITCH_GAIN_DEFAULT,
.fixedWingItermLimitOnStickPosition = SETTING_FW_ITERM_LIMIT_STICK_POSITION_DEFAULT,
.fixedWingYawItermBankFreeze = SETTING_FW_YAW_ITERM_FREEZE_BANK_ANGLE_DEFAULT,

.navVelXyDTermLpfHz = SETTING_NAV_MC_VEL_XY_DTERM_LPF_HZ_DEFAULT,
Expand Down Expand Up @@ -304,6 +312,9 @@ PG_RESET_TEMPLATE(pidProfile_t, pidProfile,
.smithPredictorDelay = SETTING_SMITH_PREDICTOR_DELAY_DEFAULT,
.smithPredictorFilterHz = SETTING_SMITH_PREDICTOR_LPF_HZ_DEFAULT,
#endif
.fwItermLockTimeMaxMs = SETTING_FW_ITERM_LOCK_TIME_MAX_MS_DEFAULT,
.fwItermLockRateLimit = SETTING_FW_ITERM_LOCK_RATE_THRESHOLD_DEFAULT,
.fwItermLockEngageThreshold = SETTING_FW_ITERM_LOCK_ENGAGE_THRESHOLD_DEFAULT,
);

bool pidInitFilters(void)
Expand Down Expand Up @@ -670,19 +681,6 @@ static void pidApplySetpointRateLimiting(pidState_t *pidState, flight_dynamics_i
}
}

bool isFixedWingItermLimitActive(float stickPosition)
{
/*
* Iterm anti windup whould be active only when pilot controls the rotation
* velocity directly, not when ANGLE or HORIZON are used
*/
if (levelingEnabled) {
return false;
}

return fabsf(stickPosition) > pidProfile()->fixedWingItermLimitOnStickPosition;
}

static float pTermProcess(pidState_t *pidState, float rateError, float dT) {
float newPTerm = rateError * pidState->kP;

Expand Down Expand Up @@ -751,20 +749,61 @@ static void nullRateController(pidState_t *pidState, float dT, float dT_inv) {
UNUSED(dT_inv);
}

static void fwRateAttenuation(pidState_t *pidState, const float rateTarget, const float rateError) {
const float maxRate = currentControlRateProfile->stabilized.rates[pidState->axis] * 10.0f;

const float dampingFactor = attenuation(rateTarget, maxRate * pidProfile()->fwItermLockRateLimit / 100.0f);

/*
* Iterm damping is applied (down to 0) when:
* abs(error) > 10% rate and sticks were moved in the last 500ms (hard stop at this mark)
* itermAttenuation = MIN(curve(setpoint), (abs(error) > 10%) && (sticks were deflected in 500ms) ? 0 : 1)
*/

//If error is greater than 10% or max rate
const bool errorThresholdReached = fabsf(rateError) > maxRate * pidProfile()->fwItermLockEngageThreshold / 100.0f;

//If stick (setpoint) was moved above threshold in the last 500ms
if (fabsf(rateTarget) > maxRate * 0.2f) {
pidState->attenuation.targetOverThresholdTimeMs = millis();
}

//If error is below threshold, we no longer track time for lock mechanism
if (!errorThresholdReached) {
pidState->attenuation.targetOverThresholdTimeMs = 0;
}

pidState->attenuation.aI = MIN(dampingFactor, (errorThresholdReached && (millis() - pidState->attenuation.targetOverThresholdTimeMs) < pidProfile()->fwItermLockTimeMaxMs) ? 0.0f : 1.0f);

//P & D damping factors are always the same and based on current damping factor
pidState->attenuation.aP = dampingFactor;
pidState->attenuation.aD = dampingFactor;

if (pidState->axis == FD_ROLL) {
DEBUG_SET(DEBUG_ALWAYS, 0, pidState->attenuation.aP * 1000);
DEBUG_SET(DEBUG_ALWAYS, 1, pidState->attenuation.aI * 1000);
DEBUG_SET(DEBUG_ALWAYS, 2, pidState->attenuation.aD * 1000);
}
}

static void NOINLINE pidApplyFixedWingRateController(pidState_t *pidState, float dT, float dT_inv)
{
const float rateTarget = getFlightAxisRateOverride(pidState->axis, pidState->rateTarget);

const float rateError = rateTarget - pidState->gyroRate;
const float newPTerm = pTermProcess(pidState, rateError, dT);
const float newDTerm = dTermProcess(pidState, rateTarget, dT, dT_inv);

fwRateAttenuation(pidState, rateTarget, rateError);

const float newPTerm = pTermProcess(pidState, rateError, dT) * pidState->attenuation.aP;
const float newDTerm = dTermProcess(pidState, rateTarget, dT, dT_inv) * pidState->attenuation.aD;
const float newFFTerm = rateTarget * pidState->kFF;

/*
* Integral should be updated only if axis Iterm is not frozen
*/
if (!pidState->itermFreezeActive) {
pidState->errorGyroIf += rateError * pidState->kI * dT;
pidState->errorGyroIf += rateError * pidState->kI * dT * pidState->attenuation.aI;
}

applyItermLimiting(pidState);
Expand Down Expand Up @@ -1046,11 +1085,9 @@ static void pidApplyFpvCameraAngleMix(pidState_t *pidState, uint8_t fpvCameraAng

void checkItermLimitingActive(pidState_t *pidState)
{
bool shouldActivate;
if (usedPidControllerType == PID_TYPE_PIFF) {
shouldActivate = isFixedWingItermLimitActive(pidState->stickPosition);
} else
{
bool shouldActivate = false;

if (usedPidControllerType == PID_TYPE_PID) {
shouldActivate = mixerIsOutputSaturated(); //just in case, since it is already managed by itermWindupPointPercent
}

Expand Down Expand Up @@ -1180,7 +1217,6 @@ void FAST_CODE pidController(float dT)

// Step 3: Run control for ANGLE_MODE, HORIZON_MODE and ANGLEHOLD_MODE
const float horizonRateMagnitude = FLIGHT_MODE(HORIZON_MODE) ? calcHorizonRateMagnitude() : 0.0f;
levelingEnabled = false;
angleHoldIsLevel = false;

for (uint8_t axis = FD_ROLL; axis <= FD_PITCH; axis++) {
Expand All @@ -1200,7 +1236,6 @@ void FAST_CODE pidController(float dT)
// Apply the Level PID controller
pidLevel(angleTarget, &pidState[axis], axis, horizonRateMagnitude, dT);
canUseFpvCameraMix = false; // FPVANGLEMIX is incompatible with ANGLE/HORIZON
levelingEnabled = true;
} else {
restartAngleHoldMode = true;
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/flight/pid.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ typedef struct pidProfile_s {
float fixedWingReferenceAirspeed; // Reference tuning airspeed for the airplane - the speed for which PID gains are tuned
float fixedWingCoordinatedYawGain; // This is the gain of the yaw rate required to keep the yaw rate consistent with the turn rate for a coordinated turn.
float fixedWingCoordinatedPitchGain; // This is the gain of the pitch rate to keep the pitch angle constant during coordinated turns.
float fixedWingItermLimitOnStickPosition; //Do not allow Iterm to grow when stick position is above this point
uint16_t fixedWingYawItermBankFreeze; // Freeze yaw Iterm when bank angle is more than this many degrees

float navVelXyDTermLpfHz;
Expand Down Expand Up @@ -156,6 +155,12 @@ typedef struct pidProfile_s {
float smithPredictorDelay;
uint16_t smithPredictorFilterHz;
#endif


uint16_t fwItermLockTimeMaxMs;
uint8_t fwItermLockRateLimit;
uint8_t fwItermLockEngageThreshold;

} pidProfile_t;

typedef struct pidAutotuneConfig_s {
Expand Down

0 comments on commit 4d5207e

Please sign in to comment.