Discussion of Integers or Fixed Point Numbers in Firmware

Has there ever been a discussion of moving as much of the computation to integers or fix point numbers in the firmware.

It doesn’t sound like it would be easy, but if we could get it to work it might have a dramatic effect on computational speed. I don’t know whether additional cycles would be a benefit, but I think many of us are under the impression that they might help and certainly couldn’t hurt.

2 Likes

Looking over the code. I think the triangular inverse calculation could be changed to fixed point math relatively easily., which is the only complex code called in the movement loop Although, based on my testing of the loop timing, I am not sure this is the real issue.

It may also be possible to do the PID calculations in fixed point as well, this may help us, if the PID ISR is the thing that is interfering with the Serial communications.

But looking at the quadrilateral calculations looks daunting, I don’t know if that could ever be done.

2 Likes

No, this is not something we’ve ever talked about. I’ve wondered about it, but
it didn’t seem like something to suggest until we had other issues working
better.

David Lang

1 Like

If it was good enough for the Mayans it should be good enough for us!

Yeah, that’s pretty obscure. According to my 1973 copy of Knuth Volume 2, “Seminumerical Algorithms” the Mayan Indians get credit for inventing fixed point around 2000 years ago. Interestingly they must have counted on both their fingers and toes in base 20.

Fixed point will leave software floating point way back in the weeds. Always amazed it isn’t used a lot more than it is. Other than teacup 3D AVR based motion control seems to have ignored it

2 Likes

Hmm, I already hit a speed bump in my initial testing.

So I figured I would try the triangular inverse function which looks like:

float Chain1 = sqrt(pow((-1*_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));
float Chain2 = sqrt(pow((_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));

I found this library https://github.com/tuxcell/sqrtLogAssemblyArduino for an integer based sqrt.

I ran 50000 loops using floating point numbers and got an average around 5 microseconds to perform the above.

Then I tried the sqrt32 function I think pow is already int friendly. I chose a 32 bit wide int because the total number of encoder steps to cover the worksurface is ~470,000 steps. So bigger than a 16 bit int.

But the outcome was a dramatically slower 72 microseconds to be exact. So I also tried this version is a 32bit sqrt https://stackoverflow.com/a/1101217 but it was nearly exactly the same.

Where have I gone wrong?

1 Like

it’s very possible that the 8 bit proccessor is just that inefficient at 32/64
bit math that the optimized floatingpoint math is actually faster.

I initially tried to keep as many variables as ints as possible and kept running into overflow problems. My favorite was that at one point if I tried to cut a straight line longer than 20 inches or so the machine would very calmly cut the first 20 inches then switch direction and calmly cut back the way it came. It took me a whole day to figure out I was overflowing an int causing a sign change in the twos complement number :speak_no_evil:

1 Like

Nice.

Yeah, given the size and resolution we have, we are talking 32 bit wide variables to accommodate everything.

So I stumbled on this post this morning:

http://forum.arduino.cc/index.php?topic=196522.0

Some of it suggests that floating point math may not be slower than integer math in some cases? Possibly because the floating point math is done as 16 bit? That seems odd to me

1 Like

Yep, they can be tricky. A long time ago it took two of us around a week to find out that the Motorola 68K C compiler didn’t handle unsigned int overflows properly - it should have gone from maxint to 0 but it was one off (don’t remember which way) due to an improper condition code on the branch instruction used. Screwed up a sequence number test in a TCP sequence number handler after about 8 hours of testing (on an Sun 3, same CPU as our real time machine but did logging to disk!), but once it got there the frame would just keep retransmitting. Think we fixed it by adding an explicit test for zero. It was tougher to find because it started with a random number rather than 0, and we didn’t catch on that it was an overflow for a long time.

8 bit exponent, 24 bit mantissa, isn’t it?

1 Like

Alright, this might be the end of the line for me. So I found this great fixed-point library:
https://github.com/Yveaux/Arduino_fixpt

It would certainly make things easier to use a well spec’d out library. 16.16 is less convenient but it has the resolution we need.

But sadly, in looking for actual implementations of this I found this discussion by the author:
https://forum.mysensors.org/topic/278/floating-point/33

It seems that someone much more knowledgeable on the issue than me has decided that there is no panacea of speed increase to be had.

2 Likes

Quite likely I’ve mis-understood the conversation but…

Have you tried caching the results?

float Chain1 = sqrt(pow((-1*_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));
float Chain2 = sqrt(pow((_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));

becomes

float ypow = pow((_yCordOfMotor - yTarget),2);

float Chain1 = sqrt(pow((-1*_xCordOfMotor - xTarget),2)+ ypow);
float Chain2 = sqrt(pow((_xCordOfMotor - xTarget),2)+ypow);

In tight loops the caching of results can have dramatic effects.

Also what about removing the call to the pow function. Whether this yields a result will depend on how well the compiler optimises out short functions but:

// I’m unclear whether the register keyword is supported in any meaningful way in the arduino
// but it won’t do any harm.
register float ydelta = _yCordOfMotor - yTarget;
register float ypow = ydelta * ydelta;

register float xdelta = _xCordOfMotor - xTarget;
float Chain2 = sqrt(xdelta+ypow);

register float invxdelta = (-1 * _xCordOfMotor) - xTarget;
register float invxpow = invxdelta * invxdelta;
float Chain1 = sqrt(invxdelta+ ypow);

3 Likes

Yes that is a good idea. I am pretty sure I did this when testing things out, but it is such an obvious improvement, we should probably make this change in the master.

It doesn’t look like registers are supported arduino forum - registers

I have put a bit of this work on the shelf, as I am still concerned that an increased loop frequency may cause damage to the motor shield. If I can find the time to finish my work on the redesign of the velocity controller, it might solve my concerns.