PID control discussion

Official FreeEMS vanilla firmware development, the heart and soul of the system!
User avatar
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

yeah - I'd sort of done that, by adding a set 75 to it (not sure what it accomplished) but I like what you suggested and will try it.

Right now I need to figure out why I'm not getting a constant offset, somehow I'm getting the same p-acting-like-i thing from before, but have been caught up in other projects and done little testing.

Thanks for the suggestion!
-Abe.
User avatar
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

Hmmmm. I have to think about this. I wonder if my whole

output = ( (40* output) + alpha-beta-gamma-terms*error ) / 40;

...thing isn't making the P term act like an integral since I'm adding in one amount but subtracting out 1/40th of it the next time through.

This scaling thing would be much easier if I could use units an variable types I chose....
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: PID control discussion

Post by thebigmacd »

8InchesFlacid wrote:Hmmmm. I have to think about this. I wonder if my whole

output = ( (40* output) + alpha-beta-gamma-terms*error ) / 40;

...thing isn't making the P term act like an integral since I'm adding in one amount but subtracting out 1/40th of it the next time through.

This scaling thing would be much easier if I could use units an variable types I chose....
Shouldn't be a problem, as you can easily split it into two fractions without factoring.
Make it:

output = ( (40* output) + alpha-beta-gamma-terms*error ) / 40;
position = output + open_loop_position;

Even though the numerical value of open_loop_position will not be evident on the output, it will still properly adjust your steady-state output as the target rpm changes. You can easily initialize output = 0 and still have control while the PID starts to kick in. The key is do not include the open loop position in the feedback loop or it will become a runaway integral.
Keith MacDonald
Control Engineering (Systems) Technologist
User avatar
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

Hmmm, my code already had that, though not as neatly as yours (I just started throwing temp variables at it since it was the middle of the night some week ago)

Code: Select all

			   tmp3 = IACmotor_pos - PID_OFFSET_VALUE;	//remove DC offset

	//output of the form: OUTPUT += Alpha * Error[0] - Beta * Error[1] + Gamma * Error[2]
	//Note subtraction of the beta term prevents kP term from summing to infinity
                            tmp1 = ((PID_SCALE_FACTOR * (long)tmp3) // This is "+=" from Z-transform math
                            		+ (z_alpha * ztrans_idle_error_array[0])	//Adds Kp, KI, and first half of Kd terms
                            		- (z_beta * ztrans_idle_error_array[1])		//this subtracts old Kp off, calcs derivative
                            		+ (z_gamma * ztrans_idle_error_array[2]))	//this removes last cycle's derivative contribution
				         / PID_SCALE_FACTOR;	//This is to scale the gain's contribution

				tmp1 += PID_OFFSET_VALUE;	//add DC offset

                            DISABLE_INTERRUPTS;
                            IACmotor_pos = tmp1;	//Set output (valve pos) to calculated value
                            ENABLE_INTERRUPTS;


So instead of maintaining a seperate variable (I'm not sure which variables get retained and which are recreated each time through the loop) I reassign tmp3 to pick up the output each time. Maybe a bit slower but I think it's functionally equivalent.

So that's already been there, except.... I have PID_OFFSET_VALUE set to 75, which I believe to be in valve %, which is HUGE! My valve normally floats in the high 20s. All this seems to accomplish is when the valve routine gets reset, it puts the initial position at 75% and ticks down through PID control, right? If your integrals are fast, you should end up with that summing to zero long before you get to your target, unless you never let the sum go negative.
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: PID control discussion

Post by thebigmacd »

8InchesFlacid wrote: If your integrals are fast, you should end up with that summing to zero long before you get to your target, unless you never let the sum go negative.
(a) Keep your integrals slow; if you are using a feed-forward value and decently high proportional gain, the integral should just be a slow adjustment.

(b) Don't let the PID output go negative. For sure clamp it between 0 and 100 before adding on your offset, then clamp again so you don't exceed 100. That's what's nice about using discrete PIDs, you can clamp the output values to eliminate integral windup. For what it's worth, you could clamp the max PID result to less than 100 to reduce its influence.

Remember, the real function of the PID controller is to add air when additional loads are present. The feed-forward portion should be tuned to take care of the unloaded idle speed, with the PID adding air when needed. By the nature of this system, there is never a need for the PID controller to go negative, so this is why we should clamp it at 0. Also, the ISV itself can not go to a negative position so this too tells us that we should clamp each stage at zero to get rid of that pesky integral windup, which, as you observed, readily negates the effect of our nice feed-forward value.

Here is some pseudocode to illustrate

ClosedLoopIdle += ABG(error) ' Perform PID correction
ClosedLoopIdle = Limit(ClosedLoopIdle, 0 , 100) ' Clamp closed loop correction
ISV = ClosedLoopIdle + OpenLoopIdle(rpm) ' Add open loop correction
ISV = Limit(ISV, IdlePositionMin , IdlePositionMax) ' Clamp final output for conversion to PWM
Keith MacDonald
Control Engineering (Systems) Technologist
User avatar
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

Keith,
These are some magic posts! Good info!
thebigmacd wrote:
8InchesFlacid wrote: If your integrals are fast, you should end up with that summing to zero long before you get to your target, unless you never let the sum go negative.
(a) Keep your integrals slow; if you are using a feed-forward value and decently high proportional gain, the integral should just be a slow adjustment.

(b) Don't let the PID output go negative. For sure clamp it between 0 and 100 before adding on your offset, then clamp again so you don't exceed 100. That's what's nice about using discrete PIDs, you can clamp the output values to eliminate integral windup. For what it's worth, you could clamp the max PID result to less than 100 to reduce its influence.

Remember, the real function of the PID controller is to add air when additional loads are present. The feed-forward portion should be tuned to take care of the unloaded idle speed, with the PID adding air when needed. By the nature of this system, there is never a need for the PID controller to go negative, so this is why we should clamp it at 0. Also, the ISV itself can not go to a negative position so this too tells us that we should clamp each stage at zero to get rid of that pesky integral windup, which, as you observed, readily negates the effect of our nice feed-forward value.

Here is some pseudocode to illustrate

ClosedLoopIdle += ABG(error) ' Perform PID correction
ClosedLoopIdle = Limit(ClosedLoopIdle, 0 , 100) ' Clamp closed loop correction
ISV = ClosedLoopIdle + OpenLoopIdle(rpm) ' Add open loop correction
ISV = Limit(ISV, IdlePositionMin , IdlePositionMax) ' Clamp final output for conversion to PWM
a - yeah, that's good, I'm only using it as a crutch, anyway
b - It seems weird to me not to let it go a little negetive, especially to let things come down quickly? I guess that's just more work for the integral term to undo after it crosses the setpoint, and anyway, the minimum duty on the valve is something like 15% anyway. Certainly anytime I let it get too low, it just oscillates. Moving that value based on target was an original idea I threw away, but it sounds like what you're saying so I guess I'll do it afterall. :-)

It sounds like basically we're shooting to get the original values always low, but not too low.

Biggest issues now: Not sure where to reset 'ClosedLoopIdle' to 0 (when I just came back into PID control), and ... well, let's see how it goes. :-)
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: PID control discussion

Post by Fred »

Great discussion guys, keep up the good work. Sooner or later you'll come up with a really good implementation that we can use in the future on FreeEMS :-)
thebigmacd wrote:
8InchesFlacid wrote:(b) Don't let the PID output go negative. For sure clamp it between 0 and 100 before adding on your offset
Why not use unsigned ints for that part then? Just check for overflow and you don't have to clamp the bottom end ;-)

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
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

Also not a bad suggestion, I've been trying to decide myself. But I suppose you're right, the valve never opens more than 100% (actually, right now I have it set to not open more than the difference from min open to max open, though in reality it should be the difference between my current minimum open-loop number and the max open), anyway, something that will fit comfortably into an int.

I still need longs for the other variables, the errors, since it's easy to have an error of 6,000 when you're at redline. Though maybe it's good to cap those too, I dunno.


But what I'm really having issues with is the thing losing count. I have no idea how this happens, but if you wrench the RPM around quickly, it will "forget" where it is, and then your p term will still be contributing even when you're at your target. I'm using the GPIOadc's as output variables, and everything seems to be fine, I'm monitoring target rpm, the three errors, my PID loop error, coolant-based dc offset.... It all looks fine, only sometimes the pid error is just wacky.

I have a feeling maybe it's losing count, getting interupted when it's supposed to be shifting values or leaving the loop in the middle and starting over from the outside. None of this should be happening, but it seems to be. I can't slow things down to below one update per half second. Is this a issue with megatune? Maybe I could remove some of the limitations which keep me from setting numbers where they would be most useful.
User avatar
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: PID control discussion

Post by Fred »

OK, if you need a block of calcs to be done without interruption, you need to put a pair of sei() and cli() asm commands around them. In my code you type ATOMIC_START(); and ATOMIC_END(); or something. I think the MS boys just type them in directly, or have different shortcuts/#defines. Use them, and you can ensure you get an answer out the end before you lose information in the middle from an ISR taking over.

Also, post a log screeny for us :-)

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
AbeFM
Post Whore!
Posts: 629
Joined: Sat Feb 16, 2008 12:11 am
Location: Sunny San Diego
Contact:

Re: PID control discussion

Post by AbeFM »

I was debating taking a log. I guess I will.

I thought when you do an interupt, it's transparent to what's interupted?

They use ENABLE_INTTERUPTS or some such thing. Which I thought about putting in just to see if it changes anything. There's still this "IAC_LAST thing to tweak, but that's down the road.
Post Reply