Shop OBEX P1 Docs P2 Docs Learn Events
PID for motion control — Parallax Forums

PID for motion control

MicksterMickster Posts: 2,694
edited 2012-10-26 15:00 in Propeller 1
Just looking at the various PID routines in the OBEX and I wonder if there is any reason why a purely integer solution is not feasible?

I'm probably missing something but at first glance, I don't see why not.

Mickster.

Comments

  • Heater.Heater. Posts: 21,230
    edited 2012-10-21 18:08
    I'm sure it's quite easy. Use fixed point arithmetic, as long as you know what kind of number ranges you are working with and what accuracy you need you can generally arrange for things to fit in 32 bits. 16 bits for integer part and 16 for the fraction might be good start. Be sure to check for overflows in all your arithmetic ops just to be sure.
    People generally use floating point because they have not thought about what they really need and they hope floating point will cover all eventualities.
  • SRLMSRLM Posts: 5,045
    edited 2012-10-21 18:48
    Heater. wrote: »
    I'm sure it's quite easy. Use fixed point arithmetic, as long as you know what kind of number ranges you are working with and what accuracy you need you can generally arrange for things to fit in 32 bits. 16 bits for integer part and 16 for the fraction might be good start. Be sure to check for overflows in all your arithmetic ops just to be sure.
    People generally use floating point because they have not thought about what they really need and they hope floating point will cover all eventualities.

    Being an oddity here... But I used floating point for my PID routines so that it would work with the floating point batch processor that I wrote. Otherwise, my code would have a delay while the Spin overseer did the math: floating point turned out to be faster. Again, probably not the common case.
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2012-10-21 19:33
    I think it also depends on the application. For example a Standard DC motor with a digital tachometer. You could setup a closed loop with nothing more than a 555 timer setup as a missing pulse detector and get surprisingly accurate speed control with the motor. Add a CPU to the equation and real precision can be achieved with only integer math. Still essentially a missing pulse detector, but you can track the number of pulses and have better control for ramping up or ramping down to get to the desired position.
  • prof_brainoprof_braino Posts: 4,313
    edited 2012-10-21 19:48
    How big and how small are the values you're dealing with? If the number of values can be determined, usually they can be scaled to fit integer math. Whether there's enough integers is the next question :)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-21 20:38
    Mickster wrote: »
    I wonder if there is any reason why a purely integer solution is not feasible?

    I've used an interger PID algorithm. I was a pain to keep the numbers from overflowing. Instead of a single multiplier for P, I and D, I used a "numerator" and "denominator" portion for each multiplier. The error would would be multiplied by the numerator and then divided by denominator portion of the "multiplier". IIRC, some of the multipliers needed to be less than one which is why I used the factional multiplier. I never ended up using a full PID integer solution (but I did try it) since PI worked well enough. Usually proportional control is enough for the projects I've done.

    Here are these "multipliers" I used in one of my programs.
    _DefaultKPNumerator = 150 '120 'S60 '120             '¡User settable!
      _DefaultKPDenominator = 100          '¡User settable!
     
      _DefaultKINumerator = 4 '5             '¡User settable!
      _DefaultKIDenominator = 100          '¡User settable!
      '_DefaultKDNumerator = 0 '3             '¡User settable! not currently used
      '_DefaultKDDenominator = 100          '¡User settable!   not currently used
    

    Here's the PID section.
    PUB Pid | localIndex, maxOut
       
      repeat localIndex from 0 to _TotalServos - 1
        'currentPosition[localIndex] := Adc.AverageSingle(localIndex, _PowerOf2ToAve)
        currentPosition[localIndex] := Adc.average(localIndex, _SamplesToAverage)
       
        error[localIndex] := targetPosition[localIndex] - currentPosition[localIndex]
        if ||error[localIndex] > deadBandOutput[localIndex]
           
          proportional[localIndex] := (kPNumerator * error[localIndex]) / kPDenominator
          integral[localIndex] := integral[localIndex] + ((kINumerator * error[localIndex]) / kIDenominator)
          output[localIndex] := proportional[localIndex] + integral[localIndex] '+ derivative[localIndex]
        else
          output[localIndex] := 0
        'previousError[localIndex] := error[localIndex]
        DriveSingle(localIndex, output[localIndex])  
       
    
    

    I was never really satisfied with this attempt. I'm sure there are better ways of implimenting an integer PID algorithm.

    I didn't have a "dt" variable since each loop took the same about of time (a delay was added if needed).

    I'm not sure if the above code was the one that worked the best or not. I tried several variations.
  • ManAtWorkManAtWork Posts: 2,176
    edited 2012-10-22 00:09
    Mickster wrote: »
    .... I wonder if there is any reason why a purely integer solution is not feasible?

    I've written PID routines for my brushless servo controller. I've used a "custom format" floating point math, 8 bits for the mantissa and 8 bit exponent. This way I can use the same mul/add routine for all coefficients although P, I and D coefficients have a different order of magnitude, which would be difficult to handle with fixed point math. 8x24 bit multiplication is also much faster than 32x32 bit multiplication on the propeller.

    Only the coefficients are floting point, in and output is fixed (constant exponent for in, one shift operation for out). As the coefficients don't change very often this saves time for conversions.
  • MicksterMickster Posts: 2,694
    edited 2012-10-22 08:15
    I have created a "discretized", trapezoidal motion profiler based on acceleration (quad counts/sec/sec), slew velocity (quad counts/sec), deceleration (quad counts/sec/sec) and final position. My update rate is ~1ms (1/1024, actually) and the output is to feed the PID in the same cog.
    In actual fact, my intention is to run one "real" and several virtual motion profiles in the same cog along with the PID for the "real" axis.

    Some rough numbers, are 1000 quad counts/mm and I can, based on my performance criteria and under hard acceleration expect up to 10mm lag (aka: following error). Beyond this, my process is deemed out-of-control and I will shut the system down. This is only 10,000 counts of error so it seems to me that I have plenty of room to "SHL" for the calcs and later "SHR" when it comes to writing to the DAC(?)

    Have I over-simplified the problem?

    Thanks for the input, guys.

    Mickster
  • Martin_HMartin_H Posts: 4,051
    edited 2012-10-22 10:15
    SRLM wrote: »
    Being an oddity here... But I used floating point for my PID routines so that it would work with the floating point batch processor that I wrote. Otherwise, my code would have a delay while the Spin overseer did the math: floating point turned out to be faster. Again, probably not the common case.

    When I wrote my Spin inverse kinematics I planned to use fixed point arithmetic for the trig functions, but I found F32 was faster and used floating point instead.
    Duane Degn wrote: »
    I've used an integer PID algorithm. I was a pain to keep the numbers from overflowing. Instead of a single multiplier for P, I and D, I used a "numerator" and "denominator" portion for each multiplier. The error would be multiplied by the numerator and then divided by denominator portion of the "multiplier". IIRC, some of the multipliers needed to be less than one which is why I used the factional multiplier.

    This is where PBasic’s */ and ** operators are really handy. They do the multiply and divide at the same time with a 32 bit intermediate quantity. If Spin had similar operators with a 64 bit intermediate, that would really help the fixed point arithmetic cause.

    UPDATE: I checked the Propeller manual and Spin does contain the ** operator..
  • MicksterMickster Posts: 2,694
    edited 2012-10-23 02:55
    Martin_H wrote: »
    When I wrote my Spin inverse kinematics ....

    Very interesting. Did you publish details regarding this? Was this for an articulated arm? How many axes?

    Regards,

    Mickster
  • JasonDorieJasonDorie Posts: 1,930
    edited 2012-10-26 15:00
    My quadrotor code uses integer PID routines. I do the equivalent of a fixed multiply up, then a variable scale down, using the ** operator. Since the ** operator keeps the high 32 bits of a 32 x 32 bit multiply, it's the equivalent of multiplying by N / 2^32.

    The actual PID code line looks like this:

    Output := (Kp ** PError) + (Kd ** DError) + (Ki ** IError)

    The source of the input value PError is just accumulated with more precision than necessary, and the K factors are chosen such that they scale the result back down into the appropriate range for the control output.
Sign In or Register to comment.