RESOLVED: EMC is poorly designed and implemented.



RESOLVED: EMC is poorly designed and implemented.

  I'm an old gray haired assembly hacker and cycle counter with some
experience in closed-loop motion control.  I have been investigating
EMC, principally emcmot.c and have come to the conclusion that EMC is
poorly designed and implemented.  I don't mean to be discourteous,
but you do not have a good solid product, nor will you without
a major redesign.  I will support this conclusion in the following.

  What I see in the implementation is wasteful, redundant access to
I/O ports, lack of insight into floating point speed considerations,
poor factorization into subroutines and far too many scattered ifdefs
for reliable code.

  Modern motion control should be adaptive, using Kalman filters, and
state-space or observer predictor methods.  EMC by design uses PIDs,
which are from old 1930s analog computers.  PIDs tuned via Ziegler
Nichols methods are inherently prone to oscillation and instability.
(The zeros don't cover poles, or drift into right hand plane, etc
as the dynamics of the plant change with temperature and load.)

  A 8-bit 6502 or 8051 running at 1 MHz could do a nice job of running
stepper motors in 1978.  Without double precision, 32-bit integers
or 800MHz clock rates.  Servos were harder, but still possible.  Today
yoy can go to http://www.jrkerr.com/ and buy a PIC-STEP or PIC-SERVO
with software for a reasonable price with software that'll get you up
and running twenty times faster than EMC.

Look at the source code (30-Jan-2002 sourceforge emcmot.c), lines 2422ff
the polling of the max and min limit switches, home and axis fault bits.
This is within the for loop on line 2373

L2373    for (axis = 0; axis < EMCMOT_MAX_AXIS; axis++) {

  This is very poorly done, the exact same bits are read repeated,
(for extsmmot.c) with over one microsecond per pptDioRead inportb
call.  That's (3 axes)*(4 switches)=12 microseconds, with 16 us
listed as the max.  Only one 1 us read is required.  The situation
is worse in stgmod.c, where 4 bytes are read, so it takes 48 us.
Folks - i/o port access always takes over a microsecond, regardless
of the speed of your i86 processors.  Reading the same bits
repeatedly in critical code is foolish and wasteful.

  I believe the choice to poll and debounce these switches within
the motion loop (rather than, say, using a hardware interrupt or
a slower emcio task read and debounce), is a very unwise design
decision.  Furthermore, American and European safety standards
REQUIRE that hard limit switches bring the machine down even if
the processor is non-functional.  Meaning the limit switches, through
a dumb relay, should drop main power to the motors.  So on commercial
machines, you don't need to check the limit switches, you'll E-stop
if you hit them.  As implemented here, EMC is likely to be HAZARDEOUS
to the health of its users.

  And there's this over-dependence upon double precision numbers
when single or integers will suffice.  For example, in the external
interface to stgmod.c, passing the raw encoders and dacs as double arrays,
when they're integers on all hardware, doubles the subroutine call time,
and then the subroutine itself has to convert it to an integer.

  Floating point is very fast these days, but it still takes time.
Even on Pentiums, double precision floating point is about half as fast
as single or integer arithmetic.  Divides take about twice as long as
multiples.  A subroutine call takes about as long as an integer add
for each 32-bit parameter.  These are ballpark figures, much affected
by caching, out-of-order and speculative execution.

So code like on emcmot.c line 2397,
  L2397  if (fabs(emcmotStatus->input[axis] - emcmotDebug->oldInput[axis]) /
  L2398      emcmotConfig->servoCycleTime > emcmotDebug->bigVel[axis]) {
Would be 50% faster if expressed as
  M2397  if (fabs(emcmotStatus->input[axis] - emcmotDebug->oldInput[axis])
  M2398      > emcmotConfig->servoCycleTime * emcmotDebug->bigVel[axis]) {
But both servoCycleTime and bigVel are infrequently changed - on a
explicit command, dump bigVel for bigLeap=bigVel*cycleTime and have
  N2397  if (fabs(emcmotStatus->input[axis] - emcmotDebug->oldInput[axis])
  N2398      > emcmotDebug->bigLeap[axis]) {
which by my tests on 233 and 800MHz machines, would take about a third
as long.  I see the "debounce bad feedback" has been changed, it indicates
a very serious hardware error which still is not logged or reported.

You may consider this the ranting of a dinosaur or as constructive
critism - as you choose.

God Bless America, President Bush and our fighting men and women.
--krb




Date Index | Thread Index | Back to archive index | Back to Mailing List Page

Problems or questions? Contact