Concept of PID explained

I have seen tons of references to it, some more advanced theory, which just confused me a bit more. Leaving me still confused as to how exactly PID works, and how it is implemented. I know what it does.

From researching around online, I have found some still advanced stuff, but nothing to really explain it that well and I want to make sure that I have the elements right before I learn it the wrong way and have to learn again later.

Ok on our imaginary robot, we will call each of the loops when the PID loop is called 1 “tick” when the motors go full speed forward (254) for 1 tick, we get 127 up counts, and full reverse(0), we get 127 down counts.

Now for the PID stuff.

The P, sees that if the desired speed is 200, which means there should have been 73 ticks (200-127) however there was only 47 ticks, so the motor’s power would be changed to 200 + (73-47)*kP. Is this right?

I’ve had calculus so I understand the concept of integration, but for the I of the PID loop it would be something like this:
(S) would be the current desired speed, -127 so that zero is neutral
(S-x) would be the desired speed, x loops ago
(A) would be the current actual speed
(A-x) would be the actual speed x loops ago

How the I would be implemented would be:

Motor value from P + (( (S)+(S-1)+(S-2)…+(S-x) )-( (A)+(A-1)+(A-2)…+(A-x) )) * kI

It would be keeping track of the difference in motor speed versus desired speed for the past x loops, and use that to affect the motor output also. Is this how the I part works?

As for the D component, I am still a little more confused than the others.
From what I understand, you take the difference from the previous loop, subtract the difference in this loop, to see if the error is decreasing or increasing. Multiply this by kD and subtract it from the motor value from the I section.

Is that how PID works for drive motors? I know that it is different for things that have position controlled rather than velocity.

When I read the usual posts about it it seems rather hard to understand, but that doesn’t seem so hard looking back over what I have up there.

I’ve never done a PID system, but from what I understand all it does is: The closer you are to the point you want to be, the speed slows proportionately. If I’m not mistaken, you can do the same with a y=mx+b kind of formula. I’ve e been tinkering with this idea lately, and figured it would be a good alternative to a complex PID system. Where
y is the speed it’s going to be set to
m is 127 divided by the distance you are starting at
x is the current distance away
b is 127

Speed = (127/StartingDistance)*Distance + 127

So say we want to travel until a pot reads 100, slowing down proportionally until we hit the destination (where it would set speed to 127)

Case
Pot is currently at 100, needs to go to 1
Current speed is 127
Speed = (127/100)*100+127

The speed decrease would proportionally go down, as shown by this chart.
(Note: This chart starts at 127, and goes up to 255. It’s the first time I’ve used Excel to generate charts.)
http://img168.echo.cx/img168/2677/graph9xp.gif

The chart was generated using the attached data sheet (.xls format)

Good luck,
Mike

APID.xls (21 KB)


APID.xls (21 KB)

The components of a PID control system are P (proportional), I (integral) and D (differential), and it is not always the case that all of the components are present in a given control system.

To understand PID control systems, and how to implement them, it is perhaps useful to consider the physical system upon which the PID control system is based. Suppose we have an object of mass M that we want to control the position of. Let the position of the object be X, and the desired position of the object be C. The position of the object, X, and the desired position of the object, C, might be indicated by the voltage reading on potentiometers attached to the object, and a control lever.

The P in PID is for a control force that is proportional to the difference between the desired position of the object, C, and the position of the object, X. If C is greater than X, the force is set up to increase X. In our robot one way to apply the control force is to set the pulse width (% on time) of a motor that is applying a force to the object. This produces, approximately, an average force that is proportional to the pulse width.

What do we have at this point? We have an object of mass M, with a force being applied to it that is proportional to C - X. Those who have taken basic physics will recognize this as the force that a spring produces, giving us the motion of a mass on a spring. If the force is perfect, and there is no damping, the mass will oscillate about the desired position, C, for quite a long time…

This brings us to the D (differential) in PID, which can also be usefully referred to as “damping.” Referring back to our mass on a spring that is oscillating forever because of the perfection of our control system, if we suspend the mass in molasses we find that the oscillation rapidly decays and, neglecting gravity, that it nicely settles into position C.

So, we have to figure out what is going on with the molasses… The mass moving in the molasses produces a frictional force that opposes its motion, and to first order produces a force that is proportional to the velocity of the mass. The effect of this dissapative force is to slow the mass down, so that it eventually comes to rest.

To implement this force we need the velocity of the mass. Lacking a direct sensor for this we can simply remember the position of the mass on the prior trip through the loop in robot control program and subtract that from the current value. This gives us a measure of the velocity and we can then suitably adjust the force being applied to the mass. If we do it perfectly, and the world is not perfect, we will get damped motion. I like to call it electric molasses. It is only the velocity of the mass, itself, that matters. If you remember (C - X) from the past loop and then subtract it from the new (C - X), C - C gets you zero so you can do it either way you would like.

Finally, we have the I (integral) in PID. Suppose our mass and spring are suspended vertically, so that the force of gravity is pulling on the mass a bit. With gravity pulling on the mass, the spring is stretched a bit to counteract the force of gravity, and as a result the mass is not pulled into the correct position by the spring. Because of the force of gravity there is a persistent error in the positon of the mass.

Now, in every control system that Team 1280 has built for a FIRST robot, such an error has always been made up by the human operator who just moves the control stick just a little more; but this is where the I in PID comes in. If there is a small error, and we integrate the error in time, the integral eventually becomes big. If we apply a force to the mass that opposes the inegrated error, the mass will eventually go to the desired control point, regardless of which way the influence that is causing the error is coming from. It this case you are integrating the error, C - X.

The world is not perfect. Timing errors in the application of the forces involved in a PID system can lead to an unstable control system where the oscillations grow instead of damping. I’ll refrain from discussing these, but will refer you to how a child pumps up the motion of a swing. It is all in the timing… Oscillation problems usually arise when the “gain” is too high for the timing errors that exist in your control system. Additionally, the “dead zone” of your victor controller (the range of values for which no motor output occurs) must be dealt with if you want fine control out of a PID control system on a robot.

Now, your question had to do with controlling the speed of your robot, instead of the position of perhaps an articulating arm. This is not something that we have done, in that students using joysticks to control motor power has been sufficient for us (with some massaging with regard to the dead zone in the victor), but the principle is the same as for position.

You need a velocity sensor, and if you do not have one that directly supplies a velocity reading to the robot you could use one that reads position and subtract a past value in order to get the velocity. A force that that is proportional to the error vis-a-vis the desired speed will cause the robot to accelerate. In this case you don’t want your “damping” force to be proportional to velocity, you want it proportional to the measured acceleration, if you use a D feedback signal. There are really huge frictional losses that require substantial motor power in order to make up for. This is the moral equivalent of the influence of gravity in the discussion above and is likely severe enough that you might want to be using the integral, I, control term.

Additionally, you can get quite an abrupt stop if you run the motors in the reverse direction of the current motor travel. It may be the case that you want to make sure that your control system is coded to avoid this type of violent behavior. In fact, if you desire to set the speed of the robot with a joystick you could be better off constructing a simple static map of motor power as a function of joystick position that tends to produce the desired speed result, and then use integral feed back to produce a correction term in response to any error in the speed, but limit the correction term in a way that avoids any power reversals in the motor vis-a-vis the direction of travel.

When debugging a PID control system, the most frequent error is a sign error in force being applied to the thing being moved. If the sign is wrong the control system goes completly out of control, so to speak. For things like an arm, it can lead to damaged equipment. For the motors driving a robot it can lead to a 130 pound robot racing across the floor completely out of control. It pays to put your robot up on blocks, or disconnect the drive to an arm, checking that the feedback is working correctly before you risk the equipment, or arm and legs.

If you control the appendages on your robot with PID systems, you can then use a state machine to have them do interesting things during the autonomous period. That is where the robot programmers really get to show off!

Have fun,
Eugene

A nice tutorial for PID control can be found at:
http://www.engin.umich.edu/group/ctm/PID/PID.html#ol
In particular, the “cruise control” or “motor speed control” examples located at the bottom of the web page may be of interest.

This is my Rookie year with FIRST, and let me tell you, I love it!
Coding software has never been one of the things that I have had to do in my professional life, but it is something I have come to realize is very important to do and/or understand to some extent if we are going to have our robots work in ways beyond the default code is set up to do. So, with that in mind, I have spent a couple month this year, after our local regional, beginning to learn. “So”, you might ask, “what does this have to do with PID systems?”

One of the first things I want to do with our future bots is to use position control to operate and arm or axis instead of just using the joystick to adjust speed and then stop when the driver felt, or saw, that they were in the correct position. The coding I came up with to do this is quite simple, remember, this was my first attempt to write anything. It follows some of the basic PID architecture described in previous posts. I find that actually seeing the code can sometimes help others to learn. Besides, I know many of you will have input as to why it will or will not work and, I’m hoping, ways to improve it. So here goes:

unsigned int pospot1;
unsigned char PosFdbk1;
char PosError3;
char DrvCmd3;

/****************************************************************************************************

  • This section compares the value of the feed-back/Position pot attached to RC Analog input 1 *

  • and drives pwm03 until the value of the feedback pot matches the value of the Port 3 Y axis. *

  • to within a value of ± 5, which is narrower than the DeadBand of the Victor it’s self. *

  • This routine will also limit the Max Forward and Reverse Speeds to that of the Victor Max/Min *

  • values. Lastly, it maintains a minimum speed as the position error approaches 0, which will *

  • also allow small movements more easily. Additional "Else If"s can bee added to modify mid speeds.*
    ****************************************************************************************************/

    pospot1 = Get_Analog_Value(rc_ana_in01) ;/* read in the analog value of the position pot*/
    PosFdbk1 = (unsigned char)(pospot1 >> 2); /* assign an 8 bit value to the variable PosFdbk1 /
    PosError3 = (p3_y - PosFdbk1);/
    create the uncorrected drive value */

if (PosError3 > 127) /Limits drive to Max Forward Speed of Victor and Motor/
{
PosError3 = 127; /* Lower this value to reduce max forward speed /
}
else if (PosError3 < -127) /Limits drive to Max reverse peed of Victor and Motor/
{
PosError3 = -127; /
Increase this value to reduce max reverse speed /
}
else if (PosError3 > 5 && PosError3 < 20) /
Small forward movement window /
{
PosError3 = 20; /
Change this number to modify Min Froward speed /
}
else if (PosError3 < -5 && PosError3 > -20) /
Small reverse movement window /
{
PosError3 = -20; /
Change this number to modify Min reverse speed /
}
else if (PosError3 > 21 && PosError3 < 40) /
medium forward movement window /
{
PosError3 = 40; /
Change this number to modify Medium Froward speed /
}
else if (PosError3 < -21 && PosError3 > -40) /
medium reverse movement window /
{
PosError3 = -40; /
Change this number to modify Medium reverse speed /
}
DrvCmd3 = (127 + PosError3);/
Apply the corrected drive value to neutral /
pwm03 = DrvCmd3;/
drive the motor */

The middle section of this code limits the drive command to 0-254. It also adds some speed corrections in the low speed range to maintain a certain minimum speed until the axis is quite close to the actual value desired. I did this because the Victors have approximately a ± 10 deadband window that may be a little larger than desired for positioning a system. The values of the intermediate steps can be modified to accommodate various masses and friction variables encountered with differing system designs etc.

Please feel free to comment on, correct, improve or otherwise suggest anything you would like. I make no claim to correctness or accuracy. Again, this is my first attempt. I suspect it will be improved upon quite a bit in the future.

Your computed values for a PID system are best declared as int to avoid problems with overflow when assigning variables. For instance, in the assignment:
PosError3 = (p3_y - PosFdbk1);
a value larger than 127 can produce a negative result when stored in PosError3, providing for undesired behavior of the control system.

The same goes for DriveCmd3, in the assignment
DrvCmd3 = (127 + PosError3);
in general, although this instance might not be producing undesired results.

Your code implements a P control system, providing for two discrete
signals for smaller values of the error and an upper bound for large values of the error. It is suitable for a system that has a lot of friction, for instance something driven by a screw. The mechanical friction provides any required damping in this case. Our robot, this year, had this kind of drive system for the arm, and as a result we used a P control system with a one stage minimum for small errors. The Fisher Price motors were prone to smoke and it was better to have them “jump into the window,” so to speak, and then be cleanly shut off. If they sat stalled they could burn up, and we did smoke one of the 6 volt motors this way.

You shift the value returned by Get_Analog_Value down by two bits, losing two bits of precision in the deal. This loss of precision can be a problem when implementing the arithmetic in a full PID control system, especially when doing subtraction involved in the D and I signals. It is better practice to shift the 8 bit values returned from the OI up by two bits, although for your specific code it is of no concern.

If your system does not have enough mechanical friction to damp oscillation, you will need to implement at least the D control signal. You can also scale and apply limits to the P, I and D signals imdependently before adding them. This allows you to easily tune each signal, without affecting the others. You will know if this is an issue when you try your code out on a given mechanical system. If you can’t get rid of oscillation you will need to add a D signal, back down on the gain of the P signal, or both.

It is best to take care of the dead band of a given victor, and the conversion to an 8 bit unsigned value for the pwm controller, somewhat separately from the PID code after the full blown PID signal has been developed. The dead band of a victor can change when swapping a part, or if “recalibration” should occur (students can do that sort of thing). You can easily measure the minimum and maximum pwm signals that do not activate the motor, and update them in this section of the code without changing the PID control code above it.

Ultimately, every system is different, and the things that you need to do in order to control it can be, as a result, different. There is no limit to the phase space for being creative.

Have fun,
Eugene

Eugene,
Thanks for the input. I truly appreciate any I can get! :slight_smile: Your description of the code and how it is affected by mass and friction match exactly my thoughts and intent for this code. I understand that the adjustments thrown in for compensation of the Victors deadband characteristics are fairly generic and will not be accurate for all Victors, let alone one that has been “experimented” on by a curious student. (But, I gotta’ say, I love their willingness to try something without really knowing the result. I just wish they would let me know before they try it on the competition robot :ahh: )
Maybe in the next re-write I can move it outside this code and place it in a separate function.

As for the declaration of the two variables as “char” instead of “int”:
The maximum range of PosError3 is -254 to 254. This is limited by they following code to -127 to 127 before it is used to create DrvCmd3. At the point DrvCmd3 is created, it can only take on a values of 0 to 254. (Again, I am new to C so here is where I may be mistaken) Both of these ranges of values can be represented by a “char” as well as a “int”. I chose the “char” just to save a little memory space. I guess as sloppy as this code is, that really doesn’t matter.

Either way, I have seen lots of coding examples where “int” is used as more of a standard, even though a “char” would work. Oh well, I’m still learning, and your input really helps.
Thanks again!!! :slight_smile:

The range of PosError3, declared as a char, is -128 to 127.
The range of the expression assigned to it is larger. When you
assign 254, for instance, and read the value back from PosError3,
you will get -2, not the value intended. Looking at your code,
it looks like the result would be a pwm signal in the dead band,
instead of full forward. Assigning 128 will be read as -128,
and in this case your code will cause the motor to go full speed
in the wrong direction.

In the case of DrvCmd3, you have the same problem, but you are
saved by the fact that the computer takes the misinterpreted 8 bits
and blindly shoves them into the unsigned char, pwm03, so the result is
the one you expect.

As I noted, the issue a visible bug for PosError3, but does not manifest
itself as a bug for DrvCmd3. If you, or someone else, were to modify
the code to somehow test the value of DrvCmd3 and manipulate it
before assigning it to pwm03, the additional bug would surface.

A table of widths, and range, of integer types on the robot controller
can be found in “An introduction to C programming for FIRST robotics
applications.” See “C programming for the robot controller”
at the web address
http://srvhsrobotics.org/index.php?goto=getarticle&cat=2
for the latest version.

I hope this is helpful…

Eugene

Thanks, this helped out a lot, I have a feeling that even though I am graduating this year, I’m going to be doing the code for our team next year. No one to really pass on the knowlege to. Hopefully we gan get someone that can at least solder good, and make up sensors for the bot. We didn’t use any this year. Not even any limit switches. Though it I had it my way, it would have been otherwise.

After reading through the Integer Types and their Properties section of the paper you linked to and re-reading the IFI Programming Reference Guide and the MPLAB Compiler Users Guide, I see my error :mad: . With out a doubt, “int” should be used for both DrvCmd3 and PosError3.

Curiosity question here.
I noted significant differences between your paper and the IFI documents when it came to variable types and their ranges, primarily “int”. Again I’m pleading ignorance here as I’m so new to coding. Why are they different?

Again, Thanks for your help! It would not have been fun trying to figure out where we/I had gone wrong with the code with out your input.

There are two tables in the paper. Table 1 is for EiC, the freely available
C interpreter used in the paper as a learning tool. Int is a 32 bit object
in EiC, with the associated signed and unsigned range. Table 2 is for
the C18 compiler used for the robot controller, where int is a 16 bit
type, with the associated signed and unsigned range. The text discusses
the issue. I hope that Table 2 is consistent with the data in IFI documents,
and the MPLAB C18 compiler documentation, it has been checked in
the past.

If you note a difference, please identify the document and page number.

Sorry for the confusion. I did not look beyond Table 1. I assumed, and there lies the problem, that the first table I came across covered what I was looking for. :o

Either way, you were correct about the “int” declaration, it would have worked regardless which table I looked at.

Thanks again.

is there a way to apply this concept onto easy C? Sorry but it’s our rookie year and we have no clue what we’re in for, but basically we have gone for a design of 3 wheels, 2 being powered by a motor and running on an axle, and 1 in the front being steered by another motor, but the problem that arises is that after steering the motor to the left what I want to do is when the joystick is back at the centre position I would like the wheel to also move back into the centre position. I’ve been doing alot of research on PID closed loops, but all I’ve been getting is the theory behind it and I have no clue how to apply the programming for it. We went with easyC and I’m wondering if it works with it. Thanks in advance!