PID control loop/Encoder question

Hey guys, I’m a programmer for a rookie team. We’re going to try to use Digikey encoders to help with velocity control. I’ve seen a few websites that help, like:

http://www.barello.net/Papers/Motion_Control/index.htm

http://nrg.chaosnet.org/repository/viewcode?id=0

Once I’ve figured out the error between requested speed and actual speed, I don’t really understand what commands I would send to the motors to make the proper adjustments. Could anyone explain how that would work? Any help is appreciated.

If you use my code at the repository that you mentioned above, all that you have to do is call this function from your Default_Routine(). As in:


/* set actualLeft and actualRight to values from two encoders */
/* all other default routine code here */
pwm01=PID(left,actualLeft,&actualLeftLast,&sumLeft);
pwm02=PID(right,actualRight,&actualRightLast,&sumRight);

Then, place this in the global vars section:


int left=127, right=127;
int actualLeft,actualRight;
int actualRightLast=127,actualLeftLast=127;
int sumLeft=0,sumRight=0;

All code above assumes that left motors are pwm01, and right are pwm02.

I’m in the middle of writing an “Advanced Programming Guide” and when I’m done it’ll be at the NRG website (see my sig), under “Resources.” I’ll be sure to include details about PID and encoders.

well, the PID control loop code you saw on the NRG repository essentially accepts a requested (nominal) PWM value, and the actual PWM value (in your case, from the encoder) - the second two variables are just for storage (note, you could just declare these in the function as static variables instead of declaring them elsewhere and passing them as arguments). It then returns the value that the PWM should be set to. It’s essentially that simple - call it with those two values, and set the pwm variable to the return value of the function.

Typically the output of the PID loop is a signed number representing the power delivered to the motor. Unfortunately for IFI they don’t use a signed number: they use 0 through 127 for 100% to 0% reverse and 127 through 255 for 0% to 100% forward.

So, if you have a signed number (e.g. -127 to 0 to 127) just add 127 to that to get an IFI compatible number. The reverse can be done to convert Joystick readings into signed numbers.

Beware, you need to explicitely declare variables as signed with the Microchip C compiler. I recommend working with ‘int’ values to avoid overflow problems, then clipping to +/- 127 then converting for output to IFI controllers.

Figuring out the function to transform error into output power can be tricky. Simple proportional and integral terms are all you need, but, check the results. If the output is being clipped (e.g. you have hit the stops) stop accumulating integral errors!

Figuring out the constants is also tricky. It depends upon your loop rate (fixed in IFI stuff) encoder resolution, velocity, etc. The integral term usually has a fractional gain while the proportional one usually has a fairly small number (1-30) - but again, it all depends.

Be careful. Even with the new controller, you may run into processor speed limitations if you try to use encoders with a resolution that is too high.

The 2004 controller seems capable of handling at least 5000 counts/sec with two encoders. I am using PORTB interrupt lines (change of state interrupt) and two four stripe encoders (home made) on the shaft of the Bosch. At ~20krpm the encoders give about 5000 counts/sec.

I did some experiments with a super fine resolution encoder and at about 100k counts/sec the IFI system locked up :slight_smile:

Anyway, 5k/sec is about right. It gives ~150 counts/26ms loop at top speed.

Yes, but what else are you doing in the background?

What do you mean?

I use the change of state interrupt for my encoders and Timer2 to increment a counter at 1khz. That is all that is done in the background. Everything else is done in the user code. Mostly in the high speed loop, running my gyro integration stuff at 100hz and a menu interface task at 10hz. All direct robot control stuff occurs in the 26ms main loop.

Even at 10k interrupts/sec (two encoders going full tilt), that is 1000 machine instructions per interrupt. It only takes a couple dozen to service the interrupt and decode the inputs. Lets say 100 to be generous. That is only 10% load. That leaves 9mips for the rest of the system.

I think we’ve all just gotten so used to slow BASIC stuff that we’re unconsciously limiting what we think we can do with the new controller…

He means “What are you doing BESIDES counting ticks?”

Mr. Barello:
Have you successfully read quatrature encoders with the IFI?
The super high resolution with the wheel encoders is nice… but our team is having difficulties reading the second output pin on the wheel encoders. Do we not have access to these input pins? What would be the purpose of Process_Data_From_Local_IO() if it weren’t to make it so as we could read digital io real-time. Why no real-time input reads if interrupts? We only seem to be able to read the direction of the wheel encoder if we spin the encoder shaft really, *really * slowly.

Any suggestions?

The local io loop is very fast. I think I clocked it at a mere 29 cycles (doing nothing else, of course) which is many hundred thousand times per second.

HOWEVER, that isn’t guarenteed! Once every 26.2 ms the main loop fires off and who knows how long that will take.

So I ran my encoder stuff off the PORTB 4-7 interrupt lines (IFI digital inputs 2-5), enabled the port B change interrupt so it interrupts whever any line changes.

When choosing encoders & such, keep in mind that the IFI CPU can only handle around 5-8 thousand counts/sec each for two encoders. So figure out how many RPM, how many slots (multiply by four for quadrature) and do the math. If you are substantially higher than 10k/sec total, then re-think.

I posted my quadrature code (including interrupt setup) in the thread about Yaw sensors.

Our team has assumed that since we are controlling the direction of the motors, we would not need quadrature for directional counting. Are we missing something?

This answers my question completely. Thanks for clearing it up and thanks to everyone else for the responses.

You’re good, as long as whatever you’re moving won’t get moved by forces other than your motor. If you have to put up with gravity and robots hitting said object, then quadrature may be the way to go.

No, you can do it that way if all you are controlling is velocity. You do lose some amount of control, but for this application that seems like a very reasonable approach.

I would recommend using the quadrature. If you are not powering the motors and you are pushed, you will have no idea what direction you are moving.

Plus it’s really easy. It’s as simple as saying: When A goes high, if B is low you are going one direction, and if B is high you are going the other direction. If you have A hooked up to an interrupt (which you will), you can assume that A has just risen so you just have to read B.

If you are using feedback to control the speed of the drive motors (wheels) then you absolutely MUST know which direction the wheels are turning

feedback must have a direction - thats WHY you are using feedback, to see what your wheels are actually doing - you already know what you want them to do and what you are telling them to do.

If your bot is going slow, or has stopped, and your PID loop tells it to slow down a little more, and it overshoots and starts going backwards a little - then you speed magnitude will be increaseing, but you will be going backwards - so your PID loop will keep telling the motors to reverse harder and harder (since you thought you were going forwards) and the bot will go backwards faster and faster.

your feedback sensors must tell you the direction of rotation.

Okay - I am convinced quadrature is the way to go.
Thanks,