Guten Morgen,
ich möchte gerne von einem kontinuierlich ansteigendem Eingangssignal
den Sinus (sowie den Cosinus) bestimmen.
Das Eingangssignal kann man sich vereinfacht als Rampenfunktion bzw.
eine Gerade mit der Steigung m vorstellen.
Aus Rechenzeitgründen möchte ich auf die Sinus und Cosinus Berechnung
verzichten und stattdessen eine Tabelle verwenden.
Derzeit simuliere ich den Aufbau in Matlab.
Bei den Simulationen stellt sich jedoch immer ein periodischer Fehler
ein.
Ich habe der Einfachheit halber zunächst einen einfachen Vektor
angenommen mit 628 Elementen (1-629). Das sind die gerundeten Argumente
von 0-2*pi*100).
Ich verwende folgenden Code:
1
2
% Signal nach Integer transformieren
3
signal = signal * 100;
4
signal = round(signal);
5
6
if(signal < 0)
7
while(signal < 0)
8
signal = signal + 628;
9
end
10
end
11
if(signal >= 628)
12
while(signal >= 628)
13
signal = signal - 628;
14
end
15
end
16
17
% Offset für Array Index addieren (Wertebereich von 0-628 auf 1-629
18
% verschieben)
19
signal = signal + 1;
20
21
% Ausgabe des Signals
22
signal_out = signal;
23
24
% Abfrage des Tabellenwertes
25
sind = sintab(signal);
26
cosd = costab(signal);
sintab und costab sind dabei die Vektoren mit den Funktionswerten.
Jetzt meine Frage: Wie lässt sich der Fehler am besten eliminieren?
Der Fehler ist eine Verschiebung der sin/cos-Werte auf der x-Achse. Bei
jeder Periode vergrößert sich die Verschiebung.
Jemand eine Idee? Ich schätze, es liegt daran, dass ich falsch auf die
Vektor-Elemente zugreife.
Bin für jede Hilfe dankbar!
-Kai
Wie hast du den die 'vectoren' definiert?
Dein größer werdender Fehler mit jeder Periode wird wahrscheinlich
darin begründet sein, dass 2*PI * 100 nun mal nicht 628 sind,
sondern ein klein wenig mehr.
Übrigens: Die If-Abfragen hier
if(signal < 0)
while(signal < 0)
sind sinnlos. Wenn signal nicht kleiner als 0 ist, dann
würde auch die while Schleife nicht betreten werden. Es reicht
also völlig aus
while(signal < 0)
signal = signal + 628;
end
while(signal >= 628)
signal = signal - 628;
end
Die Vergleiche auf Gleichheit machen mich etwas stutzig.
Dir ist schon klar, dass du bei einem Wertebereich 0 bis 628
(jeweils inklusive) in Summe 629 Werte hast? Überleg es dir mal
mit kleineren Zahlen. Wenn du einen Wertebereich 0 bis 5 hast
(jweiles inklusive der Grenzen), dann sind das
0, 1, 2, 3, 4, 5
6 Werte.
Bei mir funktioniert diese while >Konstruktion nur, wenn "signal" für
alle Werte die Abfragebedingung erfüllt... und dann wird der ganze
Vektor manipuliert. Ich weiß nicht, ob das so gewollt war, oder ob du
nur einzelne Werte (Ausreißer nach oben oder unten) korrigieren
wolltest.
test = zeros(1,10)
test(4)=4
while test<4
test=test+55
end
test bleibt 0 0 0 4 0 0 0 0 0 0
while test<5
test=test+55
end
test wird 55 55 55 59 55 55 55 55 55 55
Die Abfrage der Tabellen finde ich auch etwas merkwürdig.
Du übergibst den Wert als Index... bin mir nicht sicher ob das wirklich
funktioniert. Müsste man deine Vektoren kennen.
Karl heinz Buchegger wrote:
> Wie hast du den die 'vectoren' definiert?
x = [0:1:628]';
sintab = sin(x/100);
Damit enthält der Vektor die Funktionswerte vom Sinus (in Abhängigkeit
vom Index, allerdings mit "mangelhafter" Genauigkeit.
> Dein größer werdender Fehler mit jeder Periode wird wahrscheinlich> darin begründet sein, dass 2*PI * 100 nun mal nicht 628 sind,> sondern ein klein wenig mehr.
Wenn das der Fehler ist, wie kann man den am besten beheben? Wenn ich
noch mehr Nachkommastellen einbeziehe, bleibt er ja weiterhin bestehen.
> Übrigens: Die If-Abfragen hier>> if(signal < 0)> while(signal < 0)>> sind sinnlos. Wenn signal nicht kleiner als 0 ist, dann> würde auch die while Schleife nicht betreten werden. Es reicht> also völlig aus>> while(signal < 0)> signal = signal + 628;> end>> while(signal >= 628)> signal = signal - 628;> end
Stimmt, war ein Denkfehler meinerseits.
> Die Vergleiche auf Gleichheit machen mich etwas stutzig.> Dir ist schon klar, dass du bei einem Wertebereich 0 bis 628> (jeweils inklusive) in Summe 629 Werte hast? Überleg es dir mal> mit kleineren Zahlen. Wenn du einen Wertebereich 0 bis 5 hast> (jweiles inklusive der Grenzen), dann sind das> 0, 1, 2, 3, 4, 5> 6 Werte.
Das stimmt. Matlab indiziert die einzelnen Elemente von
Vektoren/Matrizen etc. beginnend mit 1. Das 0. Element existiert also
nicht.
Da die sin-Werte von 0 und 2pi gleich sind, reicht es also nur einen der
beiden zu betrachten.
Meine Idee: Sobald das Signal mindestens den Wert 2pi erreicht, ziehe
ich 2pi (hier 628) ab.
Der Vektor hat 628 Elemente, da es das 0. nicht gibt.
Aus diesem Grund hab ich vor der Abfrage eine 1 zu dem Signal addiert.
Damit entspricht der Signalwert 0 dem Vektorindex 1, 628 ergo 629.
Vielleicht gibts auch noch einen sinnvolleren Weg als meinen? Hab bis
jetzt nur noch nichts passendes gefunden..
...
Gibt es vielleicht noch eine andere Möglichkeit, den Sinus von einem
Signal zu bestimmen, welches den Wertebereich von 0-2pi überschreitet?
Bei dem Verfahren sollte natürlich kein Fehler auftreten, was jedoch
nicht realisierbar ist. Der günstigeste Fall wäre ein statischer, oder
bestimmbarer Fehler, den man nachträglich rausrechnen kann.
Bin für jede Hilfe und Anregungen sehr dankbar.
Wie oft kann es denn zum Überschlag kommen (Wertebereich des Eingangs)?
Hier würde ich eine Multiplikation des Wertes mit einer Konstanten,
sodass der Tabellenzugriff bei 2*pi wieder genau trifft, vorschlagen.
Berechnungen natürlich mit FixedPoint-Arithmetik.
z.B. so:
Eingangswert soll sich alle 628,318530 Werte wiederholen.
Sinus-Tabelle mit 512 Einträgen (damit der Faktor kleier als 1 bleibt).
Eingangswert mit 512/628,31853=0,8148733 fixedpoint multiplizieren.
Vom Ergebnis nur die unteren 9 Bits verwenden als Pointer in die
Sinus-Tabelle.
Beispiel:
Zum Nachrechnen in Floating-Point:
Eingangs-Wert ist 2345 (0x0929)
*0,8148733 = 1910,8779087 gerundet 1911
modulo 512 = 375--> den 375. Wert der Sinus-Tabelle verwenden.
und nun in fixed und hex, damit sich der µC leichter tut:
0929 * D09C = 0776E4FC (nur die oberen 16 Bits interessieren uns)
0777 (gerundet = bit 15 dazuaddiert)
andiert mit 01FF (gleichbedeutend mit modulo 512)
------
0177 (ist 375 dez)
Jetzt nehmen wir zur Kontrolle ein Vielfaches (z.B. 100 x) von
628,31853, das sollte ja wieder zum 0. Eintrag der Tabelle führen.
628,31853 * 100 = 62831,853 gerundet 62832 0xF570
F570 * D09C = C8009040
c801 gerundet
andiert mit 01ff
------
0001 naja, nach 100 Überschlägen greift er um 1
daneben.
Wenn Dich das stört, musst Du mit mehr Bits rechnen.
Ist das in etwa, was Du wolltest? Wenn nicht, hab ich nicht verstanden,
worauf es Dir ankommt. Dann bitte nochmal genauer erklären ;-)
2 Sachen zum Nachtrag:
D09C ist 0,8148733 * 65536 = 53403,537154
Da man bei dieser Berechnung aufrundet, kann man zum Ausgleich bei der
zweiten Berechnung (Multiplikation) abrunden, was im obigen Beispiel zum
richtigen Ergebnis 0 führen würde.
Du kannst die Länge der Sinus-Tabelle beliebig machen, es bietet sich
jedoch ein Vielfaches von 2 an, da man dann die Modulo-Funktion durch
einen einfachen AND bewerkstelligen kann.
Erzähl doch bitte mal, wofür Du das brauchst...
Vielleicht gibt es eine noch einfachere Lösung.
Da wurde einiges gelabbert. Also. Eine sinustabelle ist immer 2^N lang.
also 256, 512, 1024, 2048, 4096. Am einfachsten ist 256. Das entspricht
dann 2Pi.
eProfi wrote:
> Wie oft kann es denn zum Überschlag kommen (Wertebereich des Eingangs)?>> Hier würde ich eine Multiplikation des Wertes mit einer Konstanten,> sodass der Tabellenzugriff bei 2*pi wieder genau trifft, vorschlagen.>> Berechnungen natürlich mit FixedPoint-Arithmetik.>> z.B. so:> Eingangswert soll sich alle 628,318530 Werte wiederholen.> Sinus-Tabelle mit 512 Einträgen (damit der Faktor kleier als 1 bleibt).>> Eingangswert mit 512/628,31853=0,8148733 fixedpoint multiplizieren.> Vom Ergebnis nur die unteren 9 Bits verwenden als Pointer in die> Sinus-Tabelle.>> Beispiel:> Zum Nachrechnen in Floating-Point:> Eingangs-Wert ist 2345 (0x0929)> *0,8148733 = 1910,8779087 gerundet 1911> modulo 512 = 375--> den 375. Wert der Sinus-Tabelle verwenden.>> und nun in fixed und hex, damit sich der µC leichter tut:> 0929 * D09C = 0776E4FC (nur die oberen 16 Bits interessieren uns)> 0777 (gerundet = bit 15 dazuaddiert)> andiert mit 01FF (gleichbedeutend mit modulo 512)> ------> 0177 (ist 375 dez)>> Jetzt nehmen wir zur Kontrolle ein Vielfaches (z.B. 100 x) von> 628,31853, das sollte ja wieder zum 0. Eintrag der Tabelle führen.>> 628,31853 * 100 = 62831,853 gerundet 62832 0xF570> F570 * D09C = C8009040> c801 gerundet> andiert mit 01ff> ------> 0001 naja, nach 100 Überschlägen greift er um 1> daneben.> Wenn Dich das stört, musst Du mit mehr Bits rechnen.>> Ist das in etwa, was Du wolltest? Wenn nicht, hab ich nicht verstanden,> worauf es Dir ankommt. Dann bitte nochmal genauer erklären ;-)
Das hört sich schonmal nach einem recht sinnvollen Lösungsweg an. Werd
das gleich mal programmieren und testen.
Wenn ich deine Angaben jetzt richtig verstanden habe, ist mit einem
Fehler von 1/512 des Funktionswertes im Wertebereich von 0-200*pi zu
rechnen. Ich denke, das ist ein akzeptabler Wert.
Das ganze benötige ich für einen Simulator, der auch im Dauerbetrieb
eingesetzt wird.
EDIT: Und das Eingangssignal kann sich in bestimmten
Betriebsfällen/Simulationsfällen kontinuierlich während des
Dauerbetriebs erhöhen.
EDIT-ENDE
Aus diesem Grund sollte der Fehler statisch bleiben.
Mal sehen, was bei den Tests gleich rumkommt.
Vielen Dank
Perfekt!
Das Verfahren funktioniert.
Mit einer Tabellenauflösung von 512 Elementen bekomme ich einen
periodisch schwingenden Fehler von 0 - 0.5%.
Bei 1024 Elementen ist der Fehler maximal 0.3%.
Also insgesamt verhält sich der Weg statisch!
Vielen Dank nochmal!
-Kai