Go Down

### Topic: Any ideas how to increase floating point accuracy? (Read 2727 times)previous topic - next topic

#### Spine

##### Jul 29, 2011, 04:20 pm
Hi everyone,

I have a calculation that I would like to perform in the Arduino that I would like to gain a little more accuracy from.

Basically what this code does is calculates the intersection point of three circles (I am using it for a TDOA problem).  The problem is that these two equations are the least accurate (when comparing to using a calculator or excel) and output is off (in one case, the Y coordinate was off by almost 1cm) too much for my application.  I have a feeling this is due to the floating point precision of the arduino but I was wondering if there is something I could do, possibly optimizing the math or to use a fixed point library (which I couldn't get to compile) to increase the accuracy/resolution of the output.

The equations with the highest errors are  :
x01 = ((sq(d01) - sq(Rcircle1) + sq(Rcircle0)) / (2.0 * d01)) + c0offset;

and:
ypos = sqrt(sq(Rcircle1) + sq(x01));

With that being said, are there any math wizzes or people with experience with the fixed point libraries that could help me out? I appreciate all the help I can get!

Quote

void find_pos(){
// declare variables for this function
float Rcircle0 = 0.0;
float Rcircle1 = 0.0;
float Rcircle2 = 0.0;
float x01 = 0.0;
float x02 = 0.0;
float xerror = 999999.0;
float xerrornew = 999999.0;

// set the increment for the iteration (this should be not much smaller then the resolution of your equipment)
float inc = 0.1;  // set to 0.1cm or 1mm

// set the distance between the microphones (in cm)
float c0offset = -60.0;
float c1offset = 0.0;
float c2offset = 60.0;

// calculate the distances between mic 0 and 1 and mic 1 and 2
float d01 = c1offset - c0offset;
float d02 = c2offset - c0offset;

// for now the speed of sound is hard coded.  TBD: compensate based on air temperature
float Tair = 20.0;  // Air temperature hard coded to 20degC
float Vsound = 2004.57*(sqrt(Tair+273.15));  //speed of sound in cm/s

// After the time deltas are determined from the microphone processing logic, and normalized so that one microphone is
// time 0, the first step is to calculate the circle radii based on the speed of sound.
// Rcircle0 is the radius of mic0
// Rcircle1 is the radius of mic1
// Rcircle2 is the radius of mic2
// timeL is the normalized time of Mic0 (time0)
// timeC is the normalized time of Mic1 (time1)
// timeR is the normalized time of Mic2 (time2)

Rcircle0 = timeL* Vsound/1000000;  // Radius is in cm
Rcircle1 = timeC* Vsound/1000000;  // Radius is in cm
Rcircle2 = timeR* Vsound/1000000;  // Radius is in cm

// for every loop, increment each radius by the increment value and then calculate the X coordinate of the
// radical lines of the intersection of circles 0 and 1, and then do it again for circles 0 and 2.
// the difference between these two positions is the error. we are trying to find the point with the least error.
// loop to the radius of the offset of the mics

for (float xloop=0.0; xloop < (c2offset - c0offset); xloop=xloop+inc)
{
// increment each circle by the increment value
Rcircle0 = Rcircle0 + inc;
Rcircle1 = Rcircle1 + inc;
Rcircle2 = Rcircle2 + inc;

// calculate the radical lines
x01 = ((sq(d01) - sq(Rcircle1) + sq(Rcircle0)) / (2.0 * d01)) + c0offset;
x02 = ((sq(d02) - sq(Rcircle2) + sq(Rcircle0)) / (2.0 * d02)) + c0offset;

xerrornew = abs(x01-x02);

if (xerrornew < xerror)
{
xerror = xerrornew;
xpos = x01;
ypos = sqrt(sq(Rcircle1) + sq(x01));
temp1 = Rcircle1;

}
}
}

#### MarkT

#1
##### Jul 29, 2011, 05:58 pm
That code is incomplete and I can't test it.  Can you reduce the issue down to something that can be reproduced and is obviously returning an imprecise answer (ie differs from what a calculator says).  Otherwise it might be that you've made a mistake in your code...
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### PaulS

#2
##### Jul 29, 2011, 11:36 pm
Code: [Select]
`  for (float xloop=0.0; xloop < (c2offset - c0offset); xloop=xloop+inc)`
Using float variables for for loop indices is rarely a good idea. Either make this a while loop, or change the logic so that the index can be an int.

#### tedcook

#3
##### Jul 30, 2011, 12:12 amLast Edit: Jul 30, 2011, 12:27 am by tedcook Reason: 1
What happens if you declare everything double, instead of float?  That would be my first step if I think I'm running into float precision limits.

*edit: oops, just actually read the info on double in the reference.  I had seen double was listed under data types so I assumed Arduino could do double, but apparently not.  I was under the impression Arduino was a straightforward C implementation, are there any other caveats to C that one should be aware of?

#### EVP

#4
##### Jul 30, 2011, 01:19 am
I had no idea you could use floats in a for loop. I'm sure i had looked at this years ago and it wasn't possible?

Cant you just multiply everything by multiples of 10 to make the numbers bigger or something along those lines.

#### David Pankhurst

#5
##### Jul 30, 2011, 07:58 am
Unfortunately, double is simply a synonym for float on the Arduino, and a quick look online showed no library for higher precision floating point (but it was a quick search - if anyone finds one, I too would like to know).

The float is a 32 bit variable, which ends up being about 6-7 digits of precison, and would explain the problem here:

http://www.arduino.cc/en/Reference/Float

For example, even your value of inc (0.1) won't store precisely, so you'll have problems.

One thought - convert these to longs:

Code: [Select]
`long c0offset = -60;long c1offset = 0;long c2offset = 60;long d01 = c1offset - c0offset;long d02 = c2offset - c0offset;`

Since they all seem to be integer anyways, and then change the formula:

Code: [Select]
`x01 = ((sq(d01) - sq(Rcircle1) + sq(Rcircle0)) / (2.0 * d01)) + c0offset;`
to
Code: [Select]
`long temp1=2*d01;long temp2=d01*d01;x01 = ((temp2 - sq(Rcircle1) + sq(Rcircle0)) / temp ) + c0offset;`

It won't do much, but you'll save two floating point multiplies, and resultant loss of precision.

#### MarkT

#6
##### Jul 30, 2011, 12:06 pm
Sorry people to disagree: 7 digits is more than plenty for these calculations - this is simple trigonometry.

My suspicion is a bug in the program, use of int where float was intended, different rounding/truncation - or it might be a bug in Arduino float library, but that's less likely I feel.

Another possibility is use of a badly conditioned numerical technique that is multiplying up errors, often a gotcha for naive application of equations without error-analysis.

If the complete code was available someone could reproduce the problem and work out what's going wrong.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### Spine

#7
##### Aug 01, 2011, 04:36 am
Ok, It turns out I am an idiot!

I had a slight error in my Y position calculation, + instead of -  (comes from c^2 - a^2 = b^2).  I was so involved with thinking of all these other possibilities I overlooked a simple pythagorean theorem typo.

I re-ran my simulations with comparing to microsoft excel and the arduino answers are on average about 0.5mm off either direction with the worst case around 1mm error.  This is well within the error of other parameters in my system so It is good enough for this application.

Thanks to all that answered and sorry for my massive loss of brain function!

#### MarkT

#8
##### Aug 02, 2011, 11:46 pm
Its always good to do a back-of-the-envelop sanity calculation to check the result is in the right ballpark - unless you were triangulating on the scale of GPS I knew single floats are accurate enough...

If you frame a posting along the lines of "this is what I expect to see", "but this is what I am seeing",  then someone can have a go and working out which is actually wrong...  If you just say "blah is broken" we don't have a handle on things.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### robtillaart

#9
##### Aug 03, 2011, 03:23 pm

you are using floats to represent the mm.

Code: [Select]
`  float inc = 0.1;  // set to 0.1cm or 1mm`

You could also do most/all of the math in mm. You will get

Code: [Select]
`  int inc = 1; // set to 1 mm  unsigned long Rcircle0 = 0;   // a radius cannot be negative afaik  etc `

Using long instead of float would improve the accuracy, speed up the math and decrease the footprint of the sketch. There might be a few places were floats are needed but give it a try.

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up

Please enter a valid email to subscribe