I’ve seen several PID sources posted on the forum, and all of them divide the proportional output by 100, the integral output by 1000 and the derivative output by 10. Then the return value of the PID goes something like this:

return Limit_Mix(2000 + 127 + P + I - D);

Why do this? Why not just set the gains for P, I, and D to something that’ll work within the range of 0 to 255 and the integral max to 255 and min to 0? I don’t quite understand where the 100, 1000, and 10 came from either? Could someone please explain this?

Thanks,
Jedd

Edit - Another question? How do most people find reasonable constants for the gains of P, I and D?

I don’t think it is realistically possible to choose constants that keep the output between 0 and 254. As for the division thing, it lets you get decimal precision without making the uC do floating point operations, which it is very bad at. (ex 34/100 is the same as .34, but the robot executes it much faster)

To find gains, we just make the setpoint something near the middle of the mechanism’s travel (to keep oscillations from damaging the mechanism). Then we link the P, I, and D constants to an analog input on the OI. After that we twiddle the knobs in this order: increase P until it gets near the setpoint, increase I until it reaches the setpoint, and finish by increasing D until any oscillations present are damped. We then use the numbers determined this way as our constants. It actually takes very little time to do it this way, I think it took us maybe 20 minutes to tune three loops last year.

I don’t see any complete PID implementations in that thread… I would recommend reading these two documents if you intend to code a PID loop: Matt Krass’s whitepaper, and PID without a PhD. If you want to get a little more advanced you can search around the forum to find out how to go about writing generalized PID functions. (PID without a PhD actually uses a generalized function, but I don’t think it talks about it specifically)

I have written an attempt at a generic PID function (rough draft of it at least).

typedef char bool;
#define false 0
#define true !false
/////////////////////////////
// PID info struct - state //
/////////////////////////////
typedef struct
{
int kpGain; /* Propotional gain */
int kiGain; /* Integral gain */
int kdGain; /* Derivative gain */
int previousError; // Previous cycles error */
int sumError; /* Sum of each cycles' error */
int errorTolerance; /* How small can the error be until we're done */
int iMax; /* Maximum integral output */
int iMin; /* Minimum integral output */
bool finished; /* Signifies if the desired target has been reached */
} PID;
void InitPID( PID* pid, int pGain, int iGain, int dGain, int imin, int imax, int et );
int PID( PID* pid, int error );
void InitPID( PID* pid, int pGain, int iGain, int dGain, int imax, int imin, int et )
{
pid->kpGain = pGain;
pid->kiGain = iGain;
pid->kdGain = dGain;
pid->iMin = imin;
pid->iMax = imax;
pid->errorTolerance = et;
pid->finished = false;
}
int PID( PID* pid, int error )
{
int P, I, D, errorDelta;
P = pid->kpGain * error;
pid->sumError += error;
if( pid->sumError > pid->iMax )
pid->sumError = pid->iMax;
else if( pid->sumError < pid->iMin )
pid->sumError = pid->iMin;
I = pid->kiGain * pid->sumError;
errorDelta = error - pid->previousError;
D = pid->kdGain * errorDelta;
if( errorDelta == 0 && error < pid->errorTolerance && error > -pid->errorTolerance )
pid->finished = true;
else
pid->finished = false;
pid->previousError = error;
return P + I - D;
}

My version would allow me to use a potentiometer with it, wouldn’t? While the other one in the above thread could be used with motors and such?

Edit - I’m pretty confident it works, but I don’t know for sure. I need to make a test case for it (I don’t have access to the robot), but I don’t know how to really. Does anyone know how to setup an example?

It looks good at first glance, but you may want to do the dividing trick (especially on I, that usually ends up as a quite small constant) to improve accuracy/speed. I would also make sure to limit the output to 0-254… I can see no way that you can be certain not to exceed the 0-254 range. If you do, the consequences can be dire for both your robot’s mechanical well-being, and bystanders’ (ask Billfred what a robot arm can do if you’re too close… you really don’t want to get an uppercut from your robot). A quick Limit_Mix() call is cheap insurance against accidentally causing major robot damage and/or bodily harm.

I added the divide by x’s in there to increase precision… basically this robot controller cannot do floating point calculations, so i had to tell the controller to divide by something to get some amount of precision.

when i divide by ten, you are essentially getting precision of control to 1 decimal place, 100 is two, and 1000 is three. kI is divided by 1000 because typically very small changes in that constant can have a very large impact on the system. likewise, larger changes on kD wont have such a great impact.