Forum: Mikrocontroller und Digitale Elektronik atan() auf einem AVR ohne Fließkommaarithmetik


von john (Gast)


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

von XXX (Gast)


Lesenswert?

Hallo

Versuch es doch mal mit einer Taylorreihe.

Gruß
Joachim

von Iadnu (Gast)


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

von Willi (Gast)


Lesenswert?

john schrieb:
> Es kommt vor allem auf die
> Schnelligkeit an,

Nenn doch mal ne Hausnummer: 1ms 10ms 1µs ?

von john (Gast)


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

von Willi (Gast)


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.

von john (Gast)


Lesenswert?

Wie probiere ich das am besten aus?
Einen Timer so einstellen, dass er jede µs um eins hochzählt, und danach 
schauen wie weit er gezählt hat?

von Klaus W. (mfgkw)


Lesenswert?

z.B. so, ja

von Johannes O. (jojo_2)


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

von Johann L. (gjlayde) Benutzerseite


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

von Klaus W. (mfgkw)


Lesenswert?

john schrieb:
> Ganz um die Fließkommazahlen komme ich so aber nicht herum...

Prinzipiell kann man ziemlich alles auch mit Festkommazahlen machen; 
zumindest solange man die jeweiligen Wertebereiche im Blick hat.

von Horst H. (horha)


Lesenswert?

Hallo,

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=530476
atan ~2100-2400 cycles
oder mit Unterscheidung zwischen mit oder ohne Hardwaremultiplizierer
http://www.nongnu.org/avr-libc/user-manual/benchmarks.html

von Willi (Gast)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.