Motorized knee" on the mill works great now!

It took a lot of work, but everything is great now.

First, tuning was complicated because everything is "different". The knee has to move over 1,000 lbs of metal, using a high friction mechanism, and a "springy" transmission.

Finally, I realized that I need to greatly increase P and I terms and that took the following error way down -- it is always well under

0.001".

I also reduced max acceleration big time, as I realized that I was stressing the machine comlpetely unnecessarily.

The next issue was that in steady state, when not really moving it, the servo motor was heating up from residual current. This is because the PID loop in EMC2 was obsessively trying to move the motor that "last hair", against friction, not really strongly enough to actually move it.

I am sure that, if not fixed, it would fry the little motor one day. It was completely unnecessary, since the ACME screw is self locking.

Andy Pugh on EMC-Users list suggested a "timeout" component that would, after 10 seconds of inactivity, turn off the current to the knee servo motor. After 1.5 more hours of chasing a weird FPU comparison issue, the component is now working. So, the motor turns off completely after 10 seconds of inactivity.

To sum it:

1) The knee moves at a perfectly acceptable speed (1cm per second or so) 2) It moves smoothly, with low acceleration (which reduces stresses on everything) 3) The following error is always under 0.001 4) After 10 seconds following completion of a move, the motor turns off and uses zero current.

There is a couple of things still to be done.

a) Install limit and home switches for the knee b) Investigate a mechanical issue with the setup, whereby the gearmotor is somehow not perfectly coaxial and wobbles aruond its axis of rotation. It is as if the crank handle shaft is bent slightly or something.

i
Reply to
Ignoramus20691
Loading thread data ...

CLIP

Photos, video? Would love to see a video...

Reply to
Joe AutoDrill

I will try. Good idea. I want to restore functioning of the rotary table first. I had to use the output that I used for rotary table, for the knee, because I was waiting to get an additional digital to analog converter card from Jon. I have it now and I need to restore. Then I will do a grand finale type of video, demonstrating all capabilities of the mill -- X, Y, Z, rigid tapping, 4th axis, and W axis (motorized knee).

i
Reply to
Ignoramus20691

The only sure way to do floating point comparisons, that I know of is: if( fabs(a - b) < epsilon )

I usually pick epsilon, at least 100 X smaller than the tolerable error.

This problem has been causing headaches for 50 years, and will probably continue to do so for another 50. After that I won't care. :-) 1.5 hours is pretty good. I've seen people spend days, and never find it.

Reply to
Gary A. Gorgen

With Microsoft C epsilon is defined in float.h for both float and double data types.

Reply to
David Billington

I don't use anything Microsoft, so I don't know. What does Microsoft C do about the comparison ? What is epsilon #define as, or it a variable.

Reply to
Gary A. Gorgen

Currently from visual studio 2008 float.h

#define DBL_EPSILON 2.2204460492503131e-016 /* smallest such that

1.0+DBL_EPSILON != 1.0 */ #define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */ #define LDBL_EPSILON DBL_EPSILON /* smallest such that 1.0+LDBL_EPSILON != 1.0 */

It's a standard header file, see

formatting link
, GCC has it also but the #defines above refer to other defines not in float.h on my Linux system and I haven't gone looking for where they are.

Reply to
David Billington

It is slightly worse than this because essentially, we are comparing

"a == a"

and it still fails as inequal. This is called "GCC bug 323" and is not a GCC bug, but a very insiduous FPU register issue.

formatting link
Anyway, I ended up modifying the component proposed by Andy and what I have now, works really great. The motor reliably shuts off 10 seconds after cessation of movement.

Here it is ======================================================================

component timeout """Reduces motor command to a predetermied value after a certain number of seconds of no axis motion""";

pin in float position-command-in "link to motor-pos-cmd"; pin in float current-in "link to the PID output"; pin out float current-out "link to the DAC"; pin out float debug-t-out "Watch t"; pin out float debug-old-pos "Watch old-pos"; pin out float debug-pos-in "Watch position-command-in"; pin out float debug-pos-diff "Watch position diff"; param rw float timeout = 10 "timeout in seconds"; param rw float default-current = 0 "current output after timeout"; variable float old_pos; variable float t = 0; function _; license "GPL"; author "Andy Pugh";

;;

#include

FUNCTION(_){

debug_old_pos = old_pos; debug_pos_in = position_command_in; debug_pos_diff = position_command_in - old_pos;

if( fabs( position_command_in - old_pos ) > 1E-7 ){ t = timeout; old_pos = position_command_in; } else { t -= fperiod; }

debug_t_out = t;

if( t < 0 ){ current_out = default_current * current_in; t = -1; } else { current_out = current_in; }

}
Reply to
Ignoramus1280

I do not use Microsoft either!

i
Reply to
Ignoramus1280

No, it's fundamental, as some others have noted.

Floating point numbers are inherently fuzzy, especially after a sequence of computations, so in practice tests of equality of floating-point variables always fail, even if one is using quad precision.

That's the correct approach, but the 1e-7 tolerance is not really coarse enough to be bulletproof.

The issue is the width in bits of the mantissa of the floating-point word. According to IEEE Std 754 (which is the formal definition of the universally used "IEEE float"), the mantissa of a 32-bit single float is

24 bits (binary32 format in the "Digits" column) supports 7.22 decimal digits, so a tolerance of 10e-7 is right on the edge, which will cause random-seeming failures.

To be specific, the "epsilon" is 1/(2^24)= 5.9605e-8, which is within a factor of two of 10e-7; this is far too tight a tolerance to be reliable. I would change the tolerance to 10e-6, so the code would read "if( fabs( position_command_in - old_pos ) > 1E-6 ){"

For the record, the width in decimal digits is calculated as follows: Log10[2^24]= 7.2247

Joe Gwinn

Reply to
Joseph Gwinn

I use Linux for virtually all my personal computing but I make most of my living writing software that runs in MS Windows, must be I sinned badly in a past life.

Reply to
David Billington

I feel blessed, because my entire digital life, including personal, work, and making money from websites, is based on Linux.

i
Reply to
Ignoramus1280

Except that it does not apply here Joe.

I had the same issue at work once. a == a fails even though both numbers are equal and come from the same source. But one is in a CPU register and one is in memory.

Read that bug discussion, it is a fountain of knowledge.

i

Reply to
Ignoramus1280

My point is more general, that a==a is not reliable with floating point, even if the Gnu bug is fixed.

It sounds like a no-see-um of a problem. But a==a would is a problem regardless of that bug.

You need to make the tolerance larger, as discussed below.

Speaking of founts of knowledge, reading IEEE-754 has much to recommend itself. It may be arcane, but it lays out the foundations of floating point arithmetic, which in turn underlays essentially all of computer math.

There are many war stories.

Joe Gwinn

Reply to
Joseph Gwinn

I think everyone that programs, should read this. It does a good job of explaining the problem. I've worked on Fortran programs that every "if" statement, contained an epsilon. Some dealing with dimensions, in light years, others in X unites. This was in the 60's.

Reply to
Gary A. Gorgen

...

Using a fixed epsilon works if the numbers being compared are in some narrow range, eg 1e-6 to 1e6, but for wide-ranging numbers, epsilon needs to depend on the size of the numbers. In place of " < epsilon" one could substitute " < epsilon*(fabs(a)+fabs(b))", with epsilon~1e-9. Less-cumbersome forms apply in specific cases, eg if a and b have the same sign, or the magnitude of a always exceeds that of b, etc

If you want to test if b differs from a by no more than 1 bit of roundoff error, the following will work: if (a==b || b==nextafter(a,b) || a==nextafter(b,a)) ...

The nextafter() series of functions is defined in the C99 standard and available on linux.

Reply to
James Waldby

Ah, HA! Now I know what it is!

In a number of CPUs, especially the X86, they hold extra mantissa bits in the floating point hardware registers. When you save the register to a memory location, these extra bits of precision are truncated to the standard IEEE floating point representation. So, when you compare them again, they do not match!

I'm sure this is documented somewhere in the X86 hardware documentation. The reason for this is to allow repetitive calculations to maintain the full accuracy up to the external representation.

Jon

Reply to
Jon Elson

I consider it (extra precision in registers) a bug, by the way. It is wrong on many levels.

i
Reply to
Ignoramus1280

Comparisons on FP numbers are always fraught with potential difficulties. IME, you should always look for |x - y| < e where e is a small number, rather than x == y.

Best regards, Spehro Pefhany

Reply to
Spehro Pefhany

This is the sort of thing that separates the Real Programmers from the scriptkiddies. ;-)

Cheers! Rich

Reply to
Rich Grise

PolyTech Forum website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.