FF update including second order fitlering of boost, tidying up etc

Include default to no averaging
This commit is contained in:
ctzsnooze 2020-09-12 17:08:14 +10:00
parent 1c3346bae1
commit 73663f7fae
6 changed files with 73 additions and 95 deletions

View File

@ -1390,7 +1390,6 @@ static bool blackboxWriteSysinfo(void)
currentPidProfile->pid[PID_YAW].F); currentPidProfile->pid[PID_YAW].F);
#ifdef USE_INTERPOLATED_SP #ifdef USE_INTERPOLATED_SP
BLACKBOX_PRINT_HEADER_LINE("ff_interpolate_sp", "%d", currentPidProfile->ff_interpolate_sp); BLACKBOX_PRINT_HEADER_LINE("ff_interpolate_sp", "%d", currentPidProfile->ff_interpolate_sp);
BLACKBOX_PRINT_HEADER_LINE("ff_spike_limit", "%d", currentPidProfile->ff_spike_limit);
BLACKBOX_PRINT_HEADER_LINE("ff_max_rate_limit", "%d", currentPidProfile->ff_max_rate_limit); BLACKBOX_PRINT_HEADER_LINE("ff_max_rate_limit", "%d", currentPidProfile->ff_max_rate_limit);
#endif #endif
BLACKBOX_PRINT_HEADER_LINE("ff_boost", "%d", currentPidProfile->ff_boost); BLACKBOX_PRINT_HEADER_LINE("ff_boost", "%d", currentPidProfile->ff_boost);

View File

@ -1145,7 +1145,6 @@ const clivalue_t valueTable[] = {
#endif #endif
#ifdef USE_INTERPOLATED_SP #ifdef USE_INTERPOLATED_SP
{ "ff_interpolate_sp", VAR_UINT8 | PROFILE_VALUE | MODE_LOOKUP, .config.lookup = {TABLE_INTERPOLATED_SP}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_interpolate_sp) }, { "ff_interpolate_sp", VAR_UINT8 | PROFILE_VALUE | MODE_LOOKUP, .config.lookup = {TABLE_INTERPOLATED_SP}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_interpolate_sp) },
{ "ff_spike_limit", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 255}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_spike_limit) },
{ "ff_max_rate_limit", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 150}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_max_rate_limit) }, { "ff_max_rate_limit", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 150}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_max_rate_limit) },
{ "ff_smooth_factor", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 75}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_smooth_factor) }, { "ff_smooth_factor", VAR_UINT8 | PROFILE_VALUE, .config.minmaxUnsigned = {0, 75}, PG_PID_PROFILE, offsetof(pidProfile_t, ff_smooth_factor) },
#endif #endif

View File

@ -35,7 +35,6 @@
static float setpointDeltaImpl[XYZ_AXIS_COUNT]; static float setpointDeltaImpl[XYZ_AXIS_COUNT];
static float setpointDelta[XYZ_AXIS_COUNT]; static float setpointDelta[XYZ_AXIS_COUNT];
static uint8_t holdCount[XYZ_AXIS_COUNT];
typedef struct laggedMovingAverageCombined_s { typedef struct laggedMovingAverageCombined_s {
laggedMovingAverage_t filter; laggedMovingAverage_t filter;
@ -47,11 +46,14 @@ laggedMovingAverageCombined_t setpointDeltaAvg[XYZ_AXIS_COUNT];
static float prevSetpointSpeed[XYZ_AXIS_COUNT]; static float prevSetpointSpeed[XYZ_AXIS_COUNT];
static float prevAcceleration[XYZ_AXIS_COUNT]; static float prevAcceleration[XYZ_AXIS_COUNT];
static float prevRawSetpoint[XYZ_AXIS_COUNT]; static float prevRawSetpoint[XYZ_AXIS_COUNT];
//for smoothing
static float prevDeltaImpl[XYZ_AXIS_COUNT]; static float prevDeltaImpl[XYZ_AXIS_COUNT];
static float prevBoostAmount[XYZ_AXIS_COUNT];
static uint8_t ffStatus[XYZ_AXIS_COUNT];
static bool bigStep[XYZ_AXIS_COUNT]; static bool bigStep[XYZ_AXIS_COUNT];
static uint8_t averagingCount; static uint8_t averagingCount;
// Configuration
static float ffMaxRateLimit[XYZ_AXIS_COUNT]; static float ffMaxRateLimit[XYZ_AXIS_COUNT];
static float ffMaxRate[XYZ_AXIS_COUNT]; static float ffMaxRate[XYZ_AXIS_COUNT];
@ -69,101 +71,85 @@ FAST_CODE_NOINLINE float interpolatedSpApply(int axis, bool newRcFrame, ffInterp
if (newRcFrame) { if (newRcFrame) {
float rawSetpoint = getRawSetpoint(axis); float rawSetpoint = getRawSetpoint(axis);
float absRawSetpoint = fabsf(rawSetpoint);
const float rxInterval = getCurrentRxRefreshRate() * 1e-6f; const float rxInterval = getCurrentRxRefreshRate() * 1e-6f;
const float rxRate = 1.0f / rxInterval; const float rxRate = 1.0f / rxInterval;
float setpointSpeed = (rawSetpoint - prevRawSetpoint[axis]) * rxRate; float setpointSpeed = (rawSetpoint - prevRawSetpoint[axis]) * rxRate;
float setpointAcceleration = setpointSpeed - prevSetpointSpeed[axis]; float absSetpointSpeed = fabsf(setpointSpeed);
float setpointSpeedModified = setpointSpeed; float absPrevSetpointSpeed = fabsf(prevSetpointSpeed[axis]);
float setpointAccelerationModified = setpointAcceleration; float setpointAccelerationModifier = 1.0f;
// Glitch reduction code for identical packets if (setpointSpeed == 0 && absRawSetpoint < 0.98f * ffMaxRate[axis]) {
if (fabsf(setpointAcceleration) > 3.0f * fabsf(prevAcceleration[axis])) { // no movement, or sticks at max; ffStatus set
// the max stick check is needed to prevent interpolation when arriving at max sticks
if (prevSetpointSpeed[axis] == 0) {
// no movement on two packets in a row
// do nothing now, but may use status = 3 to smooth following packet
ffStatus[axis] = 3;
} else {
// there was movement on previous packet, now none
if (bigStep[axis] == true) {
// previous movement was big; likely an early FrSky packet
// don't project these forward or we get a sustained large spike
ffStatus[axis] = 2;
} else {
// likely a dropped packet
// interpolate forward using previous setpoint speed and acceleration
setpointSpeed = prevSetpointSpeed[axis] + prevAcceleration[axis];
// use status = 1 to halve the step for the next packet
ffStatus[axis] = 1;
}
}
} else {
// we have movement; let's consider what happened on previous packets, using ffStatus
if (ffStatus[axis] == 1) {
// was interpolated forward after previous dropped packet after small step
// this step is likely twice as tall as it should be
setpointSpeed = setpointSpeed / 2.0f;
} else if (ffStatus[axis] == 2) {
// we are doing nothing for these to avoid exaggerating the FrSky early packet problem
} else if (ffStatus[axis] == 3) {
// movement after nothing on previous two packets
// reduce boost when higher averaging is used to improve slow stick smoothness
setpointAccelerationModifier /= (averagingCount + 1);
}
ffStatus[axis] = 0;
// all is normal
}
float setpointAcceleration = setpointSpeed - prevSetpointSpeed[axis];
// determine if this step was a relatively large one, to use when evaluating next packet
if (absSetpointSpeed > 1.5f * absPrevSetpointSpeed || absPrevSetpointSpeed > 1.5f * absSetpointSpeed){
bigStep[axis] = true; bigStep[axis] = true;
} else { } else {
bigStep[axis] = false; bigStep[axis] = false;
} }
if (setpointSpeed == 0 && fabsf(rawSetpoint) < 0.98f * ffMaxRate[axis]) {
// identical packet detected, not at full deflection.
// first packet on leaving full deflection always gets full FF
if (holdCount[axis] == 0) {
// previous packet had movement
if (bigStep[axis]) {
// type 1 = interpolate forward where acceleration change is large
setpointSpeedModified = prevSetpointSpeed[axis];
setpointAccelerationModified = prevAcceleration[axis];
holdCount[axis] = 1;
} else {
// type 2 = small change, no interpolation needed
setpointSpeedModified = 0.0f;
setpointSpeed = setpointSpeed / 2.0f;
holdCount[axis] = 2;
}
} else {
// it is an unchanged packet after previous unchanged packet
// speed and acceleration will be zero, no need to change anything
holdCount[axis] = 3;
}
} else {
// we're moving, or sticks are at max
if (holdCount[axis] != 0) {
// previous step was a duplicate, handle each type differently
if (holdCount[axis] == 1) {
// interpolation was applied
// raw setpoint speed of next 'good' packet is twice what it should be
setpointSpeedModified = setpointSpeed / 2.0f;
setpointSpeed = setpointSpeedModified;
// empirically this works best
setpointAccelerationModified = (prevAcceleration[axis] + setpointAcceleration) / 2.0f;
} else if (holdCount[axis] == 2) {
// interpolation was not applied
} else if (holdCount[axis] == 3) {
// after persistent flat period, no boost
// reduces jitter from boost when flying smooth lines
// but only when no ff_averaging is active, eg hard core race setups
// WARNING: this means no boost if ADC is active on FrSky radios
if (averagingCount > 1) {
setpointAccelerationModified /= averagingCount;
}
}
holdCount[axis] = 0;
}
}
// smooth deadband type suppression of FF jitter when sticks are at or returning to centre // smooth deadband type suppression of FF jitter when sticks are at or returning to centre
// only when ff_averaging is 3 or more, for HD or cinematic flying // only when ff_averaging is 3 or more, for HD or cinematic flying
if (averagingCount > 2) { if (averagingCount > 2) {
const float rawSetpointCentred = fabsf(rawSetpoint) / averagingCount; const float rawSetpointCentred = absRawSetpoint / averagingCount;
if (rawSetpointCentred < 1.0f) { if (rawSetpointCentred < 1.0f) {
setpointSpeedModified *= rawSetpointCentred; setpointSpeed *= rawSetpointCentred;
setpointAccelerationModified *= rawSetpointCentred; setpointAcceleration *= rawSetpointCentred;
holdCount[axis] = 4;
} }
} }
setpointDeltaImpl[axis] = setpointSpeedModified * pidGetDT();
prevAcceleration[axis] = setpointAcceleration; prevAcceleration[axis] = setpointAcceleration;
// all values afterwards are small numbers
setpointAcceleration *= pidGetDT(); setpointAcceleration *= pidGetDT();
setpointAccelerationModified *= pidGetDT(); setpointDeltaImpl[axis] = setpointSpeed * pidGetDT();
const float ffBoostFactor = pidGetFfBoostFactor(); const float ffBoostFactor = pidGetFfBoostFactor();
float clip = 1.0f;
float boostAmount = 0.0f; float boostAmount = 0.0f;
if (ffBoostFactor != 0.0f) { if (ffBoostFactor != 0.0f) {
//calculate clip factor to reduce boost on big spikes
if (pidGetSpikeLimitInverse()) {
clip = 1 / (1 + (setpointAcceleration * setpointAcceleration * pidGetSpikeLimitInverse()));
clip *= clip;
}
// don't clip first step inwards from max deflection
if (fabsf(prevRawSetpoint[axis]) > 0.95f * ffMaxRate[axis] && fabsf(setpointSpeed) > 3.0f * fabsf(prevSetpointSpeed[axis])) {
clip = 1.0f;
}
// calculate boost and prevent kick-back spike at max deflection // calculate boost and prevent kick-back spike at max deflection
if (fabsf(rawSetpoint) < 0.95f * ffMaxRate[axis] || fabsf(setpointSpeed) > 3.0f * fabsf(prevSetpointSpeed[axis])) { if (fabsf(rawSetpoint) < 0.95f * ffMaxRate[axis] || absSetpointSpeed > 3.0f * absPrevSetpointSpeed) {
boostAmount = ffBoostFactor * setpointAccelerationModified; boostAmount = ffBoostFactor * setpointAcceleration * setpointAccelerationModifier;
} }
} }
@ -172,18 +158,23 @@ FAST_CODE_NOINLINE float interpolatedSpApply(int axis, bool newRcFrame, ffInterp
if (axis == FD_ROLL) { if (axis == FD_ROLL) {
DEBUG_SET(DEBUG_FF_INTERPOLATED, 0, lrintf(setpointDeltaImpl[axis] * 100)); DEBUG_SET(DEBUG_FF_INTERPOLATED, 0, lrintf(setpointDeltaImpl[axis] * 100));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 1, lrintf(setpointAccelerationModified * 100)); DEBUG_SET(DEBUG_FF_INTERPOLATED, 1, lrintf(setpointAcceleration * 100));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 2, lrintf(setpointAcceleration * 100)); DEBUG_SET(DEBUG_FF_INTERPOLATED, 2, lrintf(((setpointDeltaImpl[axis] + boostAmount) * 100)));
DEBUG_SET(DEBUG_FF_INTERPOLATED, 3, holdCount[axis]); DEBUG_SET(DEBUG_FF_INTERPOLATED, 3, ffStatus[axis]);
} }
setpointDeltaImpl[axis] += boostAmount * clip; // first order smoothing of boost to reduce jitter
// first order (kind of) smoothing of FF
const float ffSmoothFactor = pidGetFfSmoothFactor(); const float ffSmoothFactor = pidGetFfSmoothFactor();
boostAmount = prevBoostAmount[axis] + ffSmoothFactor * (boostAmount - prevBoostAmount[axis]);
prevBoostAmount[axis] = boostAmount;
setpointDeltaImpl[axis] += boostAmount;
// first order smoothing of FF (second order boost filtering since boost filtered twice)
setpointDeltaImpl[axis] = prevDeltaImpl[axis] + ffSmoothFactor * (setpointDeltaImpl[axis] - prevDeltaImpl[axis]); setpointDeltaImpl[axis] = prevDeltaImpl[axis] + ffSmoothFactor * (setpointDeltaImpl[axis] - prevDeltaImpl[axis]);
prevDeltaImpl[axis] = setpointDeltaImpl[axis]; prevDeltaImpl[axis] = setpointDeltaImpl[axis];
// apply averaging
if (type == FF_INTERPOLATE_ON) { if (type == FF_INTERPOLATE_ON) {
setpointDelta[axis] = setpointDeltaImpl[axis]; setpointDelta[axis] = setpointDeltaImpl[axis];
} else { } else {

View File

@ -121,7 +121,7 @@ PG_RESET_TEMPLATE(pidConfig_t, pidConfig,
#define LAUNCH_CONTROL_YAW_ITERM_LIMIT 50 // yaw iterm windup limit when launch mode is "FULL" (all axes) #define LAUNCH_CONTROL_YAW_ITERM_LIMIT 50 // yaw iterm windup limit when launch mode is "FULL" (all axes)
PG_REGISTER_ARRAY_WITH_RESET_FN(pidProfile_t, PID_PROFILE_COUNT, pidProfiles, PG_PID_PROFILE, 15); PG_REGISTER_ARRAY_WITH_RESET_FN(pidProfile_t, PID_PROFILE_COUNT, pidProfiles, PG_PID_PROFILE, 1);
void resetPidProfile(pidProfile_t *pidProfile) void resetPidProfile(pidProfile_t *pidProfile)
{ {
@ -202,8 +202,7 @@ void resetPidProfile(pidProfile_t *pidProfile)
.idle_p = 50, .idle_p = 50,
.idle_pid_limit = 200, .idle_pid_limit = 200,
.idle_max_increase = 150, .idle_max_increase = 150,
.ff_interpolate_sp = FF_INTERPOLATE_AVG2, .ff_interpolate_sp = FF_INTERPOLATE_ON,
.ff_spike_limit = 60,
.ff_max_rate_limit = 100, .ff_max_rate_limit = 100,
.ff_smooth_factor = 37, .ff_smooth_factor = 37,
.ff_boost = 15, .ff_boost = 15,
@ -246,12 +245,6 @@ void pidStabilisationState(pidStabilisationState_e pidControllerState)
const angle_index_t rcAliasToAngleIndexMap[] = { AI_ROLL, AI_PITCH }; const angle_index_t rcAliasToAngleIndexMap[] = { AI_ROLL, AI_PITCH };
float pidGetSpikeLimitInverse()
{
return pidRuntime.ffSpikeLimitInverse;
}
float pidGetFfBoostFactor() float pidGetFfBoostFactor()
{ {
return pidRuntime.ffBoostFactor; return pidRuntime.ffBoostFactor;

View File

@ -196,7 +196,6 @@ typedef struct pidProfile_s {
uint8_t ff_interpolate_sp; // Calculate FF from interpolated setpoint uint8_t ff_interpolate_sp; // Calculate FF from interpolated setpoint
uint8_t ff_max_rate_limit; // Maximum setpoint rate percentage for FF uint8_t ff_max_rate_limit; // Maximum setpoint rate percentage for FF
uint8_t ff_spike_limit; // FF stick extrapolation lookahead period in ms
uint8_t ff_smooth_factor; // Amount of smoothing for interpolated FF steps uint8_t ff_smooth_factor; // Amount of smoothing for interpolated FF steps
uint8_t dyn_lpf_curve_expo; // set the curve for dynamic dterm lowpass filter uint8_t dyn_lpf_curve_expo; // set the curve for dynamic dterm lowpass filter
uint8_t level_race_mode; // NFE race mode - when true pitch setpoint calcualtion is gyro based in level mode uint8_t level_race_mode; // NFE race mode - when true pitch setpoint calcualtion is gyro based in level mode
@ -257,7 +256,6 @@ typedef struct pidRuntime_s {
float antiGravityOsdCutoff; float antiGravityOsdCutoff;
float antiGravityThrottleHpf; float antiGravityThrottleHpf;
float ffBoostFactor; float ffBoostFactor;
float ffSpikeLimitInverse;
float itermAccelerator; float itermAccelerator;
uint16_t itermAcceleratorGain; uint16_t itermAcceleratorGain;
float feedForwardTransition; float feedForwardTransition;
@ -413,5 +411,4 @@ float pidGetDT();
float pidGetPidFrequency(); float pidGetPidFrequency();
float pidGetFfBoostFactor(); float pidGetFfBoostFactor();
float pidGetFfSmoothFactor(); float pidGetFfSmoothFactor();
float pidGetSpikeLimitInverse();
float dynLpfCutoffFreq(float throttle, uint16_t dynLpfMin, uint16_t dynLpfMax, uint8_t expo); float dynLpfCutoffFreq(float throttle, uint16_t dynLpfMin, uint16_t dynLpfMax, uint8_t expo);

View File

@ -204,7 +204,6 @@ void pidInitFilters(const pidProfile_t *pidProfile)
pt1FilterInit(&pidRuntime.antiGravityThrottleLpf, pt1FilterGain(ANTI_GRAVITY_THROTTLE_FILTER_CUTOFF, pidRuntime.dT)); pt1FilterInit(&pidRuntime.antiGravityThrottleLpf, pt1FilterGain(ANTI_GRAVITY_THROTTLE_FILTER_CUTOFF, pidRuntime.dT));
pidRuntime.ffBoostFactor = (float)pidProfile->ff_boost / 10.0f; pidRuntime.ffBoostFactor = (float)pidProfile->ff_boost / 10.0f;
pidRuntime.ffSpikeLimitInverse = pidProfile->ff_spike_limit ? 1.0f / ((float)pidProfile->ff_spike_limit / 10.0f) : 0.0f;
} }
void pidInit(const pidProfile_t *pidProfile) void pidInit(const pidProfile_t *pidProfile)