Forum: Mikrocontroller und Digitale Elektronik PT1 Filter mit sehr kleine Koeffizienten und Rundung


von asd (Gast)


Lesenswert?

Hallo,

ich benutze eine PT1 Filter in dieser Art:

out = alt + k*(in-alt)

jedoch will ich eine relative lange Zeitkonstante (deswegen k bei 1e-6), 
und hab einen uC der float nur mit einfacher Genauigkeit macht (4 
Bytes).
Jetzt komme ich in Bereiche in denen der Rundungsfehler dazu führt dass 
ich einen gewissen Totbereich habe, in dem eine dauerhafte kleine 
Abweichung dazu führt dass out nicht auf in nachgezogen wird sondern 
unverändert bleibt weil
k*(in-alt)
so klein ist dass es beim aufaddieren zu "alt" einfach im Rundingsfehler 
untergeht und "alt" nicht verändert wird.

Gibt es eine Standardlösung für diese problem, in der Art dass man auf 
eine Literaturstelle verweisen kann als "state of the art"?

Vielen Dank,

von pt1checker (Gast)


Lesenswert?

asd schrieb:
> out = alt + k*(in-alt)

Das ist doch kein PT1 Filter. Ein PT1 Filter sieht folgendermassen aus

A/E = K / (1+Ts).

von Martin O. (ossi-2)


Lesenswert?

Die Standardlösung ist, mit höherer Wortbreite (Genauigkeit) zu rechnen, 
das hilft Dir aber nicht weiter. Wie wäre es, wenn Du Dein PT1 mit viel 
niedriger Samplerate laufen lässt, dann werden die Koeffizienten ja 
wieder größer.
z.B. Faktor 1024 langsamer: Immer 1024 Eingangswerte aufaddieren und das 
Ergebnis durch 1024 Teilen. Dann das Ergebnis ins langsame PT1 Filter 
einspeisen.

von donvido (Gast)


Lesenswert?

pt1checker schrieb:

> Das ist doch kein PT1 Filter.
> A/E = K / (1+Ts).

Wenn man deinen PT1 im zeitdiskreten Fall (z-Transformation) betrachtet, 
dann ergibt das einen IIR-Filter mit der Struktur des vom TO genannten 
PT1 Filters.

von PTx (Gast)


Lesenswert?

>Gibt es eine Standardlösung für diese problem, in der Art dass man auf
>eine Literaturstelle verweisen kann als "state of the art"?

MEINE Standardlösung:

Du hast beispielsweise Werte in ner 1ms Task und willst die über eine 
Stunde mitteln. Dann wären das ja 1:3600000

Deshalb nehm ich dann ein PT1 mit 1h Zeitkonstante, das mir alle 1sec 
einen Eingangswert nimmt, und schalte dem ein PT1 mit 1s Zeitkonstante 
und 1ms Abtastzeit davor.

Das schnelle PT1 Filter wirkt hier also eher wie ein Antialiasing 
Filter.
Da das eine aber 1000mal schneller als das 2. ist, ist es als System 
betrachtet kein PTn, sondern doch ein PT1 Filter.

ich habe jetzt also ein 1:1000 und 1:3600 Verhältnis und die 
Rundungsfehler sind vernachlässigbar.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Du kannst das Filter auch in Integer rechnen. Bei einem 32-Bit-Integer
mit Vorzeichen ist die Auflösung immerhin um den Faktor 128 höher, was
etwa 2 zusätzlichen Dezimalstellen entspricht.

Ob 32 Bit reichen, hängt davon ab, welche Auflösung die Ausgangswerte
haben müssen und wie fein k eingestellt werden können muss.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

asd schrieb:
> Gibt es eine Standardlösung für diese problem, in der Art dass man auf
> eine Literaturstelle verweisen kann als "state of the art"?
Rechne den Regler in long. Dann hast du immerhin 9 signifikante 
Stellen...

EDIT: Fenster zu lange offen --> Zweiter...  ;-)

: Bearbeitet durch Moderator
von Wolfgang (Gast)


Lesenswert?

asd schrieb:
> jedoch will ich eine relative lange Zeitkonstante (deswegen k bei 1e-6),
> und hab einen uC der float nur mit einfacher Genauigkeit macht (4
> Bytes).

Im Vergleich zu einem gleichlangen Integer, verschenkst du beim Float 
Genauigkeit, weil du weniger Bits zur Verfügung hast. Die Bits für den 
Exponent bringen dir bei Addition/Subtraktion nämlich nicht viel. Du 
verschenkst praktisch bis zu 8 Bit.

von Karl (Gast)


Lesenswert?

asd schrieb:
> Jetzt komme ich in Bereiche in denen der Rundungsfehler dazu führt dass
> ich einen gewissen Totbereich habe, in dem eine dauerhafte kleine
> Abweichung dazu führt dass out nicht auf in nachgezogen wird sondern
> unverändert bleibt weil k*(in-alt)
> so klein ist dass es beim aufaddieren zu "alt" einfach im Rundingsfehler
> untergeht und "alt" nicht verändert wird.
>
> Gibt es eine Standardlösung für diese problem, in der Art dass man auf
> eine Literaturstelle verweisen kann als "state of the art"?

Ist dein Problem dass die Zeitkonstante in dem Bereich generell nicht 
genau genug ist oder,  dass das Filter zwar in weiten Bereichen 
funktioniert aber eben den Endwert nie erreicht?

Für ersteres wurden viele Lösungen genannt. Such dir aus was am besten 
passt.
Das zweite Problem lässt sich mmn nicht so analytisch lösen. In integer 
inkrementiere ich deshalb den Ausgang oft in jedem Rechenschritt falls 
noch eine Abweichung besteht und der zweite Summand 0 ist. In float 
müsste man sich ein geeignetes Inkrement überlegen. Out * eps oder so. 
Auch die Prüfung auf 0 geht nicht so einfach.

von gedünsteter Quadtroll (Gast)


Lesenswert?

Ein Int32 sollte sicher genuegen. Falls nun der Anspruch kommt, alle ms 
zu sampeln, und trotzdem eine Zeitkonstante von 1 stunde zu haben kann 
man das Verfahren zweistufig laugffen laaasn, zB den ersten Tiefpass auf 
15 Sekund laufen zu lassen, und alle 15 sekunden den wert in die zweite 
Stufe zu uebernehmen. Ist dann ein Tiefpass zweiter Ordnung, aber was 
solls.

von asd (Gast)


Lesenswert?

Vielen Dank für die Hinweise. Ich werde den Filter zweistufig machen.

von Hermann (Gast)


Lesenswert?

asd schrieb:
> ich benutze eine PT1 Filter in dieser Art:
> out = alt + k*(in-alt)
> jedoch will ich eine relative lange Zeitkonstante (deswegen k bei 1e-6),
> und hab einen uC der float nur mit einfacher Genauigkeit macht

K=1e-6 ist ziemlich unsinnig. Du musst entweder die doppelte Genauigkeit 
nehmen oder die Abtastzeit verringern (wurde wohl schon gesagt).

Außerdem ist das kein PT1 was du da rechnest.
Die Berechnung eines PT1-Gliedes ist:
X[n] = X[n-1]*(1-Ta/T)+Y[n]*Ta/T
Dabei ist:
X[n] der neue Ausgangswert, X[n-1] der vorhergehende Wert
Y[n] der Eingangswert
Ta die Abtastzeit
T die Zeitkonstante

von Grundschullehrer (Gast)


Lesenswert?


von Hermann (Gast)


Lesenswert?

Grundschullehrer schrieb:
> Haha. Du solltest mal etwas Rechnen lernen

Dann noch mal für Grundschullehrer zum mitsprechen:

Neu = Alt*(1-k) + In*k
Mit k = Abtastzeit / Zeitkonstante

von uuu (Gast)


Lesenswert?

Siehe auch http://www.ibrtses.com/embedded/exponential.html

Man macht die Division am besten als 2^n, sprich nach rechts schieben.

von Grundschullehrer (Gast)


Lesenswert?

Hermann schrieb:
> Dann noch mal für Grundschullehrer zum mitsprechen:

Wer im Glashaus sitzt, ...


Hermann schrieb:
> Neu = Alt*(1-k) + In*k
> Mit k = Abtastzeit / Zeitkonstante

  Neu = Alt*(1-k) + In*k
  Neu = Alt - Alt*k + In*k            (ausmultipliziert)


asd schrieb:
> out = alt + k*(in-alt)

  out = alt + k*(in-alt)
  out = alt + k*in - k*alt            (ausmultipliziert)


Umgestellt und übereinander geschrieben:

  asd-Version:      out = alt + k*in - k*alt
  Hermann-Version:  Neu = Alt + In*k - Alt*k


Aus welchem Bundesland kommst Du?

von Hermann (Gast)


Lesenswert?

asd schrieb:
> out = alt + k*(in-alt)

Ok, das stimmt dann auch. Spart sogar etwas Rechenzeit. Subtraktion 
statt Multiplikation.

von Walter L. (Gast)


Lesenswert?

Hallo,
warum nicht mal mit der exakten Lösung des PT1-Gliedes arbeiten?

for(;;)
{
y = exp(-t/TAU)* y0 + (1-exp(-t/TAU)) * u;
y0=y;
}

u kommt vom ADC
TAU ist die Zeitkonstante, 1/TAU = OmegaEck, also der -3dB-Punkt.
t ist die Abtastzeit, die for-Schleife ist alle Abtastzeit zu 
durchlaufen.
y0 die Anfangsbedingung
y die Ausgangsgröße des PT1-Gliedes.
Damit reduziert sich die Berechnung auf add und mult -> DSP.
Dieses PT1-Glied ist solange stabil, wie EXP... mit gewünschter 
Auflösung berechnet wird.
VG Walter

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

PTx schrieb:
> Deshalb nehm ich dann ein PT1 mit 1h Zeitkonstante, das mir alle 1sec
> einen Eingangswert nimmt, und schalte dem ein PT1 mit 1s Zeitkonstante
> und 1ms Abtastzeit davor.
>
> Das schnelle PT1 Filter wirkt hier also eher wie ein Antialiasing
> Filter.

Das Antialiasing ist bei dieser Dimensionierung leider unvollständig.
Wenn das Eingangssignal bspw. eine Frequenzkomponente von 1Hz enthält,
wird diese durch das Filter mit τ=1s nur um 3db gedämpft. Durch die
anschließende Abtastung mit 1Hz kann deswegen ein unerwünschter
Gleichanteil entstehen, der durch das nachgeschaltete Filter trotz
seiner großen Zeitkonstante von 1h nicht weggefiltert wird.

Walter L. schrieb:
> warum nicht mal mit der exakten Lösung des PT1-Gliedes arbeiten?
>
> for(;;)
> {
> y = exp(-t/TAU)* y0 + (1-exp(-t/TAU)) * u;
> y0=y;
> }

Ersetze exp(-t/TAU) durch 1-k, y durch out, y0 durch alt, u durch in und
fasse auf der rechten Seite der Gleichung alle Summanden, die den Faktor
k enthalten zusammen, dann erhältst du

  out = alt + k*(in-alt)

also genau die Gleichung, die der TE verwendet hat und deren Berechnung
mit 32-Bit-Floats zu große Rundungsfehler aufweist.


Auch wenn ich persönlich für solche Anwendungen die Integer-Arithmetik
bevorzuge (konstante Auflösung über den gesamten Wertebereich, Fehler
besser abschätzbar, auf CPUs ohne FPU oft effizienter usw.), habe ich
mich mal in einem Berechnungsverfahren für 32-Bit-Float- Arithmetik
versucht, das denselben Filtertyp wie der des TE, aber mit geringeren
Abweichungen vom Ideal realisiert.

Das Filter des TE (zeitdiskreter Tiefpass 1. Ordnung) wird durch
folgende Formel beschrieben, die in jedem Zyklus neu berechnet wird:

  y' = y + k · (x - y)


Das Problem:

Macht das Eingangssignal bspw. einen Sprung und bleibt dann konstant auf
einem non 0 verschiedenen Wert x0 stehen, nähert sich das Ausgangssignal
asymptotisch diesem Wert. In jedem Zyklus wird der Ausgangswert y um
Δy=k·(x-y) erhöht. Je mehr sich y x0 annähert, desto kleiner wird Δy.
Irgendwann ist Δy relativ zu y so klein, dass die Addition auf Grund der
begrenzten Auflösung der Zahlendarstellung zu keiner Änderung von y mehr
führt. Zu diesem Zeitpunkt ist Δy zwar winzig, trotzdem hat y zum
Zielwert x0 noch einen Abstand von x0-y=Δy/k, was bei sehr kleinem k
(beim TE ist k=1e-6) ein nicht zu vernachlässigender Fehler sein kann.


Lösungsansatz mit Einschränkungen:

Das Problem stellt sich nicht, wenn x0=0 ist, sich y also asymptotisch
der 0 annähert. In diesem Fall reduziert sich obige Formel zu

  Δy = -k · y

Ist k größer ist als die relative Auflösung der Zahlendarstellung (bei
32-Bit-Floats 2**-23≈1.2e-7) führt dieses Δy bei der Addition solange
zu einer Veränderung von y, bis x=0 wird.

Bei einem bekannten Sprung des Eingangssignals könnte man sich diese
Eigenschaft zunutze machen, indem man statt des Ausgangssignals y dessen
Abstand u zu x0, also u=y-x0 berechnet. Substituiert man in der Formel
ganz oben y durch x0+u, erhält man

      x0 + u' = x0 + u + k · (x - (x0 + u))
  =>       u' =      u + k · ((x - x0) - u)

Die letzte Gleichung entspricht der ursprünglichen, nur dass y durch
u=y-x0 und x durch x-x0 ersetzt worden ist. Sowohl Eingangs- als auch
Ausgangssignal werden jetzt also nicht mehr absolut, sondern auf x0
bezogen betrachtet.

Diese Vorgehensweise hat eine gewisse Ähnlichkeit mit dem Kleinsignal-
verfahren in der Analogelektronik. x-x0 und u=y-x0 sind sozusagen die
Kleinsignale des Eingangs- und Ausgangssignals und x0 ist der Arbeits-
punkt, auf den sich beide Signale beziehen.

Aus u kann man mit y=x0+u jederzeit den Ausgabewert y berechnen, der
durch den kleinen Umweg bei der Berechnung jetzt tatsächlich gegen x0
konvergiert und nicht schon vorher auf einem anderen Wert stehenbleibt.

Einen wirklichen Nutzen bringt dieser Trick leider nur bei Eingangs-
signalen, die nach jeder Änderung über einen längeren Zeitraum konstant
sind, so dass man diesen konstanten Wert in der Berechnung jeweils als
Arbeitspunkt x0 verwenden kann. Zappelt das Eingangsignal x hingegen wie
wild hin und her, kann das Ausgangssignal y beliebig stark von x
abweichen, so dass u nicht klein genug ist, um die Auslöschungsfehler
wirksam zu bekämpfen.


Die eigentliche Lösung:

Was spricht aber dagegen, den Arbeitspunkt aus dem – auf Grund der
Tiefpassfilterung – deutlich weniger zappeligen Ausgangsignal zu
gewinnen?

Nichts.

Statt auf x0 aus dem Eingangssignal bezieht man die Signal also auf ein
y0, das man zu geeigneten Zeitpunkten aus dem Ausgangsignal pickt.
Anstelle von u=y-x0 nennen wir das Ausgangskleinsignal jetzt v=y-y0, und
die Berechnungsformel lautet jetzt:

  v' = v + k · ((x - y0) - u)

Da sich y nur langsam ändert, kann der Arbeitspunkt y0 über einen
längeren Zeitraum beibehalten werden. Erst wenn v einen gewissen Wert
übersteigt, so dass bei der Berechnung Auslöschungsfehler drohen, wird
y0 auf einen neuen Wert, nämlich das aktuelle y gesetzt. Damit wird v
wieder zu 0, und das Spiel beginnt von neuem.

Auf diese Weise wird der Fehler der berechneten Ausgangswerte
deutlich verringert, und das auch bei zappeligem Eingangsqsignal.

Bei einem über längere Zeit konstanten Eingangssignal kann das Ausgangs-
signal dennoch schon vor dem Erreichen der Asymptote stehenbleiben. Das
passiert immer dann, wenn v auf Grund von Rundungsfehlern gegen einen
von der eigentlichen Asymptote verschiedenen Wert konvergiert, und v
unterhalb der Grenze bleibt, an der y0 aktualisiert wird. Der dadurch
entstehende Fehler ist zwar deutlich kleiner als im ursprünglichen
Filteralgorithmus, trotzdem möchte man ihn natürlich gerne vermeiden. Am
einfachsten geschieht dies dadurch, dass y0 unabhängig vom Verlauf von v
immer nach Ablauf einer vorgegebenen Zeitspanne (bspw. 1τ) aktualisiert
wird.


Was bringt der ganze Aufwand nun an Genauigkeit?

Ich habe die obigen Überlegungen ebenso wie das Originalfilter des TE
und das zweistufige (kaskadierte) Filter von "PTx" in C-Funktionen
umgesetzt. Im C-Code wird v in ydelta und y0 in ybase umbenannt (viele
Programmierer bestehen ja auf etwas längere Variablennamen ;-)).

Die arithmetischen Ausdrücke in den drei Filterroutinen sind mit vielen
float-Casts gespickt, um die Rundungsfehler auf der Plattform des TE,
auf der es keine 64-Bit-Floats gibt, nachzubilden.

Ein weiteres Filter entspricht dem des TE, ist aber mit 64-Bit-Float-
Arithmetik implementiert, so dass die Fehler im Vergleich zu den anderen
Verfahren vernachlässigbar sind. Das Ausgangssignal dieses Filters wird
als Referenz bei der Bewertung der anderen Verfahren verwendet.

Als Eingangssignale für den Test wird ein Sprung-, ein Rechteck- und ein
Rauschsignal generiert (s. signalformen.png im Anhang). Die Berechnung
der Ausgangssignale der einzelnen Filter läuft über 20e6 Abtastzyklen.
Die jeweiligen Differenzen zum Referenzsignal sind in abweichungen.png
dargestellt.

Das Originalfilter weicht beim Sprungsignal um bis zu 0,03 von der
Referenz ab, was der Grund dafür war, dass der TE diesem Thread
gestartet hat. Auch beim Rechtecksignal ist der Fehler noch sehr groß.
Nur beim Rauschsignal werden zufriedenstellende Ergebnisse geliefert.

Das kaskadierten Filter liefert beim Sprung- und Rauschsignal nach dem
Einschwingen gute Ergebnisse, nur direkt nach dem Start bzw. nach dem
Sprung sind die Fehler größer. Beim Rechtecksignal zeigt das Filter aber
das oben beschrieben Abtastproblem, was in einem so großen Fehler
resultiert, dass die Kurve nicht zusammen mit den beiden anderen in
einem Digramm dargestellt werden konnte.

Bei dem Filter mit der hier beschriebenen dynamischen Verschiebung des
Arbeitspunkts sind die Fehler bei den Tests so klein, man sogar die
Granularität der 32-Bit-Float-Darstellung erkennen kann. Das bedeutet,
dass man (zumindest bzgl. der getesteten Signalformen) nicht mehr viel
optimieren kann.

Ich kann aber nicht garantieren, dass die Fehler dieses Verfahrens bei
beliebigen anderen Signalformen ebenso klein sind. Genauso wie das fiese
Rechtecksignal die ansonsten guten Ergebnisse des kaskadierten Filters
zunichte macht, gibt es vielleicht auch für mein Verfahren so ein
Killersignal.

Es war für mich zwar spannend, so etwas auszudenken, zu testen und zu
vergleichen, im realen Leben fühle ich mich aber mit einem einfacheren
Verfahren, das die gewünschte Genauigkeit mit geeigneteren Datentypen
(32- oder 64-Bit-Integer oder 64-Bit-Float) erzielt, deutlich wohler.
Wer aber – aus welchen Gründen auch immer – auf 32-Bit-Float-Arithmetik
besteht, für den kann dieses Verfahren eine gute Alternative sein.

von Walter L. (Gast)


Lesenswert?

@yalu

>In jedem Zyklus wird der Ausgangswert y um
>Δy=k·(x-y) erhöht. Je mehr sich y x0 annähert, desto kleiner wird Δy.
>Irgendwann ist Δy relativ zu y so klein, dass die Addition auf Grund der
>begrenzten Auflösung der Zahlendarstellung zu keiner Änderung von y mehr
>führt.

Die Subtraktion ergibt schon eine Fehlerquelle.
In der exakten Lösung gibt es keine Subtraktion.

VG Walter

von Peter D. (peda)


Lesenswert?

asd schrieb:
> Jetzt komme ich in Bereiche in denen der Rundungsfehler dazu führt dass
> ich einen gewissen Totbereich habe

Mache die Rechnung in int, dann hast Du keinen Totbereich:
out = sum / k
sum = sum - out + in

Bei einer konstanten Folge "in" wird nach endlicher Zeit immer "out" == 
"in".
Man nimmt für k auch gerne 2-er Potenzen, dann geht schieben statt 
dividieren.

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.