Any PID experts ?

In the ideal world, we would like to make use of the PID Class to move our (encoder sensored) tube lifter up and down 2 stage ladder as follows

  • move to a specified height accurately and stay there (and hopefully not burn a motor out trying to hold it).

  • move at a specified max speed (which may be less than the physical max speed) to that height slowing down as it approaches as opposed to a hard stop.

  • be able to handle the completely different dynamics of lifting upward against gravity versus coming down with gravity (without excess speed).
    Not quite certain how to avoid gravity / momentum based overshoot at the bottom leading to a major impact.

Is this possible within the current PID classes ? Any suggestions ?

use some D. it should ramp the speed down as you get closer to the setpoint

There are two pieces of code necessary to do everything you want: PID (probably more like PD) and a gain scheduler.

What P/PD/PID will do for you:
*If you jack up the gain, it will run at max output power until it gets close to its target §
*If you tune it correctly, it will slow down faster if it is moving faster (D)
*If your motor will not burn out at very slow speeds, it will keep going after P gets to the motor deadband (I) to get to an exact position

P and PD are usually used to do this. We will be using P and if we have issues we will add D. You can write them all, and set the gains of non-used terms to 0.

What a gain scheduler will do:
*Adjust the gains of the terms depending on machine dynamics, such as gravity (adjust gains based on sign of error) or other factors.

You could also add a “friction bias”, where you add a constant to the motor all the time. Then, what the PID algorithm defines as “0” actually is turned into a very low motor power to hold the mechanism in position.

If properly written and tune, it will work well.

(p.s. potentiometers are absolute and don’t need to know their home position)

My suggestion:
Write a P controller with a gain scheduler and see how well it works. If you have issues with overshoot at high speeds, you can add D and tune that.

Well, I can’t say anything about PID Classes, but I can explain the concepts of PID to you.

First, look up the paper “PID without a PHD”, read it over and over until you understand it. It helps to work examples of the kind of numbers you might see on paper.

Whether the motor burns out or not is unrelated to PID. That concerns motor thermal load. Some motors can be held at stall for a long time, some motors will smoke in a few seconds.

For a PID loop to work, you must sense the controlled parameter - height of your fork in this case. Figure out how you’re going to do that first.

Then just try a simple P loop, and use fixed values instead of variables for development.

For example: Make the fork go to 4 feet. Let’s say P_Factor = 50, and the max range of the fork is 0 to 8 feet (for this example, to prevent value over-runs), and (Motor_speed) is a value from 0 to 255 that you send to the Jaguar, where 0 is full reverse, 255 is full forward, and 127 is neutral.

(Delta)=(actual_height-4) 
(Motor_speed)=((Delta)*(P_Factor))+127

Work that example on paper (or in your head) with a few starting heights to see what happens.

Write back with more questions.

This is a very good place to use the PID controller, and the PID classes should be able to do that nicely.

You’ll want to start with P, add some D to help prevent overshoot, and then add some I to help it get to the correct height if it isn’t there from the P and D.

You will find that just passing your goal position to the PID controller will cause it to go to the goal by applying full power when traveling long distances. Sometimes this is the desired result, and as will probably be the case with our bot this year, this is not what we would like. To fix this, you want to pass a nice movement profile to the controller, and have it follow that path rather than feed it the raw goal. I like to have a final goal, and a local goal. I set the final goal to where we want the arm to go to, and then limit the velocity of the local goal by only changing it by up to some constant each time step. This smooths the motion out considerably. You can go one step further by limiting the acceleration of the goal as well, but that starts to complicate the math for little extra gain.

What language are you using?

From experience, I know C++ & Java have the built-in PIDController class to do the computation for you.

Although it’s always better to know how things work, the time constraints limit your debug time. The class is pretty easy to use, and its used in a few of the examples. Spend your time tuning the loop instead of debugging math.

Good luck!

Thanks for the great overview. You confirm alot of what I suspected.

We actually designed in both an encoder and a 10 turn pot to give us 2 different things to program against (one perhaps as a backup). We are really concerned about momentum/gravity based on a past bad experience.

The encoder was easy to mount (small). We were going to use either a limit switch or a initial starting position to give the encoder a known location.

The 10 turn pot needs a big sprocket and we are currently having trouble finding unimpeded space to mount it (but we will).

We were wondering if there are any MATERIAL accuracy differences between the 2 approachs re a P or PD loop. The encoder is 250 ticks per turn say 15 turns = 3750 over say 100" = 0.03" per tick resolution. The Burns 10 turn pot accuracy/linearity is standard (forget what but probably 5%). The AI system I sort of remember is 10 bit (1024 values) over say 100" = 0.1" on a perfect day, probably better to plane for 0.25 ?

If you have LabVIEW, I put together a P demo.
You can input a few things:
Gain: This is the tuning parameter
Motor to Movement Scale: How much movement is caused by 1 unit of motor movement (This is for the output, if you had a real robot this would be determined by the gearing and motor load)
Time per Cycle: How fast to run the loop. This will show you what kind of affect loop time has (try tuning it then changing it, it won’t work as well).

You can play with Commanded Height and see how Actual Height and Motor respond. There is no sensor feedback, the sensor height is assumed to be what the height was last time (using a shift register).

Maybe if I get some time later, I will add I and D.

Accuracy:
The 10-turn pot will be fine (we have a 12-bit ADC for reference).
You should not expect accuracy greater than 2", less depending on how fast it moves.

P_Demo.vi (8.96 KB)


P_Demo.vi (8.96 KB)

The encoder is overkill, but might be easier.

While 0.1 or 0.25 inches is a theoretical value, count on more than that in reality, like an inch or 2, otherwise your loop will spend a LOT of time trying to get that last 0.1" just right.

just because you CAN program to 0.1" doesn’t mean you should. Or need to.

Dave,

We need to talk =).

Are you using motors that will backdrive?

How fast are you raising this thing, and how much momentum will there be, and thus how likely is it to overshoot your target?

How many positions do you need to move this thing to?

Without knowing the details, I’ll put money on doing two things:

  1. using the 10-turn pot (encoders + a homing limit switch are a pain)
  2. finding a way to mechanically preload your elevator so that it takes roughly the same effort to raise or lower

If you can do 1 and 2, then the built-in WPILib PID classes should get you what you are looking for. You can even use last year’s mecanum code for reference, although you’ll need to modify the input from an encoder to a pot.

I’m no PID expert but I whipped up a virtual PID example in LabVIEW that I think works pretty well. It adds a 5% margin of error to the motor speeds on either side to better simulate imperfect mechanical things and show how PID beautifully overcomes these things. It’s not perfect but this should be a good tool if you want to play with PIDs on the computer to get the hang of them. Oh some good numbers to play with your first time are P= 1, I= 0.01, and D=0.

PID learning test.vi (25.1 KB)


PID learning test.vi (25.1 KB)

Thanks everyone

  • We currently are using an anti-backdrive motor but may not be in the competition. Without the anti-backdrive, the motor may have to be powered somewhat to hold position and we are wondering if we could overheat it.

  • yes, good idea, if we could design something where the effects of gravity can be neutralized (gigantic spring), I could see how the current PID class could work well but so far we don’t know how to do it. So currently with the same power, going up goes slowly and controllable, going down races at high speed and gathers lots of momentum. This behaviour is a bottle neck to a super high performance (ie high speed) ladder.

Instead of a spring, could you put some kind of viscous damper* on it? Like those things they put on screen doors to keep them from flying out of your hand in a high wind? Yes it would slow down your “up” speed a little, but it would slow down your “down” speed a lot.

*caveat: check the rules to make sure it’s legal.

**

You could use a gain scheduler.
Basically, you check something (your scheduling variable) and determine which gain to use.

In your case, the sign of the error. If it’s positive (up?), set the gain to x, which you would tune in the up direction only, if it’s negative (down), set the gain to y, which you would tune in the down direction only. You would probably set the up gain higher than the down gain, probably significantly higher if you don’t have much spring force.

so this doesnt exactly answer your question but here are some good things to know about PID loops for those who are unfamiliar:

PID is really P + I + D = V, in which P is your proportional component, I is your integral component, D is your derivative component, and V is the velocity you output to the motors

Note that in code, V doesnt have to be something in m/s or mi/h, when you output say .7 to the motors, that will turn the motor at a constant speed (for all intensive purposes of this application)

Proportional component: P = A * (error)
Error = intended position - current position
where A is a constant that you have to choose. when people say they are “tuning” a PID loop, they are basically adjusting the K constant until the system stays at the position you want it to. notice that you need to have some sort of sensor to tell you your current position

Integral Component: I = B * ( .5 * (error_at_time1 + error_at_time2) * amount of time between the two measurements)

where B is another constant. so basically what i have written up here is a discrete way to calculate an integral called the trapezoidal rule, there’s other methods, but this one should work. notice you have to know the time at each measurement, there is a time keeping function built in to the CRIO, it’ll just take a little manual searching to find it

Derivative Component: D = C * (error_at_time2 - error_at_time1) / amount of time between the two measurements

where C is again another constant. note that i’m simply finding the slop between two measurements, which is the discrete approximation of the derivative. make sure in both the derivative and integral components that you use two measurements that are as close to each other as possible

Now to start off with, i like to set A, B, and C so that if my motor is as far away from the target, it will output a 1 (full throttle at least in my year) to the motors. then, you change those numbers through experimentation to find constants that work best

here is a nice little table in wikipedia that tells you how to change your constants based on what your problem is

http://en.wikipedia.org/wiki/PID_controller gives a nice table that tells you how to change each constant based on what kind of error you have

For most applications in FRC, you should only need the P component, which will save you a ton of time and trouble.

(PS: you dont need a graduate degree to understand PID loops, you just need to know how to solve high order linear diff eqs and the appropriate discrete approximations of integrals and derivatives based on your application :slight_smile: )

Thanks for all the input. We tried a number of combinations but gravity combined with the realities of a no-backturn gearbox (which leads to significant jumping going downwards as the backturn pins engage and disengage as the motor is turned on or off or reverses direction, we went back to KISS (dropped the PID code) and currently just go full speed up to within 5% of the target and power off. The no-backturn motor keeps us there. We did something similar going downwards but I believe are not going full power down. So far so good …

I suspect a generic PID algorthim is aimed at systems that has largely symetrical responses on either side of the target but we were highly assymmetrical (especially with the anti-backlash) making a single set of tuning parameters hard pressed to do a good job in both directions

That’s where the “gain scheduler” Palardy mentioned comes into play. It changes the tuning parameters based on measured conditions. In your case, the sign of the error would be used to determine which set of parameters (the “going up” or “going down” numbers) to employ.

This is where some feed-forward control comes in.

Feed-forward is a motor command in addition to the feedback motor command from the PID. Mathematically, it is:

MotorCommand = Perror + IerrorIntegral + D*derivative + feedForward

The feed forward command depends on the physics of the system, and can be as simple or as complicated as needed. For an elevator, it should be a fairly simple constant value used to fight gravity. Since F = m * g, and m and g are constant, F should be constant. Therefore, your motor command to fight gravity should be constant.

So, how do you find this constant value to fight gravity? In the case of the elevator, it’s pretty easy: 1) set up a PID controller (the I-term is necessary for this), 2) command your set point to some arbitrary value, 3) wait until your system settles at the set point, 4) read the motor command that is keeping it still. Voila, you have your feed-forward constant.

What feed forward does for you is this:

Let’s say you have the issue you’re seeing: the elevator goes slowly up, but races down. In reality, to let it go down, you may not really want to command the motor in the down direction - you may only want to relax the upward command. The feed-forward takes care of this. In the elevator example, let’s say you set your feed-forward constant at 0.10 PWM duty cycle in the up direction. If you command the elevator to go up 10 cm, the PID may command the motor up 0.07 and with the feed forward addition the total motor command would be 0.17. If you commanded the elevator DOWN 10 cm, the PID would command the motor the same 0.07 but in the down direction (with gravity it would go racing downward), but when you add the feed forward term, the motor would be commanded UP 0.03. Since it requires 0.10 to hold it steady, 0.03 up would result in gentle downward movement of the elevator.

I hope that makes some sense, it got a little long-winded.

For an arm, the feed forward term should be a function of the angle of the arm. For example, if 0 degrees represents the arm straight up, then the feed forward term should be C * sin(armAngle). If you do a little math to determine the torque at the motor as a function of arm angle, you’ll see why C*sin(armAngle) makes sense.

In summary, if you can develop some simple physics equations of the system, you can come up with a feed-forward method that makes sense. The idea is that the feed-forward motor command should be what the physics say the motor command should be in a perfect world. The PID is then there to take up the slop and make up for errors and imperfections.

Not to toot my own horn, but I wrote this a while ago:
http://www.chiefdelphi.com/media/papers/1823

It’s aimed at the old control system, so the code examples don’t apply directly (the new controller doesn’t have the same limitations in regard to math complexity) but the concepts are still valid. I’m in the process of rewriting this to be a bit cleaner with the new control system as the basis for the examples, but it’ll be pretty similar to my original paper.

As stated in the paper, please feel free to contact me directly via email, PM or anything like that if you have any questions at all.

A fair point, I wasn’t very clear about how the D term works in this paper, I really didn’t touch well on the fact that it’s usually a negative coefficient to help counteract overshoot by regulating speed, but on some systems, I’ve found a positive value works out well, depending on exactly what response you need, and if overshoot is acceptable, etc. I will be making this much clearer in the next version of the paper.

I really hope that helps, and again let me know if you have any specific questions,
Matt