Potentiometer PID

I have searched and read everything I could find on PID loops. I am still very confused. How would I create use a potentiometer with a PID loop. It will be used so that all the driver has to do is hit a button so that our arm will go to a certain height. Any help is appreciated.

Well the POT in an analog port will return 0 - 1024.

The code should explain the rest.
You probaly want to add a statement to make sure the number going into
the victor is between 0 and 255.


{
      if ( Button1 == 1 )
      {
            Target = 200 ;
      }
      else if ( Button2 == 1 )
      {
            Target = 400 ;
      }
      else if ( Button3 == 1 )
      {
            Target = 600 ;
      }
      Arm_Out = GetAnalogInput ( 1 ) ;  //GetAnalogInput(Port) Checks the Port and Return 0-1024
      Error = (Current - Target)  // Computes Error Based on How Far you are from the goal
      Drive = ((Gain * Error)+127)  // Based on the gain it outputs a number and then add 127 so the Victor Understands
      SetPWM ( 1 , Drive ) ;  //  SetPWM(Port,Speed)
}


What about this paper? http://www.chiefdelphi.com/media/papers/1823?

It explains the theory and then shows an example using a camera to track the green light, but that example is very similar to using a potentiometer to hold a mechanism in place. You are simply finding the difference between the desired and the actual values and multiplying the difference by a gain, the summation of the difference by a gain, and the change in the difference by a gain.

In reading about PID loops you probably noticed that the math is based on an error value that is the difference between an actual value and a desired value. You’re talking about using a potentiometer, so I’m guessing you are trying to set an arm or extension position.

The pot connected to an analog input returns a value that represents the current position of the arm. You probably have some desired position that you are trying to move to. If you subtract those two values, you’ll have a number that is positive for correcting in one direction and negative for correcting in the other direction. The magnitude of that value is how far away you are from the desired position.

That’s the value you use for the PID code. If you were just using proportional control, then the speed that you drive the arm would be proportional to the error (with some scaling to get it to match your motor, gearing, etc). So the more the error, the faster the motor corrects the position. When the motor is sent a value of 127, it’s off. So taking:

motor speed = error * Kp + 127 (where Kp is the proportional gain or scale factor) would drive the motor in the right direction (assuming the sign is right) and at a speed that is proportional to the distance to the desired position.

That’s the basics of how to make it work.

You can not create a potentiometer with a PID loop any more than you can create a pressure sensor or yaw rate sensor with a PID loop.

A PID loop is short hand for a particular type of closed loop control system that uses information about actual position and the desired position to determine the input to system (in this case a motor that drives your arm).

The idea of PID control is that you calculate an error (Desired Position - Actual Position) then you calculate two additional values, the rate of change of the error (the derivative of the error) and the added sum of all the errors over time (the integral of the error).

In PID control, you multiply each of these three numbers by 3 constants and, hey presto, out pops the input to the motor.

There is some magic (i.e. math) involved in making sure your system in stable and optimal (in some sense to be defined) but that is the basic idea. The hardest part is implementing it in C code and picking your constants.

The whole business depends on you system having a method of measuring position. That is where your potentiometer comes in. You have to somehow design your arm such that it rotates the pot as it moves. It is better if you can make the pot move linearly with respect to the arm movement but even some non-linearity can be worked around (at a minimum you can always linearize the pot values in C code).

There is a good Chiefdelphi.com thread on this subject called PID without Ph.D. Check it out.

Good luck.

Joe J.
that

Thanks for the whitepaper
I think I understand fairly well, would this work:

int Target;
int Error;
int Gain = 0.5;
int Drive;
int Arm_Out;

  if ( p1_sw_trig == 1 )
  {
        Target = 600 ;
  }
  else if ( p2_sw_trig == 1 )
  {
        Target = 1000 ;
  }
  else if ( p3_sw_trig == 1 )
  {
        Target = 1600 ;
  }
  Arm_Out = Get_ADC_Result(2);    //GetAnalogInput(Port) Checks the Port and Return 0-1024
  Error = (Arm_Out - Target);     // Computes Error Based on How Far you are from the goal
  Drive = ((Gain * Error)+127);   // Based on the gain it outputs a number and then add 127 so the Victor Understands
  pwm01 = Drive;

That should work extreamly well. :slight_smile:

The analog inputs on the RC vary from 0 to 1024.

A target of 1600 would cause the arm motor to continue to run indefinately.

[EDIT]

This depends on what line you have defined in Kevin’s code.

[/EDIT]

Depends on whether you’re using the standard routines, or Kevin Watson’s ADC package which integrates the readings for possible higher precision. The use of Get_ADC_Result() suggests that that code uses Kevin’s version.

Team 95’s ADC values ranges from 0 to 4095 last year…

Correct me if I am wrong, but that is NOT a PID loop. All that is going on there is a proportional control based on distance of the arm from the target. That code does not account for the integral of error or the rate of change of error, making it a P loop, not a PID loop.

So that will control the arm, but it is not PID control.

Correct. Although the way PID loops are generally created is by adjusting the P portion until it almost works and then adding the I portion until it is almost there and then adding the D portion to make it work better. A lot of the time, depending of what you are doing and how much precision you need, you’ll find that the P control may be all that you need.

AND you may find the I and D are fraught with peril when implemented in a relatively low resolution discrete CPU. There is integral windup to deal with. Taking derivatives will amplifying noise - perhaps causing bad behavior. If that wasn’t bad enough then there is the whole sampling delay causing instability issue.

Let’s also not forget extra the care and feeding a PID loop needs that a P loop does not (for example if you don’t turn off the integrator when your robot disabled it is very likely to do some violent action as soon as it is enabled*).

Bottom line for me: if you can get acceptable performance using a P loop, use a P loop. If not, add an I and finally if needed add a D.

Oh yeah, one more thing, if you are using a feedback loop on a clearly non-linear system (like a robot arm), you can really help yourself by adding in a term that corrects for KNOWN non-linear disturbances. For example, gravity. It is quite easy to know what effect that gravity has on your arm given that you know the position of the arm joints. You can simply calculate the moment due to gravity as mgLcos(theta). Once you have this term it is pretty easy calculate an added term that you output to your motors that will add the right about a torque to the motor to just cancel the gravity torque. In this way, your feedback loop only has to deal with unknown disturbances (like more fiction or a tube being on your arm, etc.)

On a similar note, you will have much better control of your arm if you can use counterbalance (e.g. lots of latex tubing) to cancel gravity without your feedback loop having to do anything. In general, anything you do to make your arm easier to control for your drivers in “open loop” mode will help your feedback loop (PID or otherwise).

Finally, a higher gear ratio is another huge help to making feedback loops more controllable – this helps for many reasons, too many to go into here, but as a rule of thumb I try to design arms to have a gear ratio such that my worst case expected load on the motor is about 1/5 to 1/4 of the stall torque of the motor (worst case does not include another robot hanging my arm but my worst case geometry with my heaviest payload)

Good luck.

Joe J.

*if you have a PID control you are living on the edge if you don’t have routines in your C code called “JustEnabled” and “justDisabled” You should have those routines anyway but you REALLY need them with PID control. JJ

Thanks for all of the replies. I actually used the example in the PID Control Theory Whitepaper. I used KP with a value of 1/10 and it works great. Thanks for all of your replies

Well this particular code will not work because there is one small error. “Gain” is an integer. When you assign the value 0.5 to it(a double) it will chop off the decimal places and just assign 0 to it. This means the Drive pwm will always be 127:

(Gain * Error)+127
is really
(0 * Error) + 127

hi there! which potentiometer are u guys using? who is the manufacturer and where did you buy it?

thanks! best of luck…

Any 100K Pot will work for most arms a single turn will work. Digikey.com and Mouser.com have parts for 100K 10 turn but you loose some resolution using a 10 turn. I haven’t found any 2 turn 100K pots.

We had some awful results 2 years ago with 100k pots on the robot. Last year we used 5k pots on the robot and had great success. I attibute the difference to noise and a close match to the input impedance of the ADC circuit.

Am I crazy?

Don

instead of


      if ( Button1 == 1 )
      {
            Target = 200 ;
      }
      else if ( Button2 == 1 )
      {
            Target = 400 ;
      }
      else if ( Button3 == 1 )
      {
            Target = 600 ;
      }

i would do this


      if ( (Button1 * Button2) + (Button2 * Button3) + (Button3 * Button1) + ( Button1 + Button2 + Button3 ) == 1 )   //prevents crazy values if more than one button is pressed and keeps the same value if no button is pressed
      {
            Target = (Button1 * 200) + (Button2 * 400) + (Button3 * 600)
      }

this just makes more sense to me and I just post it here for any like minded people out there. I think it takes a little less cpu time too.

You aren’t crazy. Those ADC inputs on the RC are rated for not much more than 2.5K. You should use a 1 or 2K pot when connecting to the RC. For the OI you’ll need a 100K pot.

Bourns makes the “good stuff.” Avoid radioshack’s potentiometers :).

It makes sense if you’re averse to using procedural programming and prefer functional programming. The logic behind your code gives the same result as the one using if statements, but the reason for doing it that way is obscure to anyone encountering it cold.

And it takes a lot more time to do it your way. A few comparisons and branches are faster than many multiplications and additions. Note that the original code never does more than three comparisons and one assignment. Yours always does six multiplications, thirteen additions, and one comparison. It would be instructive to compile both alternatives and compare the generated assembly language code in the listing file.

As an aside, wouldn’t it work just as well to simplify the condition to this?


if ( ( Button1 + Button2 + Button3 ) == 1 )