5
$\begingroup$

I'm controlling the angular position of a pendulum using a DC motor with a worm gearbox. Mechanically, worm gears are impossible to backdrive.

Using a PID controller on a pendulum system with a regular DC motor (no worm gear), the integrator would help the motor find the appropriate constant power setting to overcome gravity so the pendulum can hold any arbitrary position. With the worm gear, however, there is no need to apply constant power to the motor once the desired position is achieved. Power to the motor can be cut off and the worm gear will resist gravity's force to backdrive the pendulum to the lowest gravity potential.

It seems to me, then, that the integrator of the PID algorithm will cause large overshoots once the desired position is achieved. I want the integrator initially to help control the pendulum to the desired position. But once the position is achieved, I'd need the integrator to turn off.

The only solution I can come up with is to test for a special condition in the PID algorithm that checks if the position has been reached AND the angular speed is small, then instantaneously reset the integrator to zero. Is there a better way to handle the integrator in a system that resists backdrive?

** EDIT *

When I originally worded my question, I was mostly just interested in the academic approach of backdrive resistance in a PID loop. But it'll help if I explain the actual mechanism I'm building. The device is a robotic arm that rotates on a car window motor. It will also occasionally pick up and drop small weights at the end of the arm. Manufacturing variability in motors and the difference in drive torque when picking up the small weights led to me consider a PID loop.

$\endgroup$
  • $\begingroup$ What is your feedback? What does your system look like exactly? Whether or not you can backdrive the system shouldn't matter, i.e. it won't affect your overshoot. What may matter is backlash if any (and that again does not related to back-driving). What you're talking about is more overdamped vs. underdamped. $\endgroup$ – Guy Sirton Jun 30 '14 at 6:14
  • $\begingroup$ I'm using a hall-effect rotary sensor for feedback. I also edited my question to describe what my system looks like. $\endgroup$ – Dan Laks Jul 1 '14 at 7:22
  • $\begingroup$ And yes, backlash is a problem I'm fighting. I'm hoping to design a sufficiently overdamped control system so that the pendulum doesn't overshoot due to slop in the gears when it slows down suddenly. $\endgroup$ – Dan Laks Jul 1 '14 at 7:28
3
$\begingroup$

This is a picture of my own PID-like regulator of the form:

out = feed + MaxMin(DiffMax, DiffMin, // limit the difference, add feed (can be removed)
  + P*(E=setp-feed)                   // proportional factor, error calculation
  + (A = MaxMin(AccuMax, AccuMin, A   // limit for accumulator
  + I*E - D*(feed-prev)))             // joined integrator and derivator

The big difference from regular PID regulators is in the way of using the accumulator for both I and D - which is a bit similar to that leaking integrator mentioned by ryan0270 (I was experimenting with similar thing as well). The derivator will decrease the accumulator (which can be limited as well to prevent windup and big value when set-point is greatly changed).

PID regulator with accumulated derivator

blue = output / control signal (force, input to boiler)
white = feedback (regulated value, actual temperature)
green/yellow = set-point (desired value)
red = state of accumulator (centered like being +50%)
navy/dark blue = proportional part

I have designed it for temperature regulation, but it seems to be equally good for your problem. My fade factor of the system is simulating the temperature dissipation / error of equitherm regulator. Your gravity and variable small weights seems similar to me. The integrator can compensate for this, but would normally overshoot and cause oscillation. Derivator applied on the shared accumulator prevents that - it reduces the accumulated error by the change made.

Proportional is strongest at the beginning (startup acceleration), Integrator comes next (error-correcting acceleration) and Derivator will slow it last (like a brake - preventing the overshoot by reducing the accumulator).

Regulator step:

//  regulator step
    float diff = setp - feed
    float accu = 0
    if pA && !isnan(diff)
    //  integrator
        accu = *pA + I*diff
    //  derivator
        if pV
            accu += D*(*pV - feed)
            *pV = feed
    //  accu limits
        if accu > aMax; accu = aMax
        if accu < aMin; accu = aMin
    //  store back
        *pA = accu
//  proportional
    diff = P*diff + accu
//  diff limits
    if diff > dMax; diff = dMax
    if diff < dMin; diff = dMin

Full code:

#include <math.h>
#include "mem/config.h"
#include "device/analog.h"

__packed struct PID
    byte    type
    char    name[9]
    word    period
    Timer   tmr
    byte    feed, setp
    word    flags
    ANALOG  output
    float   vals[9]

enum
    fP          = 1<<0
    fI          = 1<<1
    fD          = 1<<2
    fDiffMax    = 1<<4
    fDiffMin    = 1<<5
    fAccuMax    = 1<<6
    fAccuMin    = 1<<7

void devPid()
    PID *pid = (PID *)xpointer(DevAddr)
    if !pid->tmr.reached(pid->period*10u)
        return
    pid->tmr.start()

//  inputs
    float feed = 0.0f
    float setp = 0.0f
    if pid->feed < ainCount
        feed = getxf(ains[pid->feed] + AIN_value)
    if pid->setp < ainCount
        setp = getxf(ains[pid->setp] + AIN_value)

//  PID factors (multiplicators)
    float P = 0.0f
    float I = 0.0f
    float D = 0.0f
//  difference limits (output-feedback)
    float dMax = INFINITY
    float dMin = -INFINITY
//  accumulator limits (anti-windup)
    float aMax = INFINITY
    float aMin = -INFINITY

//  flags and pointers
    word flags = pid->flags
    float __packed* pf = pid->vals
    float __packed* pA = null   // accumulator
    float __packed* pV = null   // previous feed

//  parse variable object part
    if flags & fP
        P = *pf++
    if flags & fI
        I = *pf++
        pA = pf++
        if flags & fD
            D = *pf++
            pV = pf++
    if flags & fDiffMax
        dMin = -(dMax = *pf++)
        if flags & fDiffMin
            dMin = *pf++
    if flags & fAccuMax
        aMin = -(aMax = *pf++)
        if flags &fAccuMin
            aMin = *pf++

//  regulator step
    float diff = setp - feed
    float accu = 0
    if pA && !isnan(diff)
    //  integrator
        accu = *pA + I*diff
    //  derivator
        if pV
            accu += D*(*pV - feed)
            *pV = feed
    //  accu limits
        if accu > aMax; accu = aMax
        if accu < aMin; accu = aMin
    //  store back
        *pA = accu
//  proportional
    diff = P*diff + accu
//  diff limits
    if diff > dMax; diff = dMax
    if diff < dMin; diff = dMin

//  final output
    ainSet(DevAddr + offsetof(PID,output),
      feed + diff)

You can tune it a bit and use the error, accumulator and feedback-change to detect the steady state to power it off.


Add-on

I have been experimenting with it a bit more - used the regulator with erroneous non-linear 7bit DAC (custom-made of few resistors and opto-transistors) with erroneous main regulation (basic conversion peformed very bad in high values) and observed the behaviour (good ADC was used as feedback). The regulator performed really well and quickly ended with 1-bit oscillation (which is inevitable). I have further enhanced my simulation with this gained knowledge and here is the picture:

PID regulation with digital granularity

$\endgroup$
1
$\begingroup$

I would find something like a leaking integrator. The primary component will integrate error but there is also another component that will always slowly decay the integral value over time.

$\endgroup$
1
$\begingroup$

It sounds like you don't need a PID controller at all. You're trying to move a worm gear until it reaches a given set point, then turn it off -- proportional control sounds like all you'd need.

$\endgroup$
  • $\begingroup$ I edited my question above to clarify a bit. I'm mostly concerned about variability in motors (if I make more than one of these) and difference in required drive torque when it picks up the weights. $\endgroup$ – Dan Laks Jul 1 '14 at 7:30
  • $\begingroup$ In that case, a PI controller might be more what you need. The integral term should compensate for any extra torque that is needed. (I assume you are talking about torque when you mention variability in motors, is that correct?) $\endgroup$ – Ian Jul 1 '14 at 17:06
  • $\begingroup$ yes, that is correct. The integral still confuses me a little though. I imagine the integrator spooling up as the arm approaches the setpoint. As the position error approaches zero, the force component from P will approach zero. Typically then, the force component from I will help resist gravity and other constant forces to keep the arm at the setpoint. But none of those forces apply because of the nature of a worm gear. Since the spooled-up I won't be resisting anything, but it still has a non-zero value, won't it cause an overshoot jump? $\endgroup$ – Dan Laks Jul 2 '14 at 17:44
  • $\begingroup$ Maybe I'm still misunderstanding your question (or else you're trying to have it both ways). If more torque is required to raise added weight -- constant force -- then the I term is the only way to achieve that. Keep in mind that as you approach the setpoint, the P term is going to decrease while the I term remains the same; you should not overshoot the target in this case. I think ryan0270's answer might be the one you want. $\endgroup$ – Ian Jul 2 '14 at 23:37
0
$\begingroup$

Why not just have a deadband region? (when position is close enough to target, stop controlling the motor).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.