Pages: [1]   Go Down
Author Topic: Any ideas how to increase floating point accuracy?  (Read 1063 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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;
      
    }
  }  
}


Logged

0
Offline Offline
Shannon Member
****
Karma: 160
Posts: 10418
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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...
Logged

[ I won't respond to messages, use the forum please ]

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 548
Posts: 46020
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  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.
Logged

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 35
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
« Last Edit: July 29, 2011, 05:27:08 pm by tedcook » Logged

Yorkshire England
Offline Offline
Sr. Member
****
Karma: 2
Posts: 267
Arduino good init
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Victoria, BC, Canada
Offline Offline
Full Member
***
Karma: 0
Posts: 222
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
x01 = ((sq(d01) - sq(Rcircle1) + sq(Rcircle0)) / (2.0 * d01)) + c0offset;
to
Code:
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.
Logged

0
Offline Offline
Shannon Member
****
Karma: 160
Posts: 10418
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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!   smiley-grin
Logged

0
Offline Offline
Shannon Member
****
Karma: 160
Posts: 10418
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

[ I won't respond to messages, use the forum please ]

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12428
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


you are using floats to represent the mm.

Code:
  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:
  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.



Logged

Rob Tillaart

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

Pages: [1]   Go Up
Jump to: