Re: PID control discussion
Posted: Sat Apr 26, 2008 10:23 am
Apparently Smith is used in the PID O2 correction in ms2 base code.
TRUE DIY engine management discussion forum
http://forum.diyefi.org/
That I'm curious about. I like how it sounds, I'm tempted to try it, but you think it's a poor choice for idle control? I bet it's awesome for boost control, which has big issues with overshoot.thebigmacd wrote:It would work but I don't think it should be implemented. I was just trying to show how a PID is just one of many types of controller that will do the same thing.
The reason I say not use it is you pretty much have to tune the inner loop independently which is isn't practical in an idle control situation. I figured out how to tune it with both loops active at once, but it's not quite as intuitive as tuning a PID.8InchesFlacid wrote:That I'm curious about. I like how it sounds, I'm tempted to try it, but you think it's a poor choice for idle control? I bet it's awesome for boost control, which has big issues with overshoot.
Tell me about that, I guess I will throw together code for idle control, either PID or this two-loop guy. Also, were it you, would you do the z-transform or the regular PID?
As to the programing, could I define alpha, beta, gamma on start up, or do I need to (costily) recalc them every time through the loop? If so, it seems you don't make the gains with the z-transform that you would otherwise have - the expensive opperations involved in rederiving them each time you might as well just use on doing true untransformed PID.
-Abe.
how about anything that has a feedback?SSDwellah wrote: I'm curious which applications are we thinking of using PID control for? I
Awesome, just what I want to hear!SSDwellah wrote:This is my first time on this forum and between this thread and the Nissan CAS one, I think I am in the right place.
Even more awesome as I'm a Java guy and a noob to C :-)SSDwellah wrote:I have a background in systems programming (especially on Linux in c and C++) and also electrical engineering so I hope I can be of some assistance.
More of exactly what I want to hear, I'm glad we come across that way to you :-)Maybe that's why nobody mentioned this aspect, because you are all "can do" positive type who like to DIY ;)
Just the obvious really. I knew there were questions raised about the MS2 implementation and wanted to get some discussion going on so that we get it right. So far 3 of you have real world experience with these things and another one (yes you Abe) has some strong opinions.SSDwellah wrote:4. I'm curious which applications are we thinking of using PID control for? I can think of the obvious idle control, boost control, and perhaps some kind of adaptive knock timing retardation/fuel enrichment. For other types of outputs, such as frequency sensing binary outputs (think EGR operating at a certain band, or Honda VTEC/Nissan VTC or VVL/Toyota VVTI etc.. activating after a certain band) then we have to consider the idea of deadbands to avoid burning out switched output, but this is not a PID control so I won't derail this thread any further :P
thebigmacd wrote:We add lines before and after to shift the data to the proper array location and we are done. As stated in the info above, Kd, Ki, and Kp change depending on the sample period. We can add the last output value by using += rather than using an array index.Code: Select all
OUTPUT[0] = (Kp + Ki + Kd) * Error[0] - (Kp + 2 * Kd) * Error[1] + Kd * Error[2] + OUTPUT[1]
Instant PID control.Code: Select all
Error[2] = Error[1] //Shift data by one time constant Error[1] = Error[0] //Shift data by one time constant Error[0] = Actual - Requested //Calculate error NOW Alpha = Kp + Ki + Kd //Calculate Alpha constant Beta = Kp + 2 * Kd //Calculate Beta constant Gamma = Kd //Calculate Gamma constant OUTPUT += Alpha * Error[0] - Beta * Error[1] + Gamma * Error[2]
Code: Select all
char valve_closed;
int boost_ctl_error_sum, boost_ctl_last_error;
int pwmidle_error_sum, pwmidle_last_error;
int pwmidle_stepsize, pwmidle_numsteps;
int pwmidle_error_array[3]; //Holds the current error (element 0) through the error two loops ago (element 2)
int z_alpha, z_beta, z_gamma; //holders for the z-transform proportional, integral and derivative k constants, respectively
Code: Select all
//My version of idle_ctl_init
void idle_ctl_init(void)
{
pwmidle_error_sum = 0;
pwmidle_last_error = 0;
IACmotor_reset = 2;
pwmidle_timer = 0;
pwmidle_stepsize = 0;
pwmidle_numsteps = 0;
ztrans_idle_error_array = {0,0,0}; //Be sure there's no weird data in the error array.
//Perhaps there are better values to use here for initialization?
//Initialize the Alpha, Beta, Gamma constants of the Z-transform
z_alpha = flash5.pwmidle_Kp + flash5.pwmidle_Ki + flash5.pwmidle_Kd; //initiallize the Alpha constant
z_beta = flash5.pwmidle_Kp + (2 * flash5.pwmidle_Kd); //initiallize the Beta constant
z_gamma = flash5.pwmidle_Kd; //initiallize the Gamma constant
}
Code: Select all
//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] = (int)outpc.rpm - (int)targ_rpm; // Error is actual - target
//Is the sign correct here?
//Original code repeated here. Don't know what IdleCtl = 6 is, don't know these constants. Try leaving them out?
// if (IdleCtl == 6) {
// tmp1 = (((long)((Kp + Ki - Kd) / 100 ) << 8) *
// (int)(flash5.pwmidle_open_duty - flash5.pwmidle_closed_duty)) /
// ((long)pg5_ptr->pwmidle_target_rpms[PWMIDLE_NUM_BINS - 1] * 2550);
// } else {
// tmp1 = ((long)(Kp + Ki - Kd) * (int)(flash5.pwmidle_open_duty - flash5.pwmidle_closed_duty)) /
// ((long)pg5_ptr->pwmidle_target_rpms[PWMIDLE_NUM_BINS - 1] * 1000);
// }
//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 = (IACmotor_pos // this is the += part
+ (z_alpha * pwmidle_error_array[0]) //this is the "now" part
- (z_beta * pwmidle_error_array[1]) //this is the "last time" part
+ (z_gamma * pwmidle_error_array[2])); //this is contribution from 2 loops ago
if (IdleCtl == 7 || IdleCtl == 8) {
if (tmp1 > (int)flash5.pwmidle_min_duty) {
tmp1 = flash5.pwmidle_min_duty;
} else if (tmp1 < (int)flash5.pwmidle_open_duty) {
tmp1 = flash5.pwmidle_open_duty;
}
} else {
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;