Forum: Mikrocontroller und Digitale Elektronik Sinus und Cosinus in einem µC ohne Fließkomma berechnen


von Bob (Gast)


Lesenswert?

Hallo,
da ich für ein Problem den Sinus und Cosinus für beliebige Winkel in 
hundertstel Grad berechnen müsste und mir da die Wertetabelle zu groß 
wird (144kB), suche ich nach einer Möglichkeit den Sinus und Cosinus 
"live" zu berechnen. Also wie der Taschenrechner auch.

Kann mir da jemand Tipps oder Rechenformeln geben?

von Jörg B. (manos)


Lesenswert?

Wie sieht es denn mit den Hochsprachen aus? Hat C und Basic in der 
Richtung nichts?

P.S. Welcher µC eigentlich?

von Stefan B. (stefan) Benutzerseite


Lesenswert?


von Gast (Gast)


Lesenswert?

Schau dir eine C Bibliothek an.

von Kai G. (runtimeterror)


Lesenswert?

Tabelle einfach kleiner machen und linear oder kubisch interpolieren. 
Außerdem reicht es eine viertel Periode in der Tabelle zu halten.

Je nach Qualitätsanforderung ist auch eine Parabelannäherung leicht und 
schnell zu realisieren. Vielleicht ist die Taylorreihenentwicklung der 
Sin/cos-Funktion eine Option.

Ansonsten halt CORDIC.

von Martin Schneider (Gast)


Lesenswert?

Zwei Ideen:

1. Du brauchst ja nur den Sinus von 0..90° in der Tabelle zu halten, 
alles
andere ergibt sich aus Quadrantenumrechnung und Verschiebungen.

2. Überlege, welche Genauigkeit notwendig ist. Man kann auch mit 
Reihenentwicklung oder wenigen Stützstellen und Interpolation dazwischen
arbeiten.

Ahoi, Martin

von Micha (Gast)


Lesenswert?

"Du brauchst ja nur den Sinus von 0..90° in der Tabelle zu halten,
alles andere ergibt sich aus Quadrantenumrechnung und Verschiebungen"

Da kann man noch weiter sparen, es reicht IMHO 0-45° ;-)

von Bob (Gast)


Lesenswert?

ja an eine C-Code Implementierung der Taylor Reihe habe ich gedacht.
Die Idee in der C Bibliothek nachzusehn ist natürlich nicht schlecht. 
Lokal auf meinem Rechner hab ich mal in den math.c files gesucht und 
nichts gefunden. Aber mal sehn was sich noch finden lässt.

von Kai G. (runtimeterror)


Lesenswert?

>Da kann man noch weiter sparen, es reicht IMHO 0-45° ;-)

Und wie kommst du ohne übertriebenen Rechenaufwand an den Rest?

von ... (Gast)


Angehängte Dateien:

Lesenswert?

1
//Aufgabe 2
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <math.h>
5
#define PI 2.0*asin(1.0)
6
#define NEGATIV 1
7
#define POSITIV 0
8
9
//****Funktion um den Sinus mittels Taylorreihe mit n gliedern zu berechnen****
10
double taylorsin(double x,int n){
11
  int i;
12
  double sum;
13
  sum = 1.0;
14
  for(i=n; i >1 ; i--){
15
    sum = 1.0 - x*x*sum/(double)((2*i-2)*(2*i-1)); //numerisch stabil
16
  }
17
  sum *=x;
18
  return (sum);
19
}
20
21
22
//****Funktion mein_sin (für beliebige x aus R den Sinus zu berechnen)****
23
double mein_sin(double x){
24
  char sign=POSITIV;
25
  double erg;
26
  if (x < 0){
27
    sign = NEGATIV;     //Symmetrieausnutzung Vorzeichenwechsel
28
    x *= -1.0;         //Vorzeichen merken
29
  }
30
31
  x=fmod(x,2*PI);
32
                  //jetzt intervall 0 bis 2PI nur pos
33
  if (x > PI){         //Vorzeichenwechsel ist notwendig
34
                //z.B. 3PI/2 (y = -1) PI subtrahieren = PI/2 (y = 1)
35
    x -= PI;
36
    sign ^= 1;
37
  }
38
  if (x > PI/2.0) x = PI - x; //Symmetrieausnutzung
39
  if(x < PI/6.0) erg = taylorsin(x,8); // ist x in Bereich von 0 bis PI/ 6
40
  //einfach taylorsin aus
41
  else if (x < PI/3.0){
42
    x-=PI/6.0; // ist x in Bereich über PI/6 und unter PI/3
43
      // bringe x in Bereich unter PI/6
44
      x = taylorsin(x,8);
45
    erg = 0.5*(x * sqrt(3.0) + sqrt(1.0 - x*x));
46
  }
47
  else{
48
    x-=PI/3.0; //siehe oben
49
    x = taylorsin(x,8);
50
    erg = 0.5*(x + sqrt(3.0) * sqrt(1.0 - x*x));
51
  }
52
  if(sign!=POSITIV){
53
    erg *= -1;
54
  }
55
  return erg;
56
}

von der mechatroniker (Gast)


Lesenswert?

Bei 0-45° benötigte er dann sin UND cos, es liefe auf denselben Aufwand 
hinaus.

Oder sin(x) = sqrt( 1 - (sin(90°-x))^2) ausnutzen. Aufgrund des Aufwands 
mit der Wurzel aber auch Käse.

von der mechatroniker (Gast)


Lesenswert?

Ich sehe gerade, genau das wurde gemacht (mit den Wurzeln) denk na ja, 
wird wohl funktionieren.

von 6632 (Gast)


Lesenswert?

Den Cosinus kann man aus dem Sinus durch ableiten errechnen. Oder durch 
integrieren.

von Falk B. (falk)


Lesenswert?

@ 6632 (Gast)

>Den Cosinus kann man aus dem Sinus durch ableiten errechnen. Oder durch
>integrieren.

Was ja auch alles sehr einfach und schnell geht . . .
Leute gibts.

von Simon K. (simon) Benutzerseite


Lesenswert?

... wrote:
> #define PI 2.0*asin(1.0)

Das sieht aber mehr als gefährlich aus. Mach da wenigstens noch Klammern 
drumherum.

von Carsten (Gast)


Lesenswert?

Wie wärs mit einem µC der sowas als Co-Prozessor onChip hat.
z.B. Infineons XC886CM.

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Laut
http://www.eng.monash.edu.au/uicee/gjee/vol8no3/Risse.pdf
verwenden Taschenrechner und x86-Prozessoren den CORDIC-Algorithmus, 
auch wenn es in der Literatur immer wieder anders behauptet wird.
"Seit 1980 implementiert der 8087, der mathematische Coprozessor des 
8086, in Hardware eine Reihe von Konstanten, trigonometrischen und 
logarithmischen Funktionen ... Die Indizien sind erdrückend: simple 
Taylor- Approximation allein kann diesen Implementierungen nicht 
zugrunde liegen!"

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Der Hauptvorteil vom CORDIC ist, dass man keine Multiplikationen 
braucht, deshalb wird er gerne für Hardwareimplementierungen verwendet. 
Wenn man aber sowieso schon einen Multiplizierer im Controller hat ist 
Cordic nicht mehr unbedingt die beste Wahl, ich würde auch mal 
Polynomapproximation mit Horner-Schema ausprobieren. Wenn die 
Geschwindigkeit keine Rolle spielt ist es natürlich das einfachste die 
sin-Funktion aus der Compilerbibliothek zu benutzen.

von Walter S. (waldo)


Lesenswert?

Hallo,
könnte man das wie folgt machen:

Man legt eine Tabelle für sin und cos wie folgt an:

phi_tab  sin_tab       cos_tab
------------------------------------
PI/4     sin(PI/4)     cos(PI/4)
PI/16    sin(PI/16)    cos(PI/16)
...
PI/16384 sin(PI/16384) cos(PI/16384)

mit den Additionstheoremen für sin und cos:
sin(x+y) = sin(x)cos(y) + cos(x)sin(y)
cos(x+y) = cos(x)cos(y) - sin(x)sin(y)

kann man sich an den Winkel heranrechnen und ist so theoretisch exakt! 
Man kann die Genauigkeit über die Länge der Tabelle bestimmen.

berechne sin(phi) und cos(phi)

Start:

sin = 0
cos = 1
xphi = 0

Schleife i über die Tabelle:
    xphi= xphi + phi_tab(i)
    if xphi <= phi
        temp = sin*cos_tab(i) + cos*sin_tab(i)
        cos  = cos*cos_tab(i) - sin*sin_tab(i)
        sin  = temp
     else
        xphi = xphi - phi_tab(i)
     endif
ende Schleife

Mit einem Trick kann man etwas schneller sein:

Man Skaliert den Winkel auf 90Grad (PI/2). Dann sind die Einsen im 
Bitmuster für den Winkel die Tabelleneiträge, die für die Näherung 
gebraucht werden:

56,25Grad = 45Grad + 11,25Grad = 10100..0

also die Zeilen für PI/4 und PI/16.

Das ganze muss man sich natürlich mal in Fixpunktarithmetik anschauen.

Waldo

von Jojo (Gast)


Lesenswert?

Hallo!

Wollte für meine Frage kein extra Thema aufmachen, und ich glaube es 
passt hier ganz gut rein... Im Großen und Ganzen geht es auch wieder um 
die Berechnung von böden trigonometrischen Funktionen:
In meinem Programm muß ich öffters mit diesen Funktionen rechnen. Und 
aufgrund der kritischen Rechenzeit kann ich das nicht "online" machen. 
Darum habe ich für Sinus und Cosinus jeweils schon Tabellen mit 
stützwerten abgelegt, was dann so aussieht:

for(i = 0; i <= 512; i++)
{
cosinus[i] = ( (int) (128 + 128 * cos((i  2  3.1416) / 512) * 0.99) );
sinus[i] = ( (int) (128 + 128 * sin((i  2  3.1416) / 512) * 0.99) );
}

Im weitern Verlauf werde ich nur mit Festkommazahlen rechnen. Aber das 
soll nicht eure Sorge sein ;) . Ich hab nur grad irgendwie ein Problem 
mit der Formel für den ARCUS-Sinus und ARCUS-Cosinus. Für die will ich 
auch Tabellen anlegen, aber ich komm nicht auf die Formel...

Kann mir die vielleicht jemand unter die Arme greifen?

Danke,
vG,
Jojo

von Karl H. (kbuchegg)


Lesenswert?

Jojo schrieb:

> auch Tabellen anlegen, aber ich komm nicht auf die Formel...

Wie wärs mit: die vorhandene Umstellen

Du hast

  CosV = 128 + 128 * cos( Winkel )

  CosV - 128 = 128 * cos( Winkel )

  ( CosV - 128 ) / 128 = cos( Winkel )

  acos( ( CosV - 128 ) / 128 ) = Winkel


(Mit den 0.99 in deiner Formel hast du dir keinen Gefallen getan)

von Jojo (Gast)


Lesenswert?

Hallo nochmal und danke für die rasche Antwort!

Hm, jetzt, wo ich das so sehe hätte ich da auch selbst drauf kommen 
können ;) ... aber trotzdem danke, manchmal hab ich einfach ein Brett 
vor dem Kopf.

Das mit dem Faktor 0,99 hab ich aus einer Formel, die ich im NEtz 
gefunden habe. Aber richtig, ob ich sie benutze oder weglasse macht 
keinen signifikanten Unterschied.

von Karl H. (kbuchegg)


Lesenswert?

Jojo schrieb:

> Das mit dem Faktor 0,99 hab ich aus einer Formel, die ich im NEtz
> gefunden habe. Aber richtig, ob ich sie benutze oder weglasse macht
> keinen signifikanten Unterschied.

Doch, das tut es :-)
Aber es löst ein Problem auf eine etwas seltsame Weise :-)
Mal angenommen es wäre nicht da

Der cos eines Winkels kann minimal -1 sein

128 + ( 128 * -1 ) = 0

aber: er kann maximal +1 sein

128 + ( 128 * 1 ) = 256

Und 256 ist nun mal zu gross für ein Byte :-)

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.