atan() auf einem AVR ohne Fließkommaarithmetik

Gast #2258514
Lesenswert?

Hallo,

mein Ziel ist es atan() zu berechnen, das ganze jedoch ohne 
Fließkommaarithmetik. Mir genügen zwei Nachkommastellen, also von 
-90.00° bis +90.00° in 0.01° Schritten.
Die in der avr-libc enthaltene atan() funktion verwendet 
Fließkommazahlen, und das wollte ich vermeiden.

Wie würdet ihr das lösen? Eine look up Tabelle im Flash? Oder eine 
Annäherung? (z.b. 
http://de.wikipedia.org/wiki/Arctan#N.C3.A4herungsweise_Berechnung )
Was haltet ihr hier für sinnvoll? Es kommt vor allem auf die 
Schnelligkeit an, RAM und Flash sind eigentlich genügend vorhanden (16kb 
bzw 128kb)

Vielen Dank für eure Hilfe
Gast #2258533
Lesenswert?

john schrieb:
> -90.00° bis +90.00° in 0.01° Schritten.

Dann wären es als LUT aber auch schon 180°/0,01° = 18.000 Werte!
arctan(pi/2) - artan(-pi/2)= 2,00 also müsste ein Byte pro Wert reichen, 
dann wären es 18kByte.

Evtl. könnte man es für verschiedene Bereiche linearisieren und dann je 
nach dem in was für einem Bereich man ist mit der dementsprechenden 
Gleichung berechnen?

Ein anderes Stichwort ist CORDIC.

Grüße
Gast #2258559
Lesenswert?

Hallo,


> Versuch es doch mal mit einer Taylorreihe.

Die Taylorreihe habe ich mir schon auf der Wikipedia angesehen, nur ich 
glaube diese Berechnung dauert auf einem AVR recht lange...


>> -90.00° bis +90.00° in 0.01° Schritten.
>
> Dann wären es als LUT aber auch schon 180°/0,01° = 18.000 Werte!
> arctan(pi/2) - artan(-pi/2)= 2,00 also müsste ein Byte pro Wert reichen,
> dann wären es 18kByte.

Eine Lookup Tabelle von 18kb wäre ja noch im Rahmen, ich habe sowieso 
recht viel Flash übrig...


> john schrieb:
>> Es kommt vor allem auf die
>> Schnelligkeit an,
>
> Nenn doch mal ne Hausnummer: 1ms 10ms 1µs ?

Das ganze (und noch einiges mehr) muss mit ca 400Hz laufen, das heißt 
ich habe ca 2.5ms zeit. Aber wie gesagt muss da noch einiges mehr 
berechnet werden... Takt des AVR ist 20MHz

Ich habe gerade mal auf Basis der Nährungsfunktionen in der Wikipedia 
eine kurze Funktion geschrieben:
1
int16_t _arctan(double x)
2
{
3
  if(-1 <= x && x <= 1)    // |x| <= 1
4
  {
5
    return (int16_t)((57.2958*x) / (0.28*x*x + 1));
6
  }
7
  else if(x > 1)    // x > 1
8
  {
9
    return (int16_t)(90 - (57.2958*x) / (x*x + 0.28));
10
  }
11
  else      // x < 1
12
  {
13
    return (int16_t)(-(90 - (57.2958*x) / (x*x + 0.28)));
14
  }
15
}}
16
}

Ganz um die Fließkommazahlen komme ich so aber nicht herum...
Wie finde ich heraus, wie viele Takte die Funktion benötigt?

Viele Grüße
Gast #2258562
Lesenswert?

john schrieb:
> Das ganze (und noch einiges mehr) muss mit ca 400Hz laufen, das heißt
> ich habe ca 2.5ms zeit. Aber wie gesagt muss da noch einiges mehr
> berechnet werden... Takt des AVR ist 20MHz

Dann hast Du genug Zeit, um die atan()-Funktion der .lib zu verwenden. 
Ich würde die Rechenzeit auf 200-300µs schätzen; probiere es doch 
einfach aus, wielange 10000 Aufrufe brauchen.
#2258573
Lesenswert?

Machs doch so wie schon geschrieben wurde:
Setze einen Pin auf HIGH, warte kurz, setze den Pin auf LOW und führe 
ein paar hundert mal atan() aus und setze danach den Pin gleich danach 
wieder auf HIGH. Die Zeit dazwischen kann man schön mit nem Oszi am Pin 
messen. Dann kannst du ja leicht berechnen wie lange du gebraucht hast 
für einen ablauf. Falls du kein Oszi hast: Mach es tausende bis 
hunderttausende Male, und stoppe die Zeit "per Hand".
Persönliche Seite #2258582
Lesenswert?

john schrieb:
> Hallo,
>
> mein Ziel ist es atan() zu berechnen, das ganze jedoch ohne
> Fließkommaarithmetik. Mir genügen zwei Nachkommastellen, also von
> -90.00° bis +90.00° in 0.01° Schritten.

Versteh ich nicht. Bei atan ist doch die Ausgabe ein Winkel, nicht die 
Eingabe.

Der Definitionsbereich von atan ist ganz |R.

> Wie würdet ihr das lösen? Eine look up Tabelle im Flash? Oder eine
> Annäherung?

Würd ich mal versuchen wie beschrieben in

http://www.mikrocontroller.net/articles/AVR_Arithmetik/Sinus_und_Cosinus_(Lineare_Interpolation%29

Das dauert weniger als 80 Ticks für Sinus (inclusive CALL+RET) und 
bringt einen Absolutfehler < 1e-4 bei 300 Byte Flash-Verbrauch.

Allerdings musst du das natürlich auf atan anpassen. Hinweis:
* atan' <= 1
* atan(-x) = -atan(x)
* atan(x) lässt sich einfach durch atan(1/x) darstellen

Die Berechnung braucht's alsu nur für Werte 0 <= x <= 1.

Ansonsten würd mir noch CORDIC einfallen.

XXX schrieb:
> Versuch es doch mal mit einer Taylorreihe.

Nö, Taylor ist notorisch schlecht, vor allem hat man mit Überläufen etc 
zu kämpfen.

Falls eine lineare Interpolation nicht ausreicht, nimm ein Polynom 
höheren Grades. Aber:

Die Auswertung machst du nicht als Anfang einer Potenzreihe, sondern 
du stellst das Polynom zur Basis der Bernsteinpolynome dar und wertest 
nach de-Casteljau aus.

Gute Stützstellen zu finden und gute Polynome sind ne Hausaufgabe ;-)
Gast #2258588
Lesenswert?

Johann L. schrieb:
> Gute Stützstellen zu finden und gute Polynome sind ne Hausaufgabe ;-)

Man kann davon ausgehen, dass die Compiler-Bauer ihre Hausaufgaben 
gemacht haben!

john schrieb:
> Wie probiere ich das am besten aus?

Schreib Dein Programm und bring es zum Laufen. Dann setzt Du Deine 
Zykluszeit von 2,5ms auf 1ms. Du wirst sehen, es funktioniert immer 
noch. Dann gehst Du wieder auf 2,5ms zurück und weißt, dass Du keine 
Probleme mit dem Timing hast.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren