|
|
View previous topic :: View next topic |
Author |
Message |
louwi_138
Joined: 17 Nov 2012 Posts: 23
|
atan error |
Posted: Tue Oct 08, 2013 12:20 pm |
|
|
Hello,
I need to calculate some angle by the use of atan but this one is not stabel always in fact after debuging I noteced that it's make for some input an error = 3° witch cause a huge bad effect on the result of my program.
so if some one have any code of atan more accurate then the one of the library math.h
this is what is writeen in the top of my library :
Code: | ////////////////////////////////////////////////////////////////////////////
//// (C) Copyright 1996,2010 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS C ////
//// compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, reproduction ////
//// or distribution is permitted without written permission. ////
//// Derivative programs created using this software in object code ////
//// form are not restricted in any way. ////
////////////////////////////////////////////////////////////////////////////
//// ////
//// History: ////
//// * 9/20/2001 : Improvments are made to sin/cos code. ////
//// The code now is small, much faster, ////
//// and more accurate. ////
//// * 2/21/2007 : Compiler handles & operator differently and does ////
//// not return generic (int8 *) so type cast is done ////
//// * 6/19/2010 : Divisions by constants converted to multiplication ////
//// by its inverse to improve computation speed ////
//// ////
//////////////////////////////////////////////////////////////////////////// |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Tue Oct 08, 2013 1:15 pm |
|
|
Are you calculating back from a division?. If so, look at atan2. This performs better at the extreme values (0, and infinity).
Also for speed, I have posted a much faster approximation (a search for atan2, and my name should find it), which is within a fraction of a degree for the whole quadrant, and a lot faster.
Worth 'trying'.
However are you sure something is not giving the calculation the wrong value?. I ran the supplied atan function for a span of values and compared it with the values calculated on a PC, and it differed by only a tiny fraction of a degree. Correct value for converting degrees to radians?.
Best Wishes |
|
|
louwi_138
Joined: 17 Nov 2012 Posts: 23
|
|
Posted: Tue Oct 08, 2013 2:04 pm |
|
|
Ttelmah wrote: | Are you calculating back from a division?. If so, look at atan2. This performs better at the extreme values (0, and infinity).
Also for speed, I have posted a much faster approximation (a search for atan2, and my name should find it), which is within a fraction of a degree for the whole quadrant, and a lot faster.
Worth 'trying'.
However are you sure something is not giving the calculation the wrong value?. I ran the supplied atan function for a span of values and compared it with the values calculated on a PC, and it differed by only a tiny fraction of a degree. Correct value for converting degrees to radians?.
Best Wishes |
Yes I'm using division. I tried atan2 and it's the same result. Here is my code:
I have an robot at position (x_r,y_r) with an initial angle "ang_r" % the axis of X. Tthe robot should go to the position (x_o,y_o).
So the first time I rotate the robot to be directly face to face with the object by the use of the function "rotate", then I make it go directly to the object.
If any error is made in the rotation angle it will make a big problem when I go to the objective, because it will cause a big offset when I go for a large distance like 4 meters. It cause an offset of 30 cm :/
Code: |
void rotation(){
//distance between robot and objective
if(x_o > x_r){
dx = x_o - x_r;
sig = 0;
}else{
dx = x_r - x_o;
sig = 1;
}
if (y_o > y_r) dy = y_o - y_r;
else dy = y_r - y_o;
//angle of the vector (robot --> objective) % OX;
ang_o = atan2(dy,dx);
if (sig) ang_o += pi / 2;
//compute error and correction
if(ang_r > ang_o){
ang = ang_r - ang_o;
watch_variable0= (int16)(ang*180/pi);
if (ang > pi)
move(rot2_rad(2 * pi - ang),G2);
else
move(rot2_rad(ang),D2);
}else{
ang = ang_o - ang_r;
watch_variable0= (int16)(ang*180/pi);
if (ang > pi)
move(rot2_rad(2 * pi - ang),D2);
else
move(rot2_rad(ang),G2);
}
ang_r = ang_o;
} |
|
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1345
|
|
Posted: Tue Oct 08, 2013 4:04 pm |
|
|
in the v4.1xx compiler somewhere, there used to be a bug using atan and atan2 directly from the h files for some chips (in my case it was a PIC24 chip). The bug would clobber a working register, making the output incorrect. I reported it but have no clue if they ever fixed it as I ended up not using the algorithm I started with.
The work around I used was to copy the function from the h file, paste it into one of my own files, rename it, and it worked fine without clobbering the working register.
Might be worth a try, but I think using a more optimized version (mentioned earlier) might be better anyways. |
|
|
louwi_138
Joined: 17 Nov 2012 Posts: 23
|
|
Posted: Tue Oct 08, 2013 7:34 pm |
|
|
It's work fine,
here is the solution for this problem I tested it and it work (it's the own of Ttelmah ) :
Code: | float angle(float X, float Y)
{
//routine to give a fast solution for angle, from X/Y co-ordinates - result in degrees
float AX,AY,ival,oval,aival;
int8 quad;
AX=fabs(X);
AY=fabs(Y);
//Now the approximation used works for tan from -1 to 1, so we have to keep the
//values inside this range and adjust the input/output.
//Four 'quadrants' are decoded -1 to 1, (315 to 45 degrees), then 45 to 135,
//135 to 225, 225 to 315
If (X >= 0) //Right hand half of the circle
{
If (AY > X)
{
If (Y < 0)
{
quad = 4;
ival = -X / Y;
}
Else
{
quad = 2;
ival = X / -Y;
}
}
Else
{
If (AY > X)
{
quad = 4;
ival = -Y / X;
}
else
{
quad = 1;
ival = Y / X;
}
}
}
else
{
//Now the others
If (Y > AX)
{
quad = 2;
ival = X / -Y;
}
Else
{
If (AY > AX)
{
quad = 4;
ival = -X / Y;
}
Else
{
quad = 3;
ival = -Y / -X;
}
}
}
//A lot of lines of code, but small and quick really.....
//Now the solution
//Now approximation for atan from -1 to +1, giving an answer in degrees.
aival = fAbs(ival);
oval = 45 * ival - ival * (aival - 1) * (14.02 + 3.79 * aival);
//Now solve back to the final result
If (quad != 1)
{
If (quad == 2)
oval = oval + 90;
Else
{
If (quad == 3)
oval = oval + 180;
Else
oval = oval + 270;
}
}
if (oval<0)
oval+=360;
return oval;
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Wed Oct 09, 2013 12:34 am |
|
|
Glad my version worked. It is optimised for size/speed, but gave quite good results.
Interesting the comment from Jeremiah. I had not seen this, using tests displaying the results, but it (of course) it'd only "rear it's head" if the same register was being used elsewhere, and then give screwy results. Might be what the original poster was seeing.... Ugh.
There was a 'historical' occasional behaviour like this when you used some functions in complex formulae, but it didn't tend to occur when you used the functions on their own. The original poster's code, doesn't do anything else in the line with the atan, so is unlikely to show this - I wonder if Jeremiah's problem could have been this one?. The fix is really 'odd', suggesting that CCS is treating their own code differently. I'd suspect it was just the function being 're-named', that actually fixed the behaviour, which is 'interesting'...
Best Wishes |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1345
|
|
Posted: Wed Oct 09, 2013 11:50 am |
|
|
Back then I didn't end up trying to rename the function in math.h (probably should have, but I usually keep those read only so I don't mess them up). For reference it was on the PIC24FJ256GA106. I want to say compiler rev 4.114, but this was a couple years ago, so not 100% on the compiler rev.
Back then, when I looked at the LST file, I didn't get 10 ASM lines into the function and one of the working registers got clobbered. Literally something put a value into a working register, and then, within a few lines, overwrote the same register without using the value from before or storing it else where (no push or move to W15 for the stack either as that had already occurred earlier).
After copy/paste/renaming the function from the CCS file to my own, the LST showed correct assembly and it worked. I reported it to CCS, so it may have been fixed and this could be a different problem.
Last edited by jeremiah on Thu Oct 10, 2013 12:26 pm; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Thu Oct 10, 2013 12:28 am |
|
|
The funny thing is that this is exactly what is happening on V5, with the problem on interrupts being left disabled after sprintf, running in another thread.
Here they save the INTCON register, then only a few lines later overwrite it with the value with the interrupt disabled, then when the code exits restore this wrong value. That on a basic PIC16....
There is a 'concept' hidden away here that the compiler/optimiser is treating the saving of registers in code flagged as 'their own', differently to the same task performed in 'user' code. With (perhaps) them specifying what register to use to save things, rather than using the automated behaviour. It then only takes somebody to get one setting wrong, and the same register gets re-used... Have to keep my 'eyes peeled' for similar behaviour elsewhere.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|