PID Programming Help

Hey guys! I’ve avoided programming PIDs for a while because they have always seemed intimidating and difficult to get right, but this year is the year, I finally decided to use a PID to control our robot in autonomous.

Actually, it’s turned out to be a little easier than I initially thought it might be. The proportional part totally makes sense and seems relatively easy to tune, (I just tune it so that it slightly undershoots, right?) but it’s the I and the D that are getting me.

From what I understand, it’s not usually necessary to use a true PID controller, but only a PI, so I’ve simply avoided the D part up to this point. So, here’s what I did:

First, tuned the P variable so that the robot slightly undershot. From what I understand, the I should take care of the undershoot. Second, I tried to tune the I to correct for the undershoot but I simply could not find a number that would get the results I want. If the I value was to high, the robot would overshoot significantly, and after a good amount of time (like 10 seconds), correct itself. If it was too low, it would still undershoot and never reach the target value. I did eventually find a number for the I that worked pretty well, but as soon as I would change the target distance, the exact same problems resurfaced.

So is my tuning process incorrect? Am I missing something? Please let me know. Here’s my code:

	int errorLeft;
	int errorRight;
	float kP = 0.177;
	//float kI = 0.000000005;
	float kI = 0.0000001;
	int integralLeft = 0;
	int integralRight = 0;
	int PIDDriveLeft;
	int PIDDriveRight;
	while(true) {
		errorLeft = PIDTargetValueLeft - getMotorEncoder(left);
		errorRight = PIDTargetValueRight - getMotorEncoder(right);
		integralLeft += errorLeft;
		integralRight += errorRight;
		PIDDriveLeft = kP*errorLeft + kI*integralLeft;
		PIDDriveRight = kP*errorRight + kI*integralRight;
		motor[left] = PIDDriveLeft;
		motor[right] = PIDDriveRight;

Only final thought:

ROBOTC has a built in PID tuning function, but I had some trouble getting that to work since you cannot disable each individual controller to tune one variable at a time, you must use all three variables. I’m wondering if there is a way to turn each off that I’m just missing, and if there is, could that controller potentially be superior to the one that I created? Our team was hoping to use a gyro sensor for turns, but there isn’t a PID programmer built into ROBOTC for the gyro, correct? Or could simply using the integrated PID controller in ROBOTC with just the integrated encoders be more effective than using the gyro?

Please let me know. Thanks, guys!

1 Like

One thing that helps me is printing values for error, actual motor power, kPerror, kIintegral, and kD*derivative into console. That way, you can see what the motor power is when you are a certain distance from target, in addition to what is affecting that value and in what way. You will need a wireless programming cable to do this. You probably want to use derivative, as well.

You will want to cap the integral contribution to the equation. Otherwise the integral winds up and you can not recover from it soon enough. Capping between 20-30% in PID is generally useful. Capping in PI may be different. Try 40% and tune down form there.

Logging the contributions of P I and D to the motor values shows you a lot. You will see I will be greater than 128 pretty quickly.

One last thing, put in a wait statement as it is looping around as fast as possible right now. PWM signals don’t react below 20ms real well. So what you are doing is telling the motor to use this value, but it only changes once every so many milliseconds. The rest of the commands to the motor get dropped. You will want to increase Ki then as the loops will be much fewer and far between than what you have right now.

I have found that using a PD loop on drives works the best personally. I have tried tuning a lot of ways but the way I have found to work the best is to increase kp until it oscillates about 1 large over 1 medium back and then go pretty close in oscillation. After that I increase kd until the oscillation goes away(hot tip, usually kd is about 3x the amount of kp if you tune this way). I usually don’t implement integral on the drive simply because tuning with this method is so accurate that 1 or 2 encoder ticks don’t affect my result and integral wind up and stuff is annoying. You can do it in the way you described but I found this way to be more accurate.

One thing you can do, that is similar to what @Team80_Giraffes said, is

if((abs(errorLeft) + abs(errorRight)) < (value really close to the closest you can get with a P controller))
		integralLeft += errorLeft;
		integralRight += errorRight;

Instead of

		integralLeft += errorLeft;
		integralRight += errorRight;

I was thinking more of a max integral contribution

if(abs(kI*integralLeft) < SOME_I_MAX_VALUE)
		integralLeft += errorLeft;
		integralRight += errorRight;

SOME_I_MAX_VALUE should be 30-40% of the motor strength as you don’t want integral wind up where the I takes over the whole motor values. Log it and you can see.

If it is within a small range of the target, then let it go. You want to have some hold strength gradually build up as you are a smidge above/below the target value.

Thanks guys so much for the great ideas! I’m gonna try to get to it sometime today or tomorrow. I’ll let you know how it goes!

does this actually help for the robot to go straight as well?