Closed loop idle improve (#481)

* Squashed commit of the following:

commit c73f316fa5bf8c929a8fef5736f4a40045ea992f
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Wed Nov 25 21:53:45 2020 +0100

    cleanup and fix merge issues

commit 57f1e8c6c73bcfb699b18ac51a2ec646be822f55
Merge: 04478ca 131673d
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Wed Nov 25 18:59:58 2020 +0100

    Merge branch 'Closedloop_idle_improvements' into ClosedLoopIdleImprovement

commit 131673dc60417cbc773b0763d2f93e917c83325f
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Sun Nov 22 21:54:03 2020 +0100

    more fiddeling with idle control

commit e4406166cde91552027a0dfb7958d6894098f066
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Sun Nov 22 20:36:24 2020 +0100

    More fiddeling with idle control

commit 45822003d41e021e3ed93e8e14ce05479ddfd17b
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Sat Nov 21 14:59:32 2020 +0100

    fix initial value

commit ff8fadae7844bd8e5934ee0f311fbf5e0842ec29
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Thu Nov 19 23:14:14 2020 +0100

    add TPS limit to prevent integeral windup.

commit 7683b2e65569787b1c94eae6f4847cdadd394402
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Thu Nov 19 22:40:53 2020 +0100

    cleanup idle.h

commit da55ee9dbd76d65608bfb5b950bc948a498b9599
Author: Tjeerd <tjeerdie@users.noreply.github.com>
Date:   Thu Nov 19 22:36:18 2020 +0100

    Further improvement simplifying code for closedloop PID control with feedforward.
    make PWM output work
    Tinkering new Idle control
    working on closedloop idle
    Initial work on improving closed loop Idle control

* reset platformio.ini

Co-authored-by: Tjeerd <tjeerdie@users.noreply.github.com>
This commit is contained in:
Tjeerd 2020-12-01 22:37:58 +01:00 committed by GitHub
parent 8d262bd8fc
commit df1f5ccbf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 30 deletions

View File

@ -390,10 +390,13 @@ page = 1
idleUpOutputInv = bits, U08, 118, [1:1], "No", "Yes"
idleUpOutputPin = bits, U08, 118, [2:7], "Board Default", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "INVALID"
tachoSweepMaxRPM = scalar, U08, 119, "RPM", 100, 0.0, 100, 10000, 0
primingDelay = scalar, U08, 120, "S", 0.1, 0.0, 0.0, 25.5, 1
tachoSweepMaxRPM = scalar, U08, 119, "RPM", 100, 0.0, 100, 10000, 0
primingDelay = scalar, U08, 120, "S", 0.1, 0.0, 0.0, 25.5, 1
unused2-95 = array, U08, 121, [7], "%", 1.0, 0.0, 0.0, 255, 0
iacTPSlimit = scalar, U08, 121, "%", 1, 0, 0, 100, 0
iacRPMlimitHysteresis = scalar, U08, 122, "RPM" 10, 0 10 2500, 0
unused2-95 = array, U08, 121, [5], "%", 1.0, 0.0, 0.0, 255, 0
;Page 2 is the fuel map and axis bins only
page = 2
@ -638,7 +641,7 @@ page = 6
iacCrankBins = array, U08, 112, [4], "F", 1.8, -22.23, -40, 215, 0
#endif
iacAlgorithm = bits , U08, 116, [0:2], "None", "On/Off", "PWM Open loop", "PWM Closed loop", "Stepper Open Loop", "Stepper Closed Loop", "INVALID", "INVALID"
iacAlgorithm = bits , U08, 116, [0:2], "None", "On/Off", "PWM Open loop", "PWM Closed loop", "Stepper Open Loop", "Stepper Closed Loop", "PWM Closed+Open loop", "INVALID"
iacStepTime = bits , U08, 116, [3:5], "INVALID","1", "2", "3", "4", "5", "6","INVALID"
iacChannels = bits, U08, 116, [6:6], "1", "2"
iacPWMdir = bits , U08, 116, [7:7], "Normal", "Reverse"
@ -1348,6 +1351,8 @@ page = 14
defaultValue = fanPin, 0
defaultValue = iacCLminDuty,0
defaultValue = iacCLmaxDuty,100
defaultValue = iacTPSlimit, 5
defaultValue = iacRPMlimitHysteresis, 200
defaultValue = boostMinDuty,0
defaultValue = boostMaxDuty,100
defaultValue = boostSens, 2000
@ -1575,13 +1580,13 @@ menuDialog = main
subMenu = ASE, "Afterstart Enrichment (ASE)"
subMenu = std_separator
subMenu = idleSettings, "Idle Control"
subMenu = iacClosedLoop_curve, "Idle - RPM targets", 7, { iacAlgorithm == 3 || iacAlgorithm == 5 || idleAdvEnabled >= 1 }
subMenu = iacPwm_curve, "Idle - PWM Duty Cycle", 7, { iacAlgorithm == 2 }
subMenu = iacPwmCrank_curve, "Idle - PWM Cranking Duty Cycle", 7, { iacAlgorithm == 2 || iacAlgorithm == 3 }
subMenu = iacClosedLoop_curve, "Idle - RPM targets", 7, { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6 || idleAdvEnabled >= 1 }
subMenu = iacPwm_curve, "Idle - PWM Duty Cycle", 7, { iacAlgorithm == 2 || iacAlgorithm == 6}
subMenu = iacPwmCrank_curve, "Idle - PWM Cranking Duty Cycle", 7, { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 6}
subMenu = iacStep_curve, "Idle - Stepper Motor", 7, { iacAlgorithm == 4 }
subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 || iacAlgorithm == 5 }
subMenu = std_separator
subMenu = idleUpSettings, "Idle Up Settings", { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 4 || iacAlgorithm == 5 }
subMenu = idleUpSettings, "Idle Up Settings", { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 4 || iacAlgorithm == 5 || iacAlgorithm == 6 }
subMenu = std_separator
subMenu = idleAdvanceSettings, "Idle Advance Settings"
@ -1705,6 +1710,8 @@ menuDialog = main
iacPWMdir = "Normal PWM valves increase RPM with higher duty. If RPM decreases with higher duty then select Reverse"
iacCLminDuty = "When using closed loop idle control, this is the minimum duty cycle that the PID loop will allow. Combined with the maximum value, this specifies the working range of your idle valve"
iacCLmaxDuty = "When using closed loop idle control, this is the maximum duty cycle that the PID loop will allow. Combined with the minimum value, this specifies the working range of your idle valve"
iacTPSlimit = "When using OL+CL idle control, if the TPS is higher than this value closed loop idle resets the integeral of the PID (To prevent RPM dips comming back to idle)"
iacRPMlimitHysteresis = "When using closed loop idle control, if the closed loop Target RPM + this value is higher than the actual RPM, closed loop idle resets the integeral of the PID (To prevent RPM dips comming back to idle)"
iacFastTemp = "Below this temperature, the idle output will be high (On). Above this temperature, it will turn off."
idleUpPolarity = "Normal polarity is a ground switch where an earthed signal activates the Idle Up. The internal pullup will be enabled with Normal polarity. \n Inverted may be used if a 5v signal is used to enable the Idle Up."
CTPSPolarity = "Normal polarity is a ground switch where an earthed signal activates the closed throttle position. The internal pullup will be enabled with Normal polarity. \n Inverted may be used if a 5v signal is used to enable the closed throttle position."
@ -2371,16 +2378,18 @@ menuDialog = main
field = "Stepper Inverted", iacStepperInv, { iacAlgorithm == 4 || iacAlgorithm == 5 }
dialog = pwm_idle, "PWM Idle"
field = "Number of outputs", iacChannels, { iacAlgorithm == 2 || iacAlgorithm == 3 }
field = "Idle valve frequency", idleFreq, { iacAlgorithm == 2 || iacAlgorithm == 3 }
field = "Idle valve direction", iacPWMdir, { iacAlgorithm == 2 || iacAlgorithm == 3 }
field = "Number of outputs", iacChannels, { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 6}
field = "Idle valve frequency", idleFreq, { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 6}
field = "Idle valve direction", iacPWMdir, { iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 6}
dialog = closedloop_idle, "Closed loop Idle"
field = "P", idleKP, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "I", idleKI, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "D", idleKD, { iacAlgorithm == 3 || iacAlgorithm == 5 }
field = "Minimum valve duty", iacCLminDuty, { iacAlgorithm == 3 }
field = "Maximum valve duty", iacCLmaxDuty, { iacAlgorithm == 3 }
field = "P", idleKP, { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6}
field = "I", idleKI, { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6}
field = "D", idleKD, { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6}
field = "Minimum valve duty", iacCLminDuty, { iacAlgorithm == 3 || iacAlgorithm == 6}
field = "Maximum valve duty", iacCLmaxDuty, { iacAlgorithm == 3 || iacAlgorithm == 6}
field = "Integeral reset above TPS", iacTPSlimit { iacAlgorithm == 6 }
field = "Integeral reset RPM Hysteresis", iacRPMlimitHysteresis { iacAlgorithm == 6 }
dialog = idleSettings, "Idle Settings"
topicHelp = "https://wiki.speeduino.com/en/configuration/Idle"
@ -2526,7 +2535,7 @@ menuDialog = main
topicHelp = "https://wiki.speeduino.com/en/configuration/IdleAdvance"
panel = idleAdvanceSettings_east
panel = idle_advance_curve, { idleAdvEnabled >= 1 }
panel = iacClosedLoop_curve, { iacAlgorithm == 3 || iacAlgorithm == 5 || idleAdvEnabled >= 1 }
panel = iacClosedLoop_curve, { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6 || idleAdvEnabled >= 1 }
dialog = rotary_ignition, "Rotary Ignition", 4
@ -4584,7 +4593,7 @@ cmdVSSratio6 = "E\x99\x06"
flex = scalar, U08, 34, "%", 1.000, 0.000
flexFuelCor = scalar, U08, 35, "%", 1.000, 0.000
flexIgnCor = scalar, S08, 36, "deg", 1.000, 0.000
idleLoad = scalar, U08, 37, { bitStringValue( idleUnits , iacAlgorithm ) }, { (iacAlgorithm == 2 || iacAlgorithm == 3 || iacMaxSteps <= 255) ? 1.000 : 2.000 }, 0.000 ; This is a combined variable covering both PWM and stepper IACs. The units and precision used depend on which idle algorithm is chosen
idleLoad = scalar, U08, 37, { bitStringValue( idleUnits , iacAlgorithm ) }, { (iacAlgorithm == 2 || iacAlgorithm == 3 || iacAlgorithm == 6 || iacMaxSteps <= 255) ? 1.000 : 2.000 }, 0.000 ; This is a combined variable covering both PWM and stepper IACs. The units and precision used depend on which idle algorithm is chosen
testoutputs = scalar, U08, 38, "bits", 1.000, 0.000
testenabled = bits, U08, 38, [0:0]
testactive = bits, U08, 38, [1:1]
@ -4790,8 +4799,8 @@ cmdVSSratio6 = "E\x99\x06"
entry = hardLimitOn , "Hard Limiter", int, "%d"
entry = idleControlOn, "Idle Control", int, "%d"
entry = idleLoad, "IAC value", int, "%d"
entry = CLIdleTarget, "Idle Target RPM", int, "%d", { iacAlgorithm == 3 || iacAlgorithm == 5 || idleAdvEnabled >= 1 } ;Only show for closed loop idle modes and if idle advance is enabled
entry = CLIdleDelta, "Idle RPM Delta", int, "%d", { iacAlgorithm == 3 || iacAlgorithm == 5 || idleAdvEnabled >= 1 } ;Only show for closed loop idle modes and if idle advance is enabled
entry = CLIdleTarget, "Idle Target RPM", int, "%%d", { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6 || idleAdvEnabled >= 1 } ;Only show for closed loop idle modes and if idle advance is enabled
entry = CLIdleDelta, "Idle RPM Delta", int, "%d", { iacAlgorithm == 3 || iacAlgorithm == 5 || iacAlgorithm == 6 || idleAdvEnabled >= 1 } ;Only show for closed loop idle modes and if idle advance is enabled
entry = baro, "Baro Pressure", int, "%d"
entry = nitrousOn, "Nitrous", int, "%d", { n2o_enable > 0 }
entry = syncLossCounter, "Sync Loss #", int, "%d"

View File

@ -20,7 +20,7 @@
***********************************************************************************************************
* Idle
*/
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_CL) )
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_CL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OLCL))
{
idle_pwm_max_count = 1000000L / (TIMER_RESOLUTION * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 4uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 5KHz
}

View File

@ -787,8 +787,11 @@ struct config2 {
byte tachoSweepMaxRPM;
byte primingDelay;
byte unused2_95[7];
byte iacTPSlimit;
byte iacRPMlimitHysteresis;
byte unused2_95[5];
#if defined(CORE_AVR)
};

View File

@ -11,6 +11,7 @@
#define IAC_ALGORITHM_PWM_CL 3
#define IAC_ALGORITHM_STEP_OL 4
#define IAC_ALGORITHM_STEP_CL 5
#define IAC_ALGORITHM_PWM_OLCL 6 //Openloop plus closedloop IAC control
#define STEPPER_FORWARD 0
#define STEPPER_BACKWARD 1
@ -53,6 +54,7 @@ volatile bool idle_pwm_state;
unsigned int idle_pwm_max_count; //Used for variable PWM frequency
volatile unsigned int idle_pwm_cur_value;
long idle_pid_target_value;
long FeedForwardTerm;
unsigned long idle_pwm_target_value;
long idle_cl_target_rpm;
byte idleCounter; //Used for tracking the number of calls to the idle control function

View File

@ -70,6 +70,13 @@ void initialiseIdle()
enableIdle();
break;
case IAC_ALGORITHM_PWM_OLCL:
iacPWMTable.xSize = 10;
iacPWMTable.valueSize = SIZE_BYTE;
iacPWMTable.axisSize = SIZE_BYTE;
iacPWMTable.values = configPage6.iacOLPWMVal;
iacPWMTable.axisX = configPage6.iacBins;
case IAC_ALGORITHM_PWM_CL:
//Case 3 is PWM closed loop
iacClosedLoopTable.xSize = 10;
@ -314,6 +321,51 @@ void idleControl()
}
break;
case IAC_ALGORITHM_PWM_OLCL: //case 6 is PWM Open Loop table as feedforward term plus closed loop.
//No cranking specific value for closed loop (yet?)
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
{
//Currently cranking. Use the cranking table
currentStatus.idleDuty = table2D_getValue(&iacCrankDutyTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
currentStatus.idleLoad = currentStatus.idleDuty;
idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count);
idle_pid_target_value = idle_pwm_target_value << 2; //Resolution increased
idlePID.Initialize(); //Update output to smooth transition
}
else
{
//Read the OL table as feedforward term
FeedForwardTerm = percentage(table2D_getValue(&iacPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET), idle_pwm_max_count<<2); //All temps are offset by 40 degrees
currentStatus.CLIdleTarget = (byte)table2D_getValue(&iacClosedLoopTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
idle_cl_target_rpm = (uint16_t)currentStatus.CLIdleTarget * 10; //Multiply the byte target value back out by 10
if( (idleCounter & 31) == 1) { idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD); } //This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per 9 seconds
if((currentStatus.RPM - idle_cl_target_rpm > configPage2.iacRPMlimitHysteresis*10) || (currentStatus.TPS > configPage2.iacTPSlimit)){ //reset integeral to zero when TPS is bigger than set value in TS (opening throttle so not idle anymore). OR when RPM higher than Idle Target + RPM Histeresis (comming back from high rpm with throttle closed)
idlePID.ResetIntegeral();
}
PID_computed = idlePID.Compute(true, FeedForwardTerm);
if(PID_computed == true)
{
idle_pwm_target_value = idle_pid_target_value>>2; //increased resolution
if( idle_pwm_target_value == 0 )
{
disableIdle();
BIT_CLEAR(currentStatus.spark, BIT_SPARK_IDLE); //Turn the idle control flag off
break;
}
BIT_SET(currentStatus.spark, BIT_SPARK_IDLE); //Turn the idle control flag on
currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count);
if(currentStatus.idleUpActive == true) { currentStatus.idleDuty += configPage2.idleUpAdder; } //Add Idle Up amount if active
}
idleCounter++;
}
break;
case IAC_ALGORITHM_STEP_OL: //Case 4 is open loop stepper control
//First thing to check is whether there is currently a step going on and if so, whether it needs to be turned off
if( (checkForStepping() == false) && (isStepperHomed() == true) ) //Check that homing is complete and that there's not currently a step already taking place. MUST BE IN THIS ORDER!
@ -577,7 +629,7 @@ static inline void disableIdle()
//Typically this is enabling the PWM interrupt
static inline void enableIdle()
{
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_CL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OL) )
if( (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_CL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OL) || (configPage6.iacAlgorithm == IAC_ALGORITHM_PWM_OLCL) )
{
IDLE_TIMER_ENABLE();
}

View File

@ -235,7 +235,7 @@ integerPID::integerPID(long* Input, long* Output, long* Setpoint,
* pid Output needs to be computed. returns true when the output is computed,
* false when nothing has been done.
**********************************************************************************/
bool integerPID::Compute(bool pOnE)
bool integerPID::Compute(bool pOnE, long FeedForwardTerm)
{
if(!inAuto) return false;
unsigned long now = millis();
@ -250,12 +250,13 @@ bool integerPID::Compute(bool pOnE)
long dInput = (input - lastInput);
long outMinResized = outMin<<PID_SHIFTS;
long outMaxResized = outMax<<PID_SHIFTS;
FeedForwardTerm <<= PID_SHIFTS;
if (ki != 0)
{
outputSum += (ki * error); //integral += error × dt
if(outputSum > outMaxResized) { outputSum = outMaxResized; }
else if(outputSum < outMinResized) { outputSum = outMinResized; }
if(outputSum > outMaxResized-FeedForwardTerm) { outputSum = outMaxResized-FeedForwardTerm; }
else if(outputSum < outMinResized-FeedForwardTerm) { outputSum = outMinResized-FeedForwardTerm; }
}
/*Compute PID Output*/
@ -266,6 +267,7 @@ bool integerPID::Compute(bool pOnE)
output = (kp * error);
if (ki != 0) { output += outputSum; }
if (kd != 0) { output -= (kd * dInput)>>2; }
output += FeedForwardTerm;
output >>= PID_SHIFTS;
}
else
@ -276,9 +278,9 @@ bool integerPID::Compute(bool pOnE)
output = outputSum;
if (kd != 0) { output -= (kd * dInput)>>2; }
output += FeedForwardTerm;
output >>= PID_SHIFTS;
}
if(output > outMax) output = outMax;
else if(output < outMin) output = outMin;
@ -522,7 +524,7 @@ void integerPID::SetControllerDirection(byte Direction)
******************************************************************************/
int integerPID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;}
int integerPID::GetDirection(){ return controllerDirection;}
void integerPID::ResetIntegeral() { outputSum=0;}
//************************************************************************************************************************
#define limitMultiplier 100 //How much outMin and OutMax must be multiplied by to get them in the same scale as the output

View File

@ -97,7 +97,7 @@ class integerPID
void SetMode(int Mode); // * sets PID to either Manual (0) or Auto (non-0)
bool Compute(bool pOnE); // * performs the PID calculation. it should be
bool Compute(bool, long FeedForwardTerm = 0); // * performs the PID calculation. it should be
// called every time loop() cycles. ON/OFF and
// calculation frequency can be set using SetMode
// SetSampleTime respectively
@ -127,6 +127,7 @@ class integerPID
int GetMode(); // inside the PID.
int GetDirection(); //
void Initialize();
void ResetIntegeral();
private: