Jon Elson <elson@...> wrote (in several messages, I'm
replying to all of them here):
>> I just spent well over an hour writing a detailed reply to
>> Jon's scope picture post, and my damned webmail client lost it
>> (session timed out).
>
> Damn! That is extremely annoying. Umm, why don't you use
> something like mozilla? I generally never lose anything unless
> the lights go out!
The operative word here is "webmail". That lets me do mail from
anywhere - home, work, windoze, linux, someone elses computer,
a hotel computer, etc. All I need is a browser.
The session times out 6 hours after you log in. If that happens
while composing a message, and then you hit send, it barfs and
even after you log in again the message is lost. Last night I
logged in about 7 pm, and when I tried to send the message around
1am it had timed out. That sucks, but its the price I pay for
the flexibility of being able to mail from anywhere, so I live
with it. (Actually its a bug, the mailer should notice the
timeout, prompt me to log back in again, and then either continue
to send my message, or return me to the message editing page
with my text intact. Nothing I can do about that though, its
my ISP.)
>> The code is part of your CVS checkout, or
>> http://cvs.linuxcnc.org/cgi-bin/cvsweb.cgi/emc2/src/hal/components/pid.c?rev=1.21
>
> I looked at it a long time ago, and said "uh huh, that looks about
> right." Now, I'm looking over it again, and I find some things that
> don't look quite right.
>
> First, the deadband code ADDS the deadband to the error, so the error
> jumps from zero (within deadband) to error PLUS deadband when it
> exceeds the deadband. I certainly don't see the necessity of doing
> this. It seems the error should either be forced to zero when within
> the deadband region or allowed to pass unchanged when outside the
> deadband region. This essentially quadruples the size of the deadband
> region.
Nope. Look again:
/* apply the deadband */
if (tmp1 > pid->deadband) {
tmp1 -= pid->deadband;
} else if (tmp1 < -pid->deadband) {
tmp1 += pid->deadband;
} else {
tmp1 = 0;
}
tmp1 is the error. If it is positive and greater than the deadband,
(first if) the deadband is _subtracted_ from it. If it less then
-deadband (second if), then the deadband is added to it. In both
cases, that moves the signal closer to zero, not away from zero.
Finally, if neither if is true, the error must be within the
deadband, so the result is zero.
The reason I don't allow the error to pass unchanged outside the
deadband region is to avoid a discontinuity at the edge of the
region.
> I had a long argument with Will Shackleford over the wisdom of
> having the integrator integrate over all time since the big bang
> in a rapidly changing environment. It might be fine for an oven
> controller, where the external effect is always heat loss, but
> not a 4-quadrant motion control. But, yes, making a ring buffer
> so you can remove old error terms after they have been allowed
> to contribute for N servo cycles will increase the complexity
> of the code.
A ring buffer is only one way to do it, and a very unconventional
way at that (there is no analog equivalent to that, for instance).
A simpler way would be a "leaky integrator". The analog equivalent
is a resistor across the integrator cap. I took yet another
approach to preventing integrator windup - I don't integrate when
the PID output is in limit.
The debate over when to integrate and when not to is eternal,
and certainly won't be settled here. One of the bigggest strengths
of HAL is that if you don't like the PID implementation, it is
very simple to write your own and substitute it by changing only
a few lines in the .hal file.
> I don't know where period comes from, ISTR it is in units of ns.
Yes.
> That seems to jibe with the calcs for periodfp and the reciprocal.
> So, for a 1 KHz servo cycle, period would be set to 1 million, and
> periodfp would be 1/1000. The reciprocal would be 1000. Is this
> all correct?
Exactly - period is 1/1000 because that is the servo period in
seconds. HAL always uses natural engineering units like seconds
and inches (or mm), NOT artificial ones like encoder counts.
(I should say, HAL lets _you_ use natural engineering units, if
you want to use counts you can. But I strongly encourage setting
the scaling parameters so the internal signals are in engineering
units.)
> And, is it consistent with the limits set for the integrator and
> differentiators? (I haven't figured out where these limits, like
> maxerror_d, are set. A comment says if the limits are zero, there
> is no limit.
The limits are HAL parameters, and you can set them any way you
like, either directly in the .hal file, or indirectly by using an
ini file reference in the .hal file and a variable in the ini file.
From the source:
The PID gains, limits, and other 'tunable' features of the
loop are implemented as parameters. These are as follows:
Pgain Proportional gain
Igain Integral gain
Dgain Derivative gain
bias Constant offset on output
FF0 Zeroth order Feedforward gain
FF1 First order Feedforward gain
deadband Amount of error that will be ignored
maxerror Limit on error
maxerrorI Limit on error integrator
maxerrorD Limit on error differentiator
maxcmdD Limit on command differentiator
maxoutput Limit on output value
> Especially for the "integrates forever" integrator, a limit
> seems a good idea. (Maybe this is where the cyclical grumbling
> comes from.)
Unlikely. The grumbling at standstill is some kind of small
signal behavior. A limit would have no effect on that, since
it would have to be set high enough to allow full speed under
normal conditions.
> With the reciprocal of the period being 1000, the D term
> could easily dominate unless the D factor was set really small.
On the Mazak, P and I gains are several thousand, and D gain
is fifteen. D gain is very handy for damping, but you can't use
too much because it amplifies the high frequency quantization
errors from encoder steps. You can probably use more than most
folks because you have such a high encoder resolution.
> I notice you got rid of FF2 (acceleration feed-forward) which
> I made use of on EMC1 to great advantage. FF0 is position
> feedforward, essentially a bias that changes in proportion to
> position. I suppose if you were compensating for a crummy
> counter-weight spring this would be useful, but otherwise I
> think FF2 would be vastly more useful.
I agree that FF0 is very unlikely to be usefull. Why do you have it
set to 4.0 in your config?
I find FF1 to be most usefull when driving a velocity mode amp (or
a voltage mode amp, which is very similar). FF2 would certainly be
usefull for a current/torque mode amp, and is on the to-do list.
But it needs to be used with care, because double differentiation
really amplifies quantization noise. On page 48 of
http://www.linuxcnc.org/Hal_Introduction.pdf is a drawing of the PID
loop, which shows FF2, but a footnote on page 49 indicates that it
isn't implemented yet. Note that the drawing also shows the old
form of the deadband, with discontinuities on each side of the
deadband. That has been changed. (Note: that document has been
re-arranged, on the latest version, included in the 2.0.0 release,
the same info is on pages 75 and 76.)
>> What config are you using? If its one of the sample configs,
>> please let us know everything that you changed (obviously things
>> like input scale, but everything else too, including ALL the gains
>> that were in effect when you took that picture. Did you have FF
>> gains set? What about output scaling?
>
> Output scale doesn't seem to do anything, there's a PWM_SCALE
> parameter that seems to be the operative one.
Yes, thats it.
> I'm using the univpwm config suite, and the important params
> are here :
Thanks. I snipped the ones that don't really matter...
> [TRAJ]
> DEFAULT_VELOCITY = 0.0167
> MAX_VELOCITY = 1.20
> DEFAULT_ACCELERATION = 1.5
> MAX_ACCELERATION = 1.5
Max accel in the axis section is 20. Are you running the TRAJ
at low accel for testing purposes? Normally the two would be
much closer, if not the same.
> [AXIS_0]
> MAX_VELOCITY = 1.20
> MAX_ACCELERATION = 20.0
> PID_MAX_VEL = 1.25
> BACKLASH = 0.000
> CYCLE_TIME = 0.001000
> INPUT_SCALE = -128000.0 0
> OUTPUT_SCALE = 1.000 0.000
> PWM_OUTPUT_SCALE = -1.0
> DEADBAND = 5e-05
> P = 1200.0
> I = 0.0
> D = 70.0
> BIAS = 0.0
> FF0 = 4.0
> FF1 = 0.8
The FF0 is almost certainly messing things up. Dgain seems
a little high, but perhaps with your high encoder resolution
it is OK. I've found it impossible to get good results without
using Igain, Pgain alone usually results in instability. (I
understand you turned Igain off as a test.)
PWM_OUTPUT_SCALE should probably not be 1.0. Both PID_MAX_VEL
and PWM_OUTPUT_SCALE should be equal to the velocity (inches
per second) that you would get at 100% PWM duty cycle. In
hindsight, for the PWM config it might make sense to get rid
of one of those two variables and use the other in both places.
(A simple change to the .hal file, but we can worry about that
at the workshop.)
If PWM_OUTPUT_SCALE is set right, the output from the PID
loop to the drive is a velocity command in inches per second.
Therefore the correct value for FF1 is 1.0 by definition, since
a commanded input that is changing at X inches per second should
result in a velocity command to the amp of X inches per second.
Then the other gains simply need to handle the errors due to
the fact that the amp is open-loop.
Regardless of the proper value of FF1, I do initial tuning
with all the FF terms set to 0, since it makes it easier to
understand what the rest of the system is doing.
>> once I get my hands on it I bet I can tune it in a couple
>> hours.
> Hours? Yikes! I usually can tune with EMC1 in about 10
> minutes! But, then I started doing this with an analog
> NON-STORAGE scope!
I'd rather say 2 hours and be done in ten minutes than say
10 minutes and be done in two hours. It seems like _nothing_
ever takes only 10 minutes. Just getting everything connected
and verifying the polarities are right might take that long.
I see you've already done that, but I was referring to the
complete commissioning process for several axis, not just to
"tuning".
I start by verifying the feedback by turning the encoders
manually and watching things change, make sure the feedback
scaling is right (magnitude and polarity). Then I check the
DACs (PWM in this case) with the amps disabled. Then enable
the amps and do a open loop move to set the output scaling
and verify polarity. Then finally, I close the loops, set
some initial gains, and iterate until I get gains I like.
The iterating part is trial and error, and you are probably
faster at that than I am because you have more experience.
I got the Mazak to do 300 ipm and 20 inches/sec^2 accels
(zero to 300 ipm in 1/4 second) so I must be doing something
right. (Actually at one point it was doing 400 ipm and
45 in/sec^2, but we backed it off because there was no
reason to push the iron that hard.
If you want to do more tuning before the workshop, set
the PWM_OUTPUT_SCALE to 1.25, set FF0 and FF1 to 0.0,
and try adding some Igain. I bet you can work both
Pgain and Igain up (together, too much of either one
alone causes trouble) into the range of several thousand.
That should get you pretty good performance. Observe
the PID output, and add Dgain as needed for damping,
or until the quantization noise gets too bad. You don't
want the PID output jumping by 20% of full scale every
time the encoder ticks over a single count.
Regards,
John Kasunich
|