[PID] Increase parameters and output resolution (#377)

Parameters are 0.03125% for kP and kI, 0.00981% for kD. Increased output 4 times to give enought room for kP.

PID now ignore compute if input is zero to avoid overshoot, kI and kD part as well.

Added idlePID.Initialize() to crank part to start PID from a working condition.

Show idleLoad when cranking.

Fix TS numbers, increase max value

Co-authored-by: Josh Stewart <josh@noisymime.org>
This commit is contained in:
Vitor Moreno B. Sales 2020-05-21 06:29:38 -03:00 committed by GitHub
parent 904e1956f4
commit b321e8e17d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 85 deletions

View File

@ -576,9 +576,9 @@ page = 6
lnchHardLim = scalar, U08, 51, "rpm", 100, 0.0, 100, 25500, 0
lnchFuelAdd = scalar, U08, 52, "%", 1.0, 0.0, 0.0, 80, 0
idleKP = scalar, U08, 53, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
idleKI = scalar, U08, 54, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
idleKD = scalar, U08, 55, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
idleKP = scalar, U08, 53, "%", 0.03125, 0.0, 0.0, 7.96, 2 ; * ( 1 byte)
idleKI = scalar, U08, 54, "%", 0.03125, 0.0, 0.0, 7.96, 2 ; * ( 1 byte)
idleKD = scalar, U08, 55, "%", 0.00781, 0.0, 0.0, 1.99, 3 ; * ( 1 byte)
boostLimit = scalar, U08, 56, "kPa", 2.0, 0.0, 0.0, 511.0, 0
boostKP = scalar, U08, 57, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
boostKI = scalar, U08, 58, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
@ -1019,9 +1019,9 @@ page = 10
;All related to the closed loop VVT control
vvtCLholdDuty = scalar, U08, 126, "%", 1.0, 0.0, 0.0, 100.0, 0
vvtCLKP = scalar, U08, 127, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
vvtCLKI = scalar, U08, 128, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
vvtCLKD = scalar, U08, 129, "%", 1.0, 0.0, 0.0, 200.0, 0 ; * ( 1 byte)
vvtCLKP = scalar, U08, 127, "%", 0.03125, 0.0, 0.0, 7.96, 2 ; * ( 1 byte)
vvtCLKI = scalar, U08, 128, "%", 0.03125, 0.0, 0.0, 7.96, 2 ; * ( 1 byte)
vvtCLKD = scalar, U08, 129, "%", 0.00781, 0.0, 0.0, 1.99, 3 ; * ( 1 byte)
vvtCLMinAng = scalar, U16, 130, "deg", 1.0, 0.0, 0.0, 360.0, 0 ; * ( 1 bytes)
vvtCLMaxAng = scalar, U16, 132, "deg", 1.0, 0.0, 0.0, 360.0, 0 ; * ( 1 bytes)
@ -1367,7 +1367,7 @@ menuDialog = main
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 }
subMenu = iacPwmCrank_curve, "Idle - PWM Cranking Duty Cycle", 7, { iacAlgorithm == 2 || iacAlgorithm == 3 }
subMenu = iacStep_curve, "Idle - Stepper Motor", 7, { iacAlgorithm == 4 }
subMenu = iacStepCrank_curve, "Idle - Stepper Motor Cranking", 7, { iacAlgorithm == 4 || iacAlgorithm == 5 }
subMenu = std_separator

View File

@ -87,7 +87,7 @@ void initialiseIdle()
#elif defined(CORE_TEENSY)
idle_pwm_max_count = 1000000L / (32 * configPage6.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz
#endif
idlePID.SetOutputLimits(percentage(configPage2.iacCLminDuty, idle_pwm_max_count), percentage(configPage2.iacCLmaxDuty, idle_pwm_max_count));
idlePID.SetOutputLimits(percentage(configPage2.iacCLminDuty, idle_pwm_max_count<<2), percentage(configPage2.iacCLmaxDuty, idle_pwm_max_count<<2));
idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD);
idlePID.SetMode(AUTOMATIC); //Turn PID on
@ -159,7 +159,7 @@ void initialiseIdle()
}
idlePID.SetSampleTime(100);
idlePID.SetOutputLimits(0, (configPage9.iacMaxSteps * 3)); //Maximum number of steps; always less than home steps count.
idlePID.SetOutputLimits(0, (configPage9.iacMaxSteps * 3)<<2); //Maximum number of steps; always less than home steps count.
idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD);
idlePID.SetMode(AUTOMATIC); //Turn PID on
break;
@ -247,6 +247,17 @@ void idleControl()
case IAC_ALGORITHM_PWM_CL: //Case 3 is PWM 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
{
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 second
@ -254,7 +265,7 @@ void idleControl()
PID_computed = idlePID.Compute(true);
if(PID_computed == true)
{
idle_pwm_target_value = idle_pid_target_value;
idle_pwm_target_value = idle_pid_target_value>>2; //increased resolution
if( idle_pwm_target_value == 0 )
{
disableIdle();
@ -267,6 +278,7 @@ void idleControl()
}
idleCounter++;
}
break;
case IAC_ALGORITHM_STEP_OL: //Case 4 is open loop stepper control
@ -291,9 +303,9 @@ void idleControl()
else
{
//Standard running
//Only do a lookup of the required value around 4 times per second (Once every 255 mainloops). Any more than this can create too much jitter and require a hyster value that is too high
//Only do a lookup of the required value around 4 times per second. Any more than this can create too much jitter and require a hyster value that is too high
//We must also have more than zero RPM for the running state
if (((mainLoopCount & 255) == 1) && (currentStatus.RPM > 0))
if (BIT_CHECK(LOOP_TIMER, BIT_TIMER_4HZ) && (currentStatus.RPM > 0))
{
idleStepper.targetIdleStep = table2D_getValue(&iacStepTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active
@ -319,7 +331,7 @@ void idleControl()
//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!
{
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK )/* ||currentStatus.rpmDOT>=500||currentStatus.rpmDOT<=-500*/)
if( BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
{
//Currently cranking. Use the cranking table
idleStepper.targetIdleStep = table2D_getValue(&iacCrankStepsTable, (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET)) * 3; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
@ -332,36 +344,35 @@ void idleControl()
}
doStep();
idle_pid_target_value = idleStepper.targetIdleStep<<2; //Resolution increased
idlePID.Initialize(); //Update output to smooth transition
}
else
{
if( (idleCounter & 31) == 1)
{
//This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD);
iacStepTime_uS = configPage6.iacStepTime * 1000;
iacCoolTime_uS = configPage9.iacCoolTime * 1000;
if( (idleCounter & 31) == 1)
{
//This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD);
iacStepTime_uS = configPage6.iacStepTime * 1000;
iacCoolTime_uS = configPage9.iacCoolTime * 1000;
}
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; //All temps are offset by 40 degrees
PID_computed = idlePID.Compute(true);
idleStepper.targetIdleStep = idle_pid_target_value>>2; //Increase resolution
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active
//limit to the configured max steps. This must include any idle up adder, to prevent over-opening.
if (idleStepper.targetIdleStep > (configPage9.iacMaxSteps * 3) )
{
idleStepper.targetIdleStep = configPage9.iacMaxSteps * 3;
}
doStep();
idleCounter++;
}
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; //All temps are offset by 40 degrees
if(currentStatus.idleUpActive == true) { idle_pid_target_value += configPage2.idleUpAdder; } //Add Idle Up amount if active
PID_computed = idlePID.Compute(true);
idleStepper.targetIdleStep = idle_pid_target_value;
//limit to the configured max steps. This must include any idle up adder, to prevent over-opening.
if (idleStepper.targetIdleStep > (configPage9.iacMaxSteps * 3) )
{
idleStepper.targetIdleStep = configPage9.iacMaxSteps * 3;
}
doStep();
idleCounter++;
}
currentStatus.idleLoad = idleStepper.curIdleStep / 2; //Current step count (Divided by 2 for byte)
currentStatus.idleLoad = idleStepper.curIdleStep / 2; //Current step count (Divided by 2 for byte)
}
//Set or clear the idle active flag
if(idleStepper.targetIdleStep != idleStepper.curIdleStep) { BIT_SET(currentStatus.spark, BIT_SPARK_IDLE); }

View File

@ -213,13 +213,12 @@ integerPID::integerPID(long* Input, long* Output, long* Setpoint,
byte Kp, byte Ki, byte Kd, byte ControllerDirection)
{
myOutput = Output;
myOutput = Output;
myInput = Input;
mySetpoint = Setpoint;
mySetpoint = Setpoint;
inAuto = false;
integerPID::SetOutputLimits(0, 255); //default output limit corresponds to
//the arduino pwm limits
integerPID::SetOutputLimits(0, 255); //default output limit corresponds to the arduino pwm limits
SampleTime = 250; //default Controller Sample Time is 0.25 seconds. This is the 4Hz control time for Idle and VVT
@ -240,45 +239,57 @@ bool integerPID::Compute(bool pOnE)
{
if(!inAuto) return false;
unsigned long now = millis();
//SampleTime = (now - lastTime);
unsigned long timeChange = (now - lastTime);
if(timeChange >= SampleTime)
{
/*Compute all the working error variables*/
long input = *myInput;
long error = *mySetpoint - input;
long dInput = (input - lastInput);
outputSum += (ki * error)/1024; //Note that ki is multiplied by 1024 so it must be divided by 1024 here
if(outputSum > outMax) { outputSum = outMax; }
else if(outputSum < outMin) { outputSum = outMin; }
/*Compute PID Output*/
long output;
if(pOnE)
long input = *myInput;
if(input > 0) //Fail safe, should never be 0
{
output = (kp * error) + outputSum - ((kd * dInput)/128);
long error = *mySetpoint - input;
long dInput = (input - lastInput);
long outMinResized = outMin<<PID_SHIFTS;
long outMaxResized = outMax<<PID_SHIFTS;
if (ki != 0)
{
outputSum += (ki * error); //integral += error × dt
if(outputSum > outMaxResized) { outputSum = outMaxResized; }
else if(outputSum < outMinResized) { outputSum = outMinResized; }
}
/*Compute PID Output*/
long output;
if(pOnE)
{
output = (kp * error);
if (ki != 0) { output += outputSum; }
if (kd != 0) { output -= (kd * dInput)>>2; }
output >>= PID_SHIFTS;
}
else
{
outputSum -= (kp * dInput);
if(outputSum > outMaxResized) { outputSum = outMaxResized; }
else if(outputSum < outMinResized) { outputSum = outMinResized; }
output = outputSum;
if (kd != 0) { output -= (kd * dInput)>>2; }
output >>= PID_SHIFTS;
}
if(output > outMax) output = outMax;
else if(output < outMin) output = outMin;
*myOutput = output;
/*Remember some variables for next time*/
lastInput = input;
lastTime = now;
return true;
}
else
{
outputSum -= kp * dInput;
if(outputSum > outMax) { outputSum = outMax; }
else if(outputSum < outMin) { outputSum = outMin; }
output = outputSum - ((kd * dInput)/128);
}
if(output > outMax) output = outMax;
else if(output < outMin) output = outMin;
*myOutput = output;
/*Remember some variables for next time*/
lastInput = input;
lastTime = now;
return true;
}
else return false;
}
@ -302,9 +313,10 @@ void integerPID::SetTunings(byte Kp, byte Ki, byte Kd)
kd = Kd / SampleTimeInSec;
*/
long InverseSampleTimeInSec = 1000 / SampleTime;
kp = Kp;
ki = (long)((long)Ki * 1024) / InverseSampleTimeInSec;
kd = ((long)Kd * InverseSampleTimeInSec);
//New resolution, 2 shifts to improve ki here | kp 1.563% | ki 1.563% | kd 0.195%
kp = (uint16_t)Kp<<2;
ki = (long)(Ki<<2) / InverseSampleTimeInSec;
kd = (long)(Kd<<2) * InverseSampleTimeInSec;
if(controllerDirection == REVERSE)
{
@ -347,8 +359,8 @@ void integerPID::SetOutputLimits(long Min, long Max)
if(*myOutput > outMax) *myOutput = outMax;
else if(*myOutput < outMin) *myOutput = outMin;
if(outputSum > outMax) { outputSum = outMax; }
else if(outputSum < outMin) { outputSum = outMin; }
if((outputSum>>PID_SHIFTS) > outMax) { outputSum = outMax<<PID_SHIFTS; }
else if((outputSum>>PID_SHIFTS) < outMin) { outputSum = outMin<<PID_SHIFTS; }
}
}
@ -373,11 +385,11 @@ void integerPID::SetMode(int Mode)
******************************************************************************/
void integerPID::Initialize()
{
outputSum = *myOutput;
outputSum = *myOutput<<PID_SHIFTS;
lastInput = *myInput;
lastMinusOneInput = *myInput;
if(outputSum > outMax) { outputSum = outMax; }
else if(outputSum < outMin) { outputSum = outMin; }
if((outputSum>>PID_SHIFTS) > outMax) { outputSum = outMax<<PID_SHIFTS; }
else if((outputSum>>PID_SHIFTS) < outMin) { outputSum = outMin<<PID_SHIFTS; }
}
/* SetControllerDirection(...)*************************************************

View File

@ -88,6 +88,7 @@ class integerPID
#define MANUAL 0
#define DIRECT 0
#define REVERSE 1
#define PID_SHIFTS 7 //Increased resolution
//commonly used functions **************************************************************************
integerPID(long*, long*, long*, // * constructor. links the PID to the Input, Output, and

View File

@ -334,7 +334,15 @@ void doUpdates()
configPage2.aeColdPct = 100;
configPage2.aeColdTaperMin = 40;
configPage2.aeColdTaperMax = 100;
//New PID resolution, old resolution was 100% for each increase, 100% now is stored as 32
configPage6.idleKP = configPage6.idleKP<<5;
configPage6.idleKI = configPage6.idleKI<<5;
configPage6.idleKD = configPage6.idleKD<<5;
configPage10.vvtCLKP = configPage10.vvtCLKP<<5;
configPage10.vvtCLKI = configPage10.vvtCLKI<<5;
configPage10.vvtCLKD = configPage10.vvtCLKD<<5;
//Cranking enrichment to run taper added. Default it to 0,1 secs
configPage10.crankingEnrichTaper = 1;