chipKIT® Development Platform

Inspired by Arduino™

Cos, and Sin problem

Created Wed, 24 Oct 2012 09:29:14 +0000 by alunosepf


alunosepf

Wed, 24 Oct 2012 09:29:14 +0000

Hello, i have this code with atmega2560, sends correct value off sin(x) an cos(x) over the serial port. if i chose cerebot mx3k the values are incorrect. can help?

double PwmMotor1; double PwmMotor2; double PwmMotor3; double PwmMotor4; double radianos;

double var1; double var2;

void Movimento( int Velocidade_Linear,int Velocidade_Rotacional,int direcao) {

// velocidade máxima 127

radianos=2*PI;
radianos=radianos*(double)direcao/360;



PwmMotor1=Velocidade_Linear*cos(radianos)+Velocidade_Rotacional;
PwmMotor3=PwmMotor1;

PwmMotor4=Velocidade_Linear*sin(radianos)+Velocidade_Rotacional;
PwmMotor2=PwmMotor4;
Serial.println("  Motor ");

Serial.print (PwmMotor1); Serial.print(" ");
Serial.print (PwmMotor2);Serial.print(" ");
Serial.print (PwmMotor3);Serial.print(" ");
Serial.print (PwmMotor4);Serial.print(" ");

}


dangeljs

Wed, 24 Oct 2012 22:33:55 +0000

Try changing the 'double' types to 'float' types. I had trouble before with the arctan and it was because I was using a double precision. If a double was good enough on your arduino, a float for chipkit should be just as good as they are both 32 bits.

Hope it helps.


ricmorte

Thu, 25 Oct 2012 20:36:27 +0000

Try changing the 'double' types to 'float' types. I had trouble before with the arctan and it was because I was using a double precision. If a double was good enough on your arduino, a float for chipkit should be just as good as they are both 32 bits. Hope it helps.

What is the problem with the double precision? I bought the max32 because I have a project that involves lots of maths and greater precision is needed than can be obtained through the Arduino Mega 2560. I quote from stackoverflow:

http://stackoverflow.com/questions/2386772/difference-between-float-and-double

[There ia a...] Huge difference. As the name implies, a double has 2x the precision of float[1]. In general a double has 15 to 16 decimal digits of precision, while float only has 7.

My project uses some very large numbers (>10^^6) where precision is required right down to the 12+ decimal place. Almost all the calculations are trigonometric so it's just not possible not to use sin, cos, atan2, etc...

Thanks, Ric


mikes

Thu, 25 Oct 2012 22:53:51 +0000

You could use a Taylor series and make your own function that uses doubles (I would use a higher one then only use the part close to zero and mirror it for the other values)

Remember cos(x)=sin(x-pi/2) tan(x)=sin(x)/cos(x)


WestfW

Sat, 27 Oct 2012 08:29:47 +0000

Now that ChipKit is all newlib and open source based, we don't have the excuse of conflicts between the open source and proprietary libraries, and we should figure out for sure IF the double-precision math functions are broken. And if they are, we should fix them; there are for-sure working dp math functions distributed with gcc, right? (perhaps not very well optimized, but they should be working...)


ricmorte

Sat, 27 Oct 2012 12:11:51 +0000

... and we should figure out for sure IF the double-precision math functions are broken. And if they are, we should fix them...

It is clear that since this time last year the double precision has been implemented. long double also appears to be 8 bytes but I cannot see that offers any advantage over the current double.

Like some other posters, I am calculating astronomical ephemeris where lack of precision in the early calculations propagates to significant errors further down the line (there may be 10 or more stages of computation for some ephemeris). Maintaining precision early on really does make a difference.

I am moving from the Arduino 2560 where I had to create functions that combined both integer and floating point calculations so that I could handle the limited precision of the avr libs (double and float are both 4 bytes which means you cannot have large numbers whilst at the same time retain precision to the nth decimal place).

I have only just started to use the Max32 these past 3 or 4 days and already I can see that some double float calculations are not as accurate as those optimised for the Arduino. This is something I had not expected. I will spend some time these next few days going through each calculation for a range of values to see where the discrepancies occur.

If I come up with anything definitive I will report back. Either way I need to see why the initial results seem so poor. I haven't yet ruled out stupidity on my part.

However I am completely in agreement with WestfW. I haven't seen any concrete evidence yet that the libraries are broken, so for me the Jury is still out. We do however need answers based on reproducible conditions.

Regards, Ric

PS: Initial results for Julian Day & Time since the J2000.0 equinox show that the dp calculation in MAX32 is accurate within the limits of the expected precision. My optimised functions combining integer and floating point are working perfectly also with the difference being that the fractional component is now accurate to 15dp rather tha the 6 or 7dp in AVR. I am perfectly happy with these results but note that none of the calculations involved use the trig functions. That's the next step.

PPS: thanks mikes for the links to the Taylor series. I hadn't thought of rolling my own but hopefully the existing library functions will turn out good. Let's see.


pito

Sun, 28 Oct 2012 12:08:47 +0000

BTW the Microchip's double precision floating point calcs (incl. trigs) were the most precise when compared to other compilers .. p.


pito

Sun, 28 Oct 2012 12:26:31 +0000

For a quick test how fast and how precise your math libs and especially trig libs are you may run this simple test called "9 degree test" as the correct result shall be 9.000000000000000 (all vars are double except timer which is unsigned int):

printf (" THIS IS THE START \r\n");   
p64 = 3.1415926535897932384626433832795;
timer = millis;
for (i=1;i<=1000;i++) {
	// 9 degree
	q64 = 9.000;
   // Convert to radians
	q64 = q64 * p64 / 180.0;
	// Make the test
	q64 = asin(acos(atan(tan(cos(sin(q64))))));
	// Convert to degree
	q64 = q64 * 180.0 / p64;
      }   
timer = millis - timer; 
printf(" THIS IS THE END \r\n");
printf(" Elapsed time 1000x : %u millis\r\n", timer);
printf(" Result= %1.15e \r\n",q64);

The precision comparision for various calculators: http://www.rskey.org/~mwsebastian/miscprj/results.htm


EmbeddedMan

Sun, 28 Oct 2012 14:33:02 +0000

Which, when converted to Arduino/chipKIT style, looks like this:

void setup()
{
  float p64, q64;
  int i, timer;
  Serial.begin(9600);
  delay(4000);
  Serial.println ("THIS IS THE START");   
  p64 = 3.1415926535897932384626433832795;
  timer = micros();
  for (i=0; i <= 1000; i++) {
    // 9 degree
    q64 = 9.000;
    // Convert to radians
    q64 = q64 * p64 / 180.0;
    // Make the test
    q64 = asin(acos(atan(tan(cos(sin(q64))))));
    // Convert to degree
    q64 = q64 * 180.0 / p64;
  }   
  timer = micros() - timer; 
  Serial.println("THIS IS THE END");
  Serial.print("Elapsed time 1000x : ");
  Serial.print(timer);
  Serial.println(" microseconds");
  Serial.print("Result= ");
  Serial.print(q64,15);
  Serial.println("");
}

void loop(void)
{
}

Now, when I run the above on my FubarinoSD board (or any chipKIT board for that matter), I get the following result:

THIS IS THE START THIS IS THE END Elapsed time 1000x : 1 microseconds Result= 9.000000000000000

But I'm not sure I believe that. How can we do that much math in such little time? I added a Serial.print(q64,15); inside the loop, and it does actually print out all those iterations (takes forever). Maybe the compiler sees all of those nested functions and is smart enough to replace the whole mess with 9.0? I don't know. But it gets the right answer, and it's really fast. Cool.

*Brian


pito

Sun, 28 Oct 2012 17:09:42 +0000

In reality the calculation does not take 1usec :). If that loop got optimised out then you possess a good compiler, indeed. Try to disable the optimisation. I would expect something like 500-1000ms for 1000x. And the precision - something like 8.9999999993432, maybe..


pito

Sun, 28 Oct 2012 17:35:45 +0000

FYI- this is a result from ARM STM32F100 @48MHz, IAR compiler: THIS IS THE START THIS IS THE END Elapsed time 1000x : 273 millis Result= 8.999999999999968e+00


EmbeddedMan

Sun, 28 Oct 2012 17:45:46 +0000

OK, so with no optimizations, I get the following output: THIS IS THE START THIS IS THE END Elapsed time 1000x : 332117 microseconds Result= 9.000000000000000

(332ms) which seems like a very reasonable number. Normally the MPIDE compiler is at -O2 optimization level, and in the version of gcc that we are using, this must allow it to seriously optimize the math functions in this test code.

*Brian


pito

Sun, 28 Oct 2012 17:53:51 +0000

The time seems to be ok, the precision is fantastic - are you sure your printf(..) does not provide a kind of rounding? Is that the equivalent of %1.15e ?


EmbeddedMan

Sun, 28 Oct 2012 18:19:21 +0000

Pito,

I believe it is. For example, if I modify the code like this :

void setup()
{
  float p64, q64;
  int i, timer;
  char t[100];
  
  Serial.begin(9600);
  delay(4000);
  Serial.println ("THIS IS THE START");   
  p64 = 3.1415926535897932384626433832795;
  timer = micros();
  for (i=0; i <= 1000; i++) {
    // 9 degree
    q64 = 9.000;
    // Convert to radians
    q64 = q64 * p64 / 180.0;
    // Make the test
    q64 = asin(acos(atan(tan(cos(sin(q64))))));
    // Convert to degree
    q64 = q64 * 180.0 / p64;
  }   
  timer = micros() - timer; 
  Serial.println("THIS IS THE END");
  Serial.print("Elapsed time 1000x : ");
  Serial.print(timer);
  Serial.println(" microseconds");
  Serial.print("Result= ");
  Serial.print(q64,15);
  Serial.println("");
  sprintf(t, "%1.15e", q64);
  Serial.println(t);
}

void loop(void)
{
}

I get the following output:

THIS IS THE START THIS IS THE END Elapsed time 1000x : 1 microseconds Result= 9.000000000000000 9.000000000000000e+00

Is that pretty good proof that the value and accuracy we see is real? (I've never really pushed the limits of floats before, so I don't know exactly what to look for here.)

*Brian


pito

Sun, 28 Oct 2012 18:27:01 +0000

1usec - it seems to be optimised out. You must get the elepsed time as you got before - 332ms.. Precision - 9.000000000000000 to get is rare with 64bit fp. That is typical for 80bit or 128bit (34 dec places). Typical 64bit fp results I've seen with AVR, PIC24/33, ARM is from 8.999999999XXXXXX to 8.9999999999999XX, the best results with MCHP C30 (PIC24/33) and IAR (AVR, ARM).


EmbeddedMan

Sun, 28 Oct 2012 20:40:31 +0000

Yup, the 1us is 'optimized out'. If I make the floats volatile (even with -O2), then the time jumps up to 343ms.

Gcc probably sees that the result is never used, and so the whole loop just goes away.

*Brian


pito

Sun, 28 Oct 2012 21:35:40 +0000

I've split the calc (latest MPIDE beta, fubarinoSD795): // Make the test q64 = (tan(cos(sin(q64)))); q64 = (asin(acos(atan(q64)))); With volatile float I get: Elapsed time 1000x : 332988 microseconds Result= 9.000000953674316 With volatile double: Elapsed time 1000x : 323840 microseconds Result= 9.000000000000010 I'm a little bit confused with the timings as the double fp takes usually ~2x single fp. It seems there are still some issues with the math, or better we have to read the manual :). Maybe you have got something like sin(), sinf(), sinl(), etc.


EmbeddedMan

Sun, 28 Oct 2012 22:13:21 +0000

I wonder why the results are different than when you have everything all on a single line. Shouldn't the answer come out the same?

*Brian


Jacob Christ

Mon, 29 Oct 2012 03:34:45 +0000

The math routines probably use doubles so when you use a float type your just up converting to a double in the math calls (hence similar times).

Jacob


WestfW

Mon, 29 Oct 2012 04:28:25 +0000

Hmm. Using the last "release" version (I think. I can't actually read the version number in the "about" dialog...), I get:

Elapsed time 10000x : 390 milliseconds
Result= 9.000030517578125

But it's not doing double-precision trig:

q64 = asin(acos(atan(tan(cos(sin(q64))))));
9d0013f4:       8fa40018        lw      a0,24(sp)
9d0013f8:       8fa5001c        lw      a1,28(sp)
9d0013fc:       0f400efb        jal     9d003bec <__truncdfsf2>
9d001400:       00000000        nop
9d001404:       0f40137b        jal     9d004dec <fpsin>
9d001408:       00402021        move    a0,v0
9d00140c:       0f401336        jal     9d004cd8 <cosf>
9d001410:       00402021        move    a0,v0
9d001414:       0f4012cc        jal     9d004b30 <fptan>
9d001418:       00402021        move    a0,v0
9d00141c:       0f4009b4        jal     9d0026d0 <atanf>
9d001420:       00402021        move    a0,v0
9d001424:       0f400b63        jal     9d002d8c <acosf>
9d001428:       00402021        move    a0,v0
9d00142c:       0f400b81        jal     9d002e04 <asinf>
9d001430:       00402021        move    a0,v0
9d001434:       0f400ed2        jal     9d003b48 <__extendsfdf2>
9d001438:       00402021        move    a0,v0
9d00143c:       afa20018        sw      v0,24(sp)
9d001440:       afa3001c        sw      v1,28(sp)

WestfW

Mon, 29 Oct 2012 05:03:20 +0000

The latest test image (20121013) does indeed seem to support "long double" as a 64 bit quantity, and comes up with exactly 9....

// Make the test
    q64 = asin(acos(atan(tan(cos(sin(q64))))));
9d0013f4:       8fa40020        lw      a0,32(sp)
9d0013f8:       8fa50024        lw      a1,36(sp)
9d0013fc:       0f400ab3        jal     9d002acc <sin>
9d001400:       00000000        nop
9d001404:       00402021        move    a0,v0
9d001408:       0f400a76        jal     9d0029d8 <cos>
9d00140c:       00602821        move    a1,v1
9d001410:       00402021        move    a0,v0
9d001414:       0f400aee        jal     9d002bb8 <tan>
9d001418:       00602821        move    a1,v1
9d00141c:       00402021        move    a0,v0
9d001420:       0f40090e        jal     9d002438 <atan>
9d001424:       00602821        move    a1,v1
9d001428:       00402021        move    a0,v0
9d00142c:       0f400b12        jal     9d002c48 <acos>
9d001430:       00602821        move    a1,v1
9d001434:       00402021        move    a0,v0
9d001438:       0f400b60        jal     9d002d80 <asin>
9d00143c:       00602821        move    a1,v1
9d001440:       afa20020        sw      v0,32(sp)
9d001444:       afa30024        sw      v1,36(sp)

"Print" Still doesn't support long double, though... The image has gotten pretty huge: 20121013: Binary sketch size: 58336 bytes (of a 126976 byte maximum) 20120903: Binary sketch size: 34288 bytes (of a 126976 byte maximum) AVR1.0.1: Binary sketch size: 6,854 bytes (of a 32,256 byte maximum) (keep in mind that AVR has it's own special math library for microcontrollers, optimized for low memory footprint. (pretty neat stuff, actually.))


WestfW

Mon, 29 Oct 2012 05:12:19 +0000

(I should note that the new mpide is using the 64bit trig functions even for "float" variables (just like a "real" computer))


pito

Mon, 29 Oct 2012 07:51:54 +0000

FYI - some ~2-3 years old results I've found:

Microchip C30 comp, dspic33/pic24:

float 9.000009536743164e+00 float fast_math_lib 9.000010490417480e+00 double 8.999999999999971e+00

CCS comp. dspic33/pic24:

float 32bit 8.999962806701660156E+00 float 48bit 9.000006227055564522E+00 double 64bit 9.000001620467530827E+00

PS: the code with C30 is ~4x smaller (pic24HJ, 16.3kB, double) than with the latest MPIDE.


EmbeddedMan

Mon, 29 Oct 2012 11:53:07 +0000

pito, when comparing code sizes, are you JUST looking at the math libraries and the test code? Or (for the MPIDE builds) are you also including all of the other core library code that always gets included in a sketch? (Like serial, interrupt handling, etc.)

I just want to make sure you're comparing apples to apples. The MPIDE sketches are pretty large, primarily because we don't compile in MIPS16 mode and we include a lot of libraries by default. The good news is that, as you add your own sketch, the overall image size doesn't grow as fast as you think it might, meaning that you can put a whole lot of your own code in a 256K part.

*Brian


pito

Mon, 29 Oct 2012 20:19:40 +0000

Brian, that is a complete code for above test (pic24hj, serial setup, printf, math_libs, interrupts, oscillators, everything..). The fast_math lib worked with dspic33. From C30 and p24HJ128GP502 (9degree test, double, res=8.999999999999971e+00 ):

Program Memory  [Origin = 0x200, Length = 0x15600]

section                    address   length (PC units)   length (bytes) (dec)
-------                    -------   -----------------   --------------------
.text                        0x200              0x26d0          0x3a38  (14904)
.const                      0x28d0                0xdc           0x14a  (330)
.text                       0x29ac               0x188           0x24c  (588)
.dinit                      0x2b34               0x146           0x1e9  (489)
.isr                        0x2c7a                 0x2             0x3  (3)

                     Total program memory used (bytes):         0x3fba  (16314) 12%]

FYI - the complete single precision math lib (inclusive trig functions) for atmega32 is ~4-5kBytes. For double and 32bit mips32 I would expect max 16kBytes. You may try to compile with mips16. The code might be 20-30% smaller. p.