Idle PID improvements

This commit is contained in:
Andrei 2020-07-22 18:39:09 +03:00
parent 1460194065
commit b7b50e5cb3
1 changed files with 66 additions and 24 deletions

View File

@ -61,6 +61,8 @@ EXTERN_ENGINE;
Engine *unitTestEngine;
#endif
// todo: move all static vars to engine->engineState.idle?
static bool prettyClose = false;
static bool shouldResetPid = false;
@ -70,6 +72,13 @@ static bool shouldResetPid = false;
// See automaticIdleController().
static bool mightResetPid = false;
// This is needed to slowly turn on the PID back after it was reset.
static bool wasResetPid = false;
// This is used when the PID configuration is changed, to guarantee the reset
static bool mustResetPid = false;
static efitimeus_t restoreAfterPidResetTimeUs = 0;
class PidWithOverrides : public PidIndustrial {
public:
float getOffset() const override {
@ -283,19 +292,34 @@ static void undoIdleBlipIfNeeded() {
}
}
static bool isOutOfAutomaticIdleCondition(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
static bool isOutOfAutomaticIdleCondition(float rpm, int targetRpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
// first, check the pedal threshold
if (CONFIG(throttlePedalUpPin) != GPIO_UNASSIGNED) {
return !engine->engineState.idle.throttlePedalUpState;
if (!engine->engineState.idle.throttlePedalUpState) {
return true;
}
} else {
const auto [valid, pos] = Sensor::get(SensorType::DriverThrottleIntent);
// Disable auto idle in case of TPS/Pedal failure
if (!valid) {
return true;
}
if (pos > CONFIG(idlePidDeactivationTpsThreshold))
return true;
}
const auto [valid, pos] = Sensor::get(SensorType::DriverThrottleIntent);
// Disable auto idle in case of TPS/Pedal failure
if (!valid) {
return true;
// then, check the RPM threshold (if in coasting mode)
if (CONFIG(idlePidRpmUpperLimit) > 0) {
int idlePidLowerRpm = targetRpm + CONFIG(idlePidRpmDeadZone);
int upperRpmLimit = idlePidLowerRpm + CONFIG(idlePidRpmUpperLimit);
if (rpm > upperRpmLimit) {
return true;
}
}
return pos > CONFIG(idlePidDeactivationTpsThreshold);
return false;
}
/**
@ -307,7 +331,21 @@ static percent_t automaticIdleController(float tpsPos DECLARE_ENGINE_PARAMETER_S
industrialWithOverrideIdlePid.antiwindupFreq = engineConfiguration->idle_antiwindupFreq;
industrialWithOverrideIdlePid.derivativeFilterLoss = engineConfiguration->idle_derivativeFilterLoss;
if (isOutOfAutomaticIdleCondition(PASS_ENGINE_PARAMETER_SIGNATURE)) {
// get Target RPM for Auto-PID from a separate table
int targetRpm = getTargetRpmForIdleCorrection(PASS_ENGINE_PARAMETER_SIGNATURE);
efitick_t nowNt = getTimeNowNt();
efitimeus_t nowUs = getTimeNowUs();
float rpm;
if (CONFIG(useInstantRpmForIdle)) {
rpm = engine->triggerCentral.triggerState.calculateInstantRpm(NULL, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
} else {
rpm = GET_RPM();
}
if (isOutOfAutomaticIdleCondition(rpm, targetRpm PASS_ENGINE_PARAMETER_SUFFIX)) {
// Don't store old I and D terms if PID doesn't work anymore.
// Otherwise they will affect the idle position much later, when the throttle is closed.
if (mightResetPid) {
@ -320,21 +358,8 @@ static percent_t automaticIdleController(float tpsPos DECLARE_ENGINE_PARAMETER_S
return engine->engineState.idle.baseIdlePosition;
}
// get Target RPM for Auto-PID from a separate table
int targetRpm = getTargetRpmForIdleCorrection(PASS_ENGINE_PARAMETER_SIGNATURE);
efitick_t nowNt = getTimeNowNt();
float rpm;
if (CONFIG(useInstantRpmForIdle)) {
rpm = engine->triggerCentral.triggerState.calculateInstantRpm(NULL, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
} else {
rpm = GET_RPM();
}
// #1553 we need to give FSIO variable offset or minValue a chance
bool acToggleJustTouched = (getTimeNowUs() - engine->acSwitchLastChangeTime) < MS2US(500);
bool acToggleJustTouched = (nowUs - engine->acSwitchLastChangeTime) < MS2US(500);
// check if within the dead zone
if (!acToggleJustTouched && absI(rpm - targetRpm) <= CONFIG(idlePidRpmDeadZone)) {
engine->engineState.idle.idleState = RPM_DEAD_ZONE;
@ -347,6 +372,17 @@ static percent_t automaticIdleController(float tpsPos DECLARE_ENGINE_PARAMETER_S
percent_t errorAmpCoef = 1.0f;
if (rpm < targetRpm)
errorAmpCoef += (float)CONFIG(pidExtraForLowRpm) / PERCENT_MULT;
// if PID was previously reset, we store the time when it turned on back (see errorAmpCoef correction below)
if (wasResetPid) {
restoreAfterPidResetTimeUs = nowUs;
wasResetPid = false;
}
// increase the errorAmpCoef slowly to restore the process correctly after the PID reset
// todo: move restoreAfterPidResetTimeUs to engineState.idle?
efitimeus_t timeSincePidResetUs = nowUs - /*engine->engineState.idle.*/restoreAfterPidResetTimeUs;
// todo: add 'pidAfterResetDampingPeriodMs' setting
errorAmpCoef = interpolateClamped(0.0f, 0.0f, MS2US(/*CONFIG(pidAfterResetDampingPeriodMs)*/1000), errorAmpCoef, timeSincePidResetUs);
// If errorAmpCoef > 1.0, then PID thinks that RPM is lower than it is, and controls IAC more aggressively
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->setErrorAmplification(errorAmpCoef);
@ -415,9 +451,14 @@ static percent_t automaticIdleController(float tpsPos DECLARE_ENGINE_PARAMETER_S
}
if (shouldResetPid) {
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->reset();
// we reset only if I-term is negative, because the positive I-term is good - it keeps RPM from dropping too low
if (getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->getIntegration() <= 0 || mustResetPid) {
getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->reset();
mustResetPid = false;
}
// alternatorPidResetCounter++;
shouldResetPid = false;
wasResetPid = true;
}
@ -557,6 +598,7 @@ void setDefaultIdleParameters(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
void onConfigurationChangeIdleCallback(engine_configuration_s *previousConfiguration) {
shouldResetPid = !getIdlePid(PASS_ENGINE_PARAMETER_SIGNATURE)->isSame(&previousConfiguration->idleRpmPid);
mustResetPid = shouldResetPid;
idleSolenoidOpen.setFrequency(CONFIG(idle).solenoidFrequency);
idleSolenoidClose.setFrequency(CONFIG(idle).solenoidFrequency);
}