Generic PID Function Requirments Discussion

Official FreeEMS vanilla firmware development, the heart and soul of the system!
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

Okay then, I'll get the ball rolling (or roll it faster).

For a generic PID controller you need to know if the following exist, and if so, what the values are:

Max control value - The largest value that can be given to the physical actuator. Values larger than this are useless.
Minimum control value - The smallest value that can be given to the physical actuator. Values smaller than this are useless.
Start control value - The value typically given from an approximated curve describing the desired behavior
Target system value - The value for the system that the controller is trying to reach
Current system value - The current value for the system
P control value - Proportional value, see wikipedia for explanation
I control value - Integral value, see wikipedia for explanation
D control value - Derivative value, see wikipedia for explanation
Counter - number of times the PID loop has run (counter rolls over?)
Target deadband low system value - The system will accept (Target value - target deadband low value) without making any changes
Target deadband high system value - The system will accept (Target value + target deadband high value) without making any changes

Proportional deadband low control value - If the proportion is calculated between this value and proportional deadband high value then no proportion is applied
Proportional deadband high control value - If the proportion is calculated between this value and proportional deadband low value then no proportion is applied
Proportional decrease control limit - If the proportion is calculated below this limit, use the value of the limit instead
Proportional increase control limit - If the proportion is calculated above this limit, use the value of the limit instead

Integral deadband low control value - If the integral is calculated between this value and integral deadband high value then no integral is applied
Integral deadband high control value - If the integral is calculated between this value and integral deadband low value then no integral is applied
Integral decrease control limit - If the integral is calculated below this limit, use the value of the limit instead
Integral increase control limit - If the integral is calculated above this limit, use the value of the limit instead

Derivative deadband low control value - If the derivative is calculated between this value and derivative deadband high value then no integral is applied
Derivative deadband high control value - If the derivative is calculated between this value and derivative deadband low value then no integral is applied
Derivative decrease control limit - If the derivative is calculated below this limit, use the value of the limit instead
Derivative increase control limit - If the derivative is calculated above this limit, use the value of the limit instead

That's what I've got right now off the top of my head. Maybe there should be a "speed" input to control how fast the PID "runs" but maybe I'm just talking out of my ass. I'm not a PID expert by any means so please chime in if you know better.

Gearhead made a point in the other PID thread about P, I and D not being constants, but being variables themselves. I can see how this could be taken advantage of (system behavior changes based on outside influence such as temperature), just not sure if it's necessary. Regardless, once implemented in code it would be trivial to make sure P, I and D were variables instead of constants.

Boy what I could do with the PID control above. =D

I'm also getting the itch to write code. Should I bust this out in pseudo-code or would that be a waste of time?
Last edited by BenFenner on Tue Nov 04, 2008 11:17 pm, edited 4 times in total.
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

This could be written much, much nicer but I left it this way for simplicity and clarity.

Code: Select all

GenericPIDController(maxCtrlVal, 
minCtrlVal, 
startCtrlVal,
targetSysVal,
curSysVal,
pVal,
iVal,
dVal,
counter,
targetDeadLowSysVal,
targetDeadHighSysVal,
pDeadLowCtrlVal,
pDeadHighCtrlVal,
pDecreaseCtrlLimit,
pIncreaseCtrlLimit,
iDeadLowCtrlVal,
iDeadHighCtrlVal,
iDecreaseCtrlLimit,
iIncreaseCtrlLimit,
dDeadLowCtrlVal,
dDeadHighCtrlVal,
dDecreaseCtrlLimit,
dIncreaseCtrlLimit){

    // kP, kI and kD variables
    kP, kI, kD;

    //Increase counter for use in I calculations
    counter++;

    //Check if system is within deadband range and return if so
    if(curSysVal < targetDeadHighSysVal && curSysVal > targetDeadLowSysVal){
        return(startCtrlVal, counter);
    }
    
    //Calculate new values of kP, kI and kD
    kP = calculatePVal(maxCtrlVal, minCtrlVal, startCtrlVal, curSysVal, kVal);
    kI = calculateIVal(maxCtrlVal, minCtrlVal, startCtrlVal, curSysVal, iVal, counter);
    kD = calculateDVal(maxCtrlVal, minCtrlVal, startCtrlVal, curSysVal, dVal);
    
    //Check if new PID values are over limits and adjust if so
    if(kP < pDecreaseCtrlLimit){
        kP = pDecreaseCtrlLimit;
    else if(kP > pIncreaseCtrlLimit){
        kP = pIncreaseCtrlLimit;
    }
    if(kI < iDecreaseCtrlLimit){
        kI = iDecreaseCtrlLimit;
    else if(kI > iIncreaseCtrlLimit){
        kI = iIncreaseCtrlLimit;
    }
    if(kD < dDecreaseCtrlLimit){
        kD = dDecreaseCtrlLimit;
    else if(kD > dIncreaseCtrlLimit){
        kD = dIncreaseCtrlLimit;
    }

    //Check if new PID values are within deadband ranges and ignore if so
    if(kP < pDeadHighCtrlVal && kP > pDeadLowCtrlVal){
        kP = 0;
    }
    if(kI < iDeadHighCtrlVal && kI > iDeadLowCtrlVal){
        kI = 0;
    }
    if(kD < dDeadHighCtrlVal && kD > dDeadLowCtrlVal){
        kD = 0;
    }
    
    return(startCtrlVal + kP + kI + kD, counter);

}
Last edited by BenFenner on Wed Nov 05, 2008 4:39 am, edited 3 times in total.
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: Generic PID Function Requirments Discussion

Post by Fred »

Can you explain in english why you have dozens of inputs and what they all are and what they all do in detail? I admit to paying less than full attention, but I don't get it 100% from that.

Fred.
DIYEFI.org - where Open Source means Open Source, and Free means Freedom
FreeEMS.org - the open source engine management system
FreeEMS dev diary and its comments thread and my turbo truck!
n00bs, do NOT PM or email tech questions! Use the forum!
The ever growing list of FreeEMS success stories!
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

See the post above the code?
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: Generic PID Function Requirments Discussion

Post by Fred »

Yeah, that is what I was referring to :-p make it easy for me, I'm a busy lad these days...

I'm not seeing the value in the max control value for a generic system. it should output X from 65535 and the external logic should apply that percentage to the thing being driven regardless of its bit count, resolution, max/min etc. it can be scaled external to the function if there are artificial limits to be considered. Tell me why I'm wrong if I am.

OK, I re read it and I'm now wondering why the hell you want to limit everything like that. Also, what benefits does limiting bring, and what problem does it solve. And what problems does it cause?

Edit in blue.

The simplest solution is often the best one and I'm wondering why we have dozens of parameters up front on this. If there are solid reasons for them, sure, but I'd like to hear and understand.

You can ignore me if you wish as someone else is writing this block :-) I would like to understand decisions made etc before accepting it in, but that's not critical either really.

Fred.
DIYEFI.org - where Open Source means Open Source, and Free means Freedom
FreeEMS.org - the open source engine management system
FreeEMS dev diary and its comments thread and my turbo truck!
n00bs, do NOT PM or email tech questions! Use the forum!
The ever growing list of FreeEMS success stories!
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

I've got reasons for them being there but I'll wait until after tonight to post as I'm about to go hash out these concerns you (and a mate of mine) brought up at dinner with him and another electrical engineer.

Regardless, if those are your only concerns, maybe we're well on our way... =P
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: Generic PID Function Requirments Discussion

Post by Fred »

Before dinner, read edited bit :-)

I'm not sure about what else there is to pick on. See my comments on Abes post too.

Fred.
DIYEFI.org - where Open Source means Open Source, and Free means Freedom
FreeEMS.org - the open source engine management system
FreeEMS dev diary and its comments thread and my turbo truck!
n00bs, do NOT PM or email tech questions! Use the forum!
The ever growing list of FreeEMS success stories!
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

From the spirit-of-pure-PID-control department I have to concede the excess deadbands and limits. I'm convinced they are not part of the solution. Also Fred says the max and min values should move outside of the PID control which makes sense. Revised legend and code here:


Start control value - The value typically given from an approximated curve describing the desired behavior
Target system value - The value for the system that the controller is trying to reach
Current system value - The current value for the system
P control value - Proportional value, see wikipedia for explanation
I control value - Integral value, see wikipedia for explanation
D control value - Derivative value, see wikipedia for explanation
Counter - number of times the PID loop has run (counter rolls over?)
Target deadband low system value - The system will accept (Target value - target deadband low value) without making any changes
Target deadband high system value - The system will accept (Target value + target deadband high value) without making any changes

Code: Select all

GenericPIDController(startCtrlVal,
targetSysVal,
curSysVal,
pVal,
iVal,
dVal,
counter,
targetDeadLowSysVal,
targetDeadHighSysVal){

    // kP, kI and kD variables
    kP, kI, kD;

    //Increase counter for use in I calculations
    counter++;

    //Check if system is within deadband range and return if so
    if(curSysVal < targetDeadHighSysVal && curSysVal > targetDeadLowSysVal){
        return(startCtrlVal, counter);
    }
    
    //Calculate new values of kP, kI and kD
    kP = calculatePVal(startCtrlVal, curSysVal, kVal);
    kI = calculateIVal(startCtrlVal, curSysVal, iVal, counter);
    kD = calculateDVal(startCtrlVal, curSysVal, dVal);
        
    return(startCtrlVal + kP + kI + kD, counter);

}
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: Generic PID Function Requirments Discussion

Post by Fred »

BenFenner wrote:Also Fred says <XYZ blah blah BS often etc>
Let's not ever do anything fore that reason, rather because we all agree it's the right thing to do :-)
Counter - number of times the PID loop has run (counter rolls over?)
What is this one for? Or just out of interest for logging?

What about internal state?

What was out output last time? (do we care, why do we care/not care)
How long has it been since last time? (ditto)

I was trying to reason the others into a struct of state etc, but at the end of the day you want them passed in separately, or maybe as a struct constructed and then passed in, but thats an imp detail.

I wonder about Abe and GearHead and TheBigMacD. I know they all have opinions on this.

Fred.
DIYEFI.org - where Open Source means Open Source, and Free means Freedom
FreeEMS.org - the open source engine management system
FreeEMS dev diary and its comments thread and my turbo truck!
n00bs, do NOT PM or email tech questions! Use the forum!
The ever growing list of FreeEMS success stories!
User avatar
BenFenner
LQFP144 - On Top Of The Game
Posts: 360
Joined: Wed Jul 09, 2008 3:15 pm

Re: Generic PID Function Requirments Discussion

Post by BenFenner »

I'm not following your suggestions blindly, only when they make sense (to me at least).

Like I said, I'm not a PID control expert. As far as I know, to calculate the "kI" term you need to know how many iterations you've done which is what the counter is for. You might also need to know the past value of "kI"... I haven't looked into it deep enough. From other PID controllers I've seen in code, my implementation is unorthodox and we can move to the accepted style if we'd like. I've been keeping it this way for simplicity's sake.
Fred wrote:What about internal state?
What about it? I know the code is incomplete as there is no looping structure, but I'm not entirely understanding the question.
Fred wrote:What was our output last time?
The "Start control value" plays double duty here. It can be fed the start value, and also takes in the last output value. Same thing as far as my controller is concerned.
Fred wrote:How long has it been since last time?
No idea. I'm not great with time. If we need it, it might be best if someone else adds that. Right now it would seem I'm relying on what ever is calling the PID controller code to handle a lot of the overhead (probably a bad idea, but you wanted generic). Right now the caller would have to call the method after an identical delay (every ten rpm readings for rpm dependent stuff, every five O2 readings for O2 reading stuff, etc.) to get the desired behavior. Would we like to avoid this and just be able to call it when ever we have time for it? Doesn't sound ideal to me...
Fred wrote:I was trying to reason the others into a struct of state etc, but at the end of the day you want them passed in separately, or maybe as a struct constructed and then passed in, but thats an imp detail.
Oh absolutely you want a struct (or object) passed in that has all the required data. This is just pseudo-code remember.
Fred wrote:I wonder about Abe and GearHead and TheBigMacD. I know they all have opinions on this.
Yah I don't want to really have to do this alone. It might be possible, but I'd much rather have help because this'll take for ever if it's just the two of us (me flailing away and you humoring me).
Post Reply