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 »

I guess I could. It's not floating point, so I don't expect anything strange there. It only rarely moves, when it does, it just keeps moving. I didn't think the log showed it.

What bugs me is I get to where the error is 0, and I've still got a correction. That's what bugs me. And if I go away and come back to the same value, I'll have a different correction.

That's not proportional. There should be no memory.

Maybe I'll write a true p-only, no transform.

Output = openloopoffset + ( ki * error[0] );

Can't see anything wrong with that.
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: Maybe I'll write a true p-only, no transform.

Output = openloopoffset + ( ki * error[0] );
That's what I was getting at. *thumbsup*
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 »

Ok, there's lots of extra stuff in here, but it works great. Take any term, remove it, and it does what it should - responds to sudden changes, intagrates over errors, or has a directly proportional to the error.

The only weird thing was this bit"

Code: Select all

k_pid_array[3] = ( ztrans_idle_error_array[0] - ztrans_idle_error_array[1] ) * flash5.pwmidle_Kd / PID_SCALE_FACTOR;	//Pure I control
k_pid_array[2] = k_pid_array[3] + (KD_DECAY_FACTOR * k_pid_array[2]);	//Set integral over Kd speed (0 = no effect)
I'm hoping integrating (decaying average) over the D term will help to make it less sensitive to sudden changes, but still "pad" a rapid decrease.

Ok. Dinner time. Enjoy. I'll post again when I get a chance to clean it up, or try it on the car (out of town right now)

Code: Select all


#include "ms2_extra.h"
#define PID_SCALE_FACTOR 125
#define PID_OFFSET_VALUE 23
#define LOWEST_RPM 700
#define KD_DECAY_FACTOR 0.8
	//KD_DECAY_FACTOR = 0 to disable, should be less than 1, higher number means less decay

char valve_closed;
int boost_ctl_error_sum, boost_ctl_last_error;
int pwmidle_error_sum, pwmidle_last_error;
int pwmidle_stepsize, pwmidle_numsteps;
long ztrans_idle_error_array[3] = {0,0,0};		//Holds the current error (element 0) through the error two loops ago (element 2), initialize to 0's - no error history
	//Perhaps there are better values to use here for initialization?
long k_pid_array[4] = {0,0,0};	//Kp, Ki, Kd corrections, and instentaneous Kd	

long z_alpha, z_beta, z_gamma;		//holders for the z-transform proportional, integral and derivative k constants, respectively
long pid_correction, pid_correction_min, pid_correction_max;	//Holds output of PID control function, limits

 pwmidle_error_sum = 0;
    pwmidle_last_error = 0;
    IACmotor_reset = 2;
    pwmidle_timer = 0;
    pwmidle_stepsize = 0;
    pwmidle_numsteps = 0;
	pid_correction_min = -10; //limit PID loop correction to sane values
	pid_correction_max = flash5.pwmidle_open_duty - flash5.pwmidle_min_duty; //limit PID loop correction to sane values



				//shift over values in error array each loop, a "time step". Z += 1
				ztrans_idle_error_array[2] = ztrans_idle_error_array[1];
				ztrans_idle_error_array[1] = ztrans_idle_error_array[0];

				ztrans_idle_error_array[0] = (short)targ_rpm - (short)outpc.rpm;	// Error is target - actual



k_pid_array[0] = ztrans_idle_error_array[0] * flash5.pwmidle_Kp / PID_SCALE_FACTOR;	//Pure P control
k_pid_array[1] += ztrans_idle_error_array[0] * flash5.pwmidle_Ki / PID_SCALE_FACTOR;	//Pure I control
k_pid_array[3] = ( ztrans_idle_error_array[0] - ztrans_idle_error_array[1] ) * flash5.pwmidle_Kd / PID_SCALE_FACTOR;	//Pure I control
k_pid_array[2] = k_pid_array[3] + (KD_DECAY_FACTOR * k_pid_array[2]);	//Set integral over Kd speed (0 = no effect)

	//Limit kp term to someting sane
				if (k_pid_array[1] < -20) {
					k_pid_array[1] = -20;
				} else if (k_pid_array[1] > 20) {
                                    k_pid_array[1] = 20;
                                }

pid_correction = k_pid_array[0] + k_pid_array[1] + k_pid_array[2];	//Sum Error Contributions

				tmp3 = flash5.pwmidle_open_duty - flash5.pwmidle_min_duty; //limit PID loop correction to sane values
				if (pid_correction < pid_correction_min) {
					pid_correction = pid_correction_min;
				} else if (pid_correction > pid_correction_max) {
                                    pid_correction = pid_correction_max;
                                }

				open_loop_offset = flash5.pwmidle_dp_adder * ( (int)targ_rpm - LOWEST_RPM ) / 500;	//For every 100 rpm of target over LOWEST_RPM, add (dashpot_adder) / 5 percent to idle valve position
				open_loop_offset += PID_OFFSET_VALUE;	//Minimum open value for LOWEST_RPM
				tmp1 = pid_correction + open_loop_offset;	//add DC offset


                                if (tmp1 < flash5.pwmidle_min_duty) {
                                    tmp1 = flash5.pwmidle_min_duty;
                                } else if (tmp1 > flash5.pwmidle_open_duty) {
                                    tmp1 = flash5.pwmidle_open_duty;
                                }

                           DISABLE_INTERRUPTS;
                            IACmotor_pos = tmp1;	//Set output (valve pos) to calculated value
                            ENABLE_INTERRUPTS;
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 »

Freakin' awesome. This should go in the daily wtf thread instead.

I wrote straight up PID code, no z-transform.... It worked well. I had some things I wanted to change, so I did. At first I was bothered that my changes seemed to make only a small difference.

Later, I noticed a limit I'd moved to 30 was still 20, this tipped me off.

Turns out, the c file compiles fine, but the errors put out by the linker is one longer than before. So it's not writing the .s19 file. So I kept re-uploading the same .s19 again and again, editing source code, and not noticing.

DAMNIT if your code won't compile without errors, fix it. Much like other undocumented MS errata, the "When your code compiles, expect 14, not 15, and not 13 (unless thou does immediately proceed unto 14) errors, Amen." thing blows.


I'm so shooting anyone who touched the FreeEMS code if it does that in any release form.


I'll post the error in a bit maybe, see if you guys can help. But you got me, I've spent a lot of time looking at a diff file trying to figure out the difference and not coming up with much. I made one variable a long that used to be an int, I think that's it. And in case the code was getting too long I took a bunch of unused stuff out.
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 »

Sure, post the error, maybe I can *try* to reproduce it in my code?

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!
thebigmacd
LQFP112 - Up with the play
Posts: 205
Joined: Thu Apr 10, 2008 5:51 pm

Re: PID control discussion

Post by thebigmacd »

The only thing that pops out at me in your code is the order of operations:

k_pid_array[3] = ( ztrans_idle_error_array[0] - ztrans_idle_error_array[1] ) * flash5.pwmidle_Kd / PID_SCALE_FACTOR; //Pure I control

Are you sure it shouldn't be like this?

k_pid_array[3] = (( ztrans_idle_error_array[0] - ztrans_idle_error_array[1] ) * flash5.pwmidle_Kd) / PID_SCALE_FACTOR; //Pure I control

If "flash5.pwmidle_Kd / PID_SCALE_FACTOR" is an integer operation, you might have some issues if you don't include the brackets.
Keith MacDonald
Control Engineering (Systems) Technologist
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 »

Good spotting!

might >> will

At the least it will introduce large errors. As I said to him last night, there should be casting in there too.

If the subtraction is between two shorts then it should probably go like this :

k_pid_array[3] = ((unsigned long)( ztrans_idle_error_array[0] - ztrans_idle_error_array[1] ) * flash5.pwmidle_Kd) / PID_SCALE_FACTOR; //Pure I control

By doing this you are saying the result of the subtractions multiplication with flash5 should be stored in a long. That way the PID scale has something large enough to divide by in the right format.

This assumes the result is going back into a short and i'm not sure if they should be signed or unsigned.

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!
gearhead
LQFP112 - Up with the play
Posts: 120
Joined: Sun Feb 03, 2008 9:30 pm
Location: Chicago, USA

Re: PID control discussion

Post by gearhead »

So, this thread just dies? No conclusion? No final code? No worries, I'll try my hand at it as well. I am at it too (coding).

Still struggling with it (MS2 PWM), though it is a minor inconvenience in the idle control. It is a royal pain in the O2 correction code, though. My experience is that D does nothing. I cannot get it to 'slow down' as it approaches target speed no matter what the value of D. I'll try a few things in the idle code. If it works there, then I'll put it in the o2 code as well to see if there is any improvement.

One thing that Ken said that is not really talked about here is the mismatch between the precision of the inputs and error compared to the outputs (pwm DC). Depending on your 'pwm frequency' setting in MS2, the granularity of the output can be a tremendous cause of oscillation in idle speed. Make sure you are not fighting this problem. It does not appear that Flacid is, but I thought I'd mention it.

Gearhead
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 »

Ken wrote:The idea that came from Matt_Fulgham is instead of setting the target to the correct target immediately upon entering PID, which will cause a large amount of error, and cause the user to need to de-tune the PID parameters to avoid oscillation, set the target to whatever the RPM is upon first entering the PID loop, then slowly scale down the target to the final value over a user-settable number of seconds (longer is better, will allow for larger PID values). This will keep the error to a minimum, allowing for a very fast control loop, and very quick response to things like the AC turning on.

The PID math itself is getting changed from being a delta added to/from the current valve position to a PID calculated value added to the bias position. In addition, I'm doing all internal math as a percentage in % * 10000 units for precision purposes. So I figure out the error, convert it to a percent do all math on that percent, then when it's time to figure out how much to move the valve by, I convert the percent to a duty or a number of steps based on the min and max duty values. Since all the internal calcs are basically in .01% units, we keep precision, but now I can directly set the valve position rather than using a delta. I'll be making similar changes to boost control as well.

This should result in the PID parameters being easier to tune.

These changes alone should really help the PID code, but I also plan on trying to add some form of tunable dead-time compensation as well, which will also allow for much higher PID values without oscillation.

All of this is a combination of ideas that Matt_Fulgham and I batted back and forth, and about 3-5 hrs of research on PID from various locations.

Ken
Sounds somehow familiar!
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
Fred
Moderator
Posts: 15431
Joined: Tue Jan 15, 2008 2:31 pm
Location: Home sweet home!
Contact:

Re: PID control discussion

Post by Fred »

The time has come for implementation, please see the following thread for a discussion on requirements for a generic PID control function :

http://www.diyefi.org/forum/viewtopic.php?f=8&t=486

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!
Post Reply