PID straightdrive

I’ve been reading around the forum that people are using a Proportional-Integral-Derivative (PID) control structure to better manage their program. I think I understand the basic concepts of each individual type of structure, but I have no idea how to cause them to work together or apply them to apply them to my programs.

Our robot has a problem where one side is always faster than the other. I seek to use Integrated Encoder Motors (IEM’s) one on each side where one is PID controlled such that the error is equal to the difference between the right and left encoder values.

Does anyone have a simple way to explain how this can be created in RobotC?

Start here, with George Guillard’s most awesome, practical, I-can-actually-do-this introduction to PID: http://georgegillard.com/documents/2-introduction-to-pid-controllers

1 Like

@biglesliep
From what I learned from George Guillard, would this be an adequate representation of a basic PID control?

#pragma config(I2C_Usage, I2C1, i2cSensors)
#pragma config(Sensor, I2C_1,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign )
#pragma config(Sensor, I2C_2,  ,               sensorQuadEncoderOnI2CPort,    , AutoAssign )
#pragma config(Motor,  port2,           lefty,         tmotorVex393_MC29, openLoop, encoderPort, I2C_1)
#pragma config(Motor,  port3,           righty,        tmotorVex393_MC29, openLoop, reversed, encoderPort, I2C_2)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

int pidtoggle;
int error;
int prevError;
int integral;
int power;
int setpoint;
int derivative;


task pid()
{
	while(true)
	{
		while(pidtoggle==1)
		{
			setpoint = nMotorEncoder[righty];
			error = setpoint-nMotorEncoder[lefty];
			integral = integral + error;
			if (error == 0)
			{
				integral = 0;
			}
			if (error <= -1)
			{
				integral = 0;
			}
			derivative = error-prevError;
			prevError = error;
			power = error*1 + integral*1 + derivative*1; /*These ones can be changed to better preformance*/
			wait1Msec(5);
		}
	}
}

task main()
{
	startTask(pid);
	while(true)
	{
		while(nMotorEncoder[righty]<=500)
		{
			pidtoggle = 1;
			motor[righty]=100;
			motor[lefty]=power;
			pidtoggle = 0;
		}
		motor[lefty]=0;
		motor[righty]=0;
	}
}

First and foremost, if you’re going to go down this road, make a p-controller only and get that working as well as you can. Get it to the point where changing Kp either up or down does not produce a better outcome in robot movement. That’s the signal that a p-controller has reached the limit of what it can do.

And, in many cases p-only is all that you need. Voila! You’re done. No more programming or tuning required.

ONLY if p-controller doesn’t get you close enough to what you want do will you then move on to the “I” section (or skip over “I” and go to “D” as George describes). Trying to determine all 3 Kp, Ki, and Kd values simultaneously is a recipe for frustration and wasting a lot of time.

Kp, Kd, and Ki are all going to differ a lot from the 1s you have in your sample code. On page 15 of Guillard’s guide, he has some starter values for the different constants.

My inclination is to calculate the error, and then set


power = power + error * Kp

which is a little different than what George describes.

Why do you turn off PID toggle each time through the loop, and then turn it right back on again? If you want it to be 0 after it’s done with that bit of driving, I think you could set it to 0 right after the while(…<=500) closing brace.

Thanks for the help. I’ll begin running some tests tomorrow right after school.

ps. the reason I stopped the PID and restarted it after each iteration of the while true loop is because it’s like midnight and I am what one might call “beyond tired”.

:slight_smile:

http://www.aura.org.nz/archives/1869

This article explains making sure the robot runs straight really well.

As a side note, I learned control loops through a combination of this, George Gillard’s PDF, Mr. Pearman’s flywheel velocity control, and Martin Ma’s PID loop tutorials.

Wow – thanks so much for the Aura link. They have a fantastic explanation. I will also take a look at the video tutorials, which I have also not seen previously.

Anyone try straight drive with a gyro?
I’m going to.

That could be a lot simpler than PID…

Not really if you plan to do it the “correct” way… It would just be a control loop maintaining the same gyro angle.

Yeah. It could help if you run into cones to maintain direction.
Also, I could use it for strafing, lol we have mecanums, to always strafe straight.

@SkinnyPanda Robotics I’ve heard bad things about the gyro sensor…my school’s senior team used it last year with little success. Apparently, it works differently in different temperatures and it needs to be manually calibrated every time.

This is how I calculate drift error (I got the original code from MartinMaVexForever, and I have modified it to make it work better)

driftError = Kdrift*(nMotorEncoder[leftMotor] - nMotorEncoder[rightMotor]); //drift correction code
//if robot is tilting left drift error is negative
//if robot is tilting right drift error is positive
if(driftError > 30)
{
driftError = 30;
}
if(driftError < -30)
{
driftError = -30;
}

finalPowerLeft = finalPower - driftError;
finalPowerRight = finalPower + driftError;
moveBase(finalPowerLeft, finalPowerRight);

The rest of my PID code is pretty normal. This part is placed after the line where I calculate finalPower.

Hmm motor encoders. In my experience, they are much more temperamental than gyros.

@SkinnyPanda Robotics - FYI, here’s a thread about using PID with a Gyro, from @jmmckinney:
QCC2 and Gyro PID code
– EDIT: see JMMcKinney’s post lower down for the correct link to his Gyro library –
And his associated Github library

Apart from having to replug the I2C port wire every time I turn on the robot, motor encoders have been working fine for me.

Sorry, didn’t read whole thread but got to ask why we wouldn’t use the Cortex’s PID

Seems it can be toggled on/off easy enough and even set tuning params in Motor Setup

The github repo is outdated, see here instead . There’s a really nice PID implementation, my motion planner code, and my gyro filtering code. Note that my gyro functions only return rate, I assume that the user will sum it over time themselves.

@jrobitai Most teams don’t use the built in PID from ROBOTC, EASYC etc… generally because it seems to perform worse than homespun implementations that do the same thing, or because teams want to implement it themselves as a learning exercise.

Some use cases also require finer control over how the control loop behaves. For example, I generally combine a slower position controller using PID with a faster feedforward velocity controller with a PID loop (with just an I gain) for velocity control, which gives really great results.

@jrobitai I’ve had a lot of issues with the built in PID. I’d turned it on at the beginning of the year to mess with it and forgot to turn it off, and then we had issues controlling our lift for months afterwards. Now that its off, everything works perfectly.

Just write your own. Depending on what you are doing, you can probably get away with a simple P-loop, which is what I have been using for controlling our main lift and our mogo lift. The code for them is simple and they are easy to tune. For my kP I’ve started with 1.0 and adjusted it up or down depending on whether I had oscillation, and that’s seemed to work for me, but if anyone can chime in with a better strategy I’d be glad to hear it.

There’s the added advantage that a DIY feedback loop will let you use the red quad encoders or the potentiometers instead of the IME’s.

FYI all - I’ve compiled a list of PID Forum posts and websites together into one article on my website (with links), in hopes that a centralized library will be of help to someone down the road: PID Resources.

If anyone has items to add to what’s listed (useful, practical stuff, not just all stuff), please send me a message or respond here.

1 Like