DPRG
DPRG List  



[DPRG] RE: When to PID?

Subject: [DPRG] RE: When to PID?
From: David M Wilson davidmw at tx.rr.com
Date: Sat Mar 17 15:03:00 CDT 2007

David Anderson,

Sorry for the delay, wanted to get some code and data in this response.

Motors:  Lego 43362  340 RPM at 9VDC
http://www.philohome.com/motors/motorcomp.htm 

This controller (Xport 2 / GBA) provides about 7V PWM so this cuts speed to
around 225 - 250 RPM.  Direct drive, no additional gearing.  The effort here
is to control wheel RPM.

I'm not hoping PID will replace torque but am looking for a control
technique that will attempt requested speeds using all the available power
(assuming we don't have a motor stall condition).  When I request 200 RPM
but the device settles at 150 RPM using 90% of available power there is an
opportunity to get more out of the system.  Driving harder might seem
careless but it decreases the chances of a stall on carpet and pushes the
motors into a better place on the torque curve.  Operate at 100% and simply
trim the power or brake a wheel to make course corrections.  Regardless of
the power settings in software the motors are  operated well below the rated
voltage.

Remember that this is an axle pivot differential steering system - good
control of relative wheel speeds will improve turning.

The following code is interrupt triggered.  I've measured the control
frequency two ways.  A counter will increment 396 - 399 times over two
seconds.  And the time delta between executions is 5100 microseconds, +/-
25.  Basically 196 Hz.  I might never have measured it if not for responding
to your questions.  This is a 16.78 Mhz ARM running binaries and no OS - too
bad I can't convert some of the clock cycles to torque.  

Activating the camera, blob processing, and live video display will
certainly impact the stability of this timing.  Just eyeballing it the
fluctuations seem to be hitting +/- 300 microseconds per iteration.  I can't
complain about this performance but I'm leaving the camera and radio off
while tuning PID at this stage.

Since the motors are geared, the encoder (BEMF) resolution is roughly 7000
ticks per revolution when rotated by hand.  Motor conditions vary so this is
a calibration item.  I should test these with an optical counter to see how
much the resolution drifts at speed.

This all suggests that instantaneous RPM measurements (over 5100
microseconds) will be pretty good but in the code below I'm experimenting
with a longer window.  It computes RPM each cycle by comparing current time
and encoder counts with data gathered 30 iterations ago - spanning about .15
seconds.

void RoverBase::Periodic() {  
 
    // GET CURRENT WHEEL RPM and error. 
    for (ucAxis = 0; ucAxis < 4; ucAxis++) {   

        // Keep recent time & position measurements in a circular array
        // so that we have a sliding window of history on which to base our
RPM calculations

        lNextHistoryLoopIndex = Wheels[ucAxis].lHistoryLoopIndex + 1;
        if (lNextHistoryLoopIndex >= HISTORYLOOPSIZE) lNextHistoryLoopIndex
= 0;

        // NextHistoryLoopIndex now points to the oldest data we have and
the target location
        // for the new data storage

        // get current data
        timer.GetCount(&timeTemp);
        axisTemp = CAxesOpen::GetPosition(ucAxis);       
        
        //  RPM = (msec per sec * 60 sec) / timelapsed * ticks traveled /
7000 ticks per rev
        Wheels[ucAxis].lRealRPM = (60000000L /  (long) (timeTemp -
Wheels[ucAxis].timeLoop[lNextHistoryLoopIndex]) * (long) (axisTemp -
Wheels[ucAxis].axisPosLoop[lNextHistoryLoopIndex]) /
CurrentRoverConfig.lWheelTickPerRev[ucAxis] ) ;

        // ErrorLast should be the error saved at the beginning of the RPM
history loop
        Wheels[ucAxis].lRPMErrorLast =
Wheels[ucAxis].lRPMErrorLoop[lNextHistoryLoopIndex];
        Wheels[ucAxis].lRPMErrorNow = Wheels[ucAxis].lRequestedRPM -
Wheels[ucAxis].lRealRPM;
	  Wheels[ucAxis].lRPMErrorIntegral += Wheels[ucAxis].lRPMErrorNow;

        // save current data in history loops
        Wheels[ucAxis].timeLoop[lNextHistoryLoopIndex] = timeTemp;
        Wheels[ucAxis].axisPosLoop[lNextHistoryLoopIndex] = axisTemp;
        Wheels[ucAxis].lRPMErrorLoop[lNextHistoryLoopIndex] =
Wheels[ucAxis].lRPMErrorNow;
        Wheels[ucAxis].lHistoryLoopIndex = lNextHistoryLoopIndex;
    }      

...

    for (ucAxis=0; ucAxis < 4; ucAxis++) {
    
       Wheels[ucAxis].lSetPWM = Wheels[ucAxis].RPM_pGain *
Wheels[ucAxis].lRPMErrorNow/100L;
       Wheels[ucAxis].lSetPWM += Wheels[ucAxis].RPM_dGain *
(Wheels[ucAxis].lRPMErrorNow - Wheels[ucAxis].lRPMErrorLast)/100L;  
       Wheels[ucAxis].lSetPWM += Wheels[ucAxis].RPM_iGain *
Wheels[ucAxis].lRPMErrorIntegral/1000L; 
                    
       if (Wheels[ucAxis].lSetPWM > 255L) Wheels[ucAxis].lSetPWM = 255L;
       if (Wheels[ucAxis].lSetPWM < -255L) Wheels[ucAxis].lSetPWM= -255L;

       SetPWM(ucAxis, Wheels[ucAxis].lSetPWM);

   }
}



More information about the DPRG mailing list

Copyright © 1984 - 2006 Dallas Personal Robotics Group. All rights reserved.
Website Design by NCC

For the latest robot news visit robots.net