Forum: Mikrocontroller und Digitale Elektronik Filter ohne Wertverlust


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe eine Reihe aufeinanderfolgender Integer-Werte, u[i], die ich 
tiefpassfiltern will, d.h. die Anstiegs- und Abstiegsgeschwindigkeit 
soll begrenzt werden. Allerdings soll das Integral über alle Werte 
erhalten bleiben. Oder im diskreten Fall: Die Summe.

Wenn der Eingang z.B. so aussähe:

u[] = {0, 0, 10, 100, 40, 11, 0, 0, 0, 0, 0}

und ein Anstieg von 40 erlaubt ist, könnte der Ausgang y[i] so aussehen:

y[] = {0, 0, 10, 50, 90, 50, 10, -30, -9, 0, 0}

Das zu implementieren sollte keine Schwierigkeit sein. Man benötigt im 
einfachsten Fall eine Akkumulator-Variable und ein paar Regeln für den 
Ausgang. Allerdings stört der negative Überschwinger etwas.

Deswegen zwei Fragen:
a) Gibt es für diese Klasse von Filtern einen Namen?

b) Gibt es in dieser Klasse von Filtern Varianten ohne Überschwinger?

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Walter T. schrieb:
> Deswegen zwei Fragen:
> a) Gibt es für diese Klasse von Filtern einen Namen?

Was würde das helfen?

Auf jeden Fall ist es kein einfaches Tiefpassfilter, dass durch eine 
einfache Zeitkonstante beschrieben werden kann. Slew Rate Begrenzer 
würde die Sache vielleicht noch halbwegs beschreiben. Bei einem Clipping 
der Slew Rate kannst du aber nicht erwarten, dass das Integral 
unverändert bleibt.
Stell dir einfach ein Sägezahnsignal vor. Die langsame Flanke kommt 
unverändert durch, auf der steilen Flanke wird abgeschnitten. Das Filter 
wird sogar nichtlinear, i.e. die Übertragungsfunktion hängt von der 
Signalamplitude ab.

von Walter T. (nicolas)


Lesenswert?

Wolfgang schrieb:
> Auf jeden Fall ist es kein einfaches Tiefpassfilter, dass durch eine
> einfache Zeitkonstante beschrieben werden kann

Da bin ich mir nicht so sicher. Es sieht nach einem direkten Komplement 
zum I-Glied aus. Ein gleitender Mittelwert ohne Rundungsfehler hätte 
diese Erhaltungseigenschaft übrigens auch.

Wolfgang schrieb:
> Was würde das helfen?

Da sich schon viele schlaue Köpfe mit Signalverarbeitung und 
Regelungstechnik befasst haben, und viele davon Bücher und Artikel 
geschrieben haben, um ihr Wissen zu teilen, gilt es diesen Schatz zu 
finden. Das geht am besten dann, wenn man den Namen dessen weiß, was man 
sucht.

: Bearbeitet durch User
von Egon D. (Gast)


Lesenswert?

Walter T. schrieb:

> ich habe eine Reihe aufeinanderfolgender Integer-Werte,
> u[i], die ich tiefpassfiltern will, d.h. die Anstiegs-
> und Abstiegsgeschwindigkeit soll begrenzt werden.
> Allerdings soll das Integral über alle Werte erhalten
> bleiben. Oder im diskreten Fall: Die Summe.

Das wäre ein Allpass: Verändert nur die Phase, nicht aber
die Amplitude der Spektralkomponenten.


> Wenn der Eingang z.B. so aussähe:
>
> u[] = {0, 0, 10, 100, 40, 11, 0, 0, 0, 0, 0}

Summe: 161


> und ein Anstieg von 40 erlaubt ist, könnte der Ausgang
> y[i] so aussehen:
>
> y[] = {0, 0, 10, 50, 90, 50, 10, -30, -9, 0, 0}

Summe: 171 :)


> Das zu implementieren sollte keine Schwierigkeit sein.
> Man benötigt im einfachsten Fall eine Akkumulator-Variable
> und ein paar Regeln für den Ausgang. Allerdings stört der
> negative Überschwinger etwas.
>
> Deswegen zwei Fragen:
> a) Gibt es für diese Klasse von Filtern einen Namen?

Welche "diese"?

Ich fürchte, dass Deine Randbedingungen bis jetzt noch
zu viel Spielraum lassen. Ein Filter, das die Summe der
Eingangswerte unverändert lässt, lässt einfach nur den
Gleichanteil unverändert, und das leistet jeder Tiefpass,
der bei f=0 die Verstärkung "Eins" hat.

Nun enthält Deine Beispielfolge keine negativen Werte.
Was soll dann passieren? Wäre eine Umsetzung von
{0,50,100,0,-50,-100} auf {0,0,0,0,0,0} akzeptabel? Die
Summe der Werte bleibt ja erhalten.

Ist der Gleichanteil das richtige Maß, oder soll nicht
vielmehr die Summe der Quadrate (=die Energie) erhalten
bleiben?


> b) Gibt es in dieser Klasse von Filtern Varianten ohne
> Überschwinger?

Im stetigen sind die (unüblichen) Gauss-Filter ganz und
die Bessel-Filter nahezu ohne Überschwinger. M.W. ist
auch der ungewichtete gleitende Mittelwert frei von
Überschwingern.

Ich sehe aber das Problem, dass es keinen Sinn hat, für
beliebige Eingangsfolgen die Erhaltung der Energie zu
fordern, weil dann logischerweise nur Allpässe in Frage
kommen. Jedes "echte" Filter soll ja bestimmte
Spektralbereiche dämpfen.

Von Überschwingern im strengen Sinne freie Allpässe
können m.M.n. nur Laufzeitleitungen sein, und die
helfen Dir ja nicht weiter.
Ich denke, dass alles, was Flanken beeiflusst,
bereichsweise einen nichtlinearen Phasengang haben
muss, und das verursacht bereichsweise Überschwinger.

von Egon D. (Gast)


Lesenswert?

Walter T. schrieb:

> Ein gleitender Mittelwert ohne Rundungsfehler hätte
> diese Erhaltungseigenschaft übrigens auch.

Ja... was ich nicht verstehe: Welche Eigenschaft
benötigst Du, die der gleitende Mittelwert NICHT hat?

von Walter T. (nicolas)


Lesenswert?

Egon D. schrieb:
> Welche Eigenschaft
> benötigst Du, die der gleitende Mittelwert NICHT hat?

Die Erhaltung der Summe. Führe ich den gleitenden Mittelwert auf Integer 
mit N Stützstellen aus, kann der Rundungsfehler bis zu N*(N-1) betragen. 
Auf Anhieb fällt mir auch kein Korrekturterm ein, der dieses Problem 
beheben würde.

Egon D. schrieb:
> Ist der Gleichanteil das richtige Maß,

Ja.

Egon D. schrieb:
> und das leistet jeder Tiefpass,
> der bei f=0 die Verstärkung "Eins" hat.

Im kontinuierlichen Fall ja. Im diskreten Fall spucken mir die 
Rundungsfehler in die Suppe.

Egon D. schrieb:
> Wäre eine Umsetzung von
> {0,50,100,0,-50,-100} auf {0,0,0,0,0,0} akzeptabel?

Das wäre sogar das gewünschte Verhalten.


Hintergrund: Ich habe eine Regelung, die bei berechneten 
Sollwertvorgaben sehr stabil läuft. Beim Handradbetrieb über einen 
Drehgeber lassen sich jedoch leicht Regelschwingungen provozieren. Jetzt 
sehe ich zwei Lösungsmöglichkeiten:

a) Im Handbetrieb die Kreisverstärkung massiv zurücknehmen.

b) Eine Art "virtuelles Schwungrad" am Drehgeber vorsehen.

a) ist einfach zu realisieren, bewirkt aber eine Haptik wie eine 
mittlere Weinbergschnecke. Jetzt will ich b) probieren.

: Bearbeitet durch User
von Mach (Gast)


Lesenswert?

wenn du sowieso mit Integer arbeitest, kannst du doch fehlerfrei 
arbeiten. Im Akkumulator dann nicht den Durchschnittswert vorhalten, 
sondern die Gasamtsumme, und nur für die Ausgabe teilen/runden.

von c-hater (Gast)


Lesenswert?

Walter T. schrieb:

> Die Erhaltung der Summe. Führe ich den gleitenden Mittelwert auf Integer
> mit N Stützstellen aus, kann der Rundungsfehler bis zu N*(N-1) betragen.

Nö, das sicher nicht. Der Rundungsfehler eines gleitenden Mittelwertes 
von N Integern ist natürlich < 1. Und obendrein völlig unabhängig von N.

von c-hater (Gast)


Lesenswert?

c-hater schrieb:
> Walter T. schrieb:
>
>> Die Erhaltung der Summe. Führe ich den gleitenden Mittelwert auf Integer
>> mit N Stützstellen aus, kann der Rundungsfehler bis zu N*(N-1) betragen.
>
> Nö, das sicher nicht. Der Rundungsfehler eines gleitenden Mittelwertes
> von N Integern ist natürlich < 1. Und obendrein völlig unabhängig von N.

Mist. Mus natürlich <= 1 heissen.

von Egon D. (Gast)


Lesenswert?

Walter T. schrieb:

> Egon D. schrieb:
>> Welche Eigenschaft
>> benötigst Du, die der gleitende Mittelwert NICHT hat?
>
> Die Erhaltung der Summe. Führe ich den gleitenden
> Mittelwert auf Integer mit N Stützstellen aus, kann
> der Rundungsfehler bis zu N*(N-1) betragen. Auf Anhieb
> fällt mir auch kein Korrekturterm ein, der dieses
> Problem beheben würde.

Ich habe aus dem Stand auch keine Lösung -- glaube aber
fest an die Lösbarkeit :)

Folgender Denkansatz: Man stellt sich beim gleitenden
Mittel immer vor, dass man zuerst die ganzen Zahlen
addiert und anschließend durch die Anzahl teilt, und
dabei bleiben halt Nachkommastellen übrig. Ich schlage
vor, die Reihenfolge im Geiste umzudrehen und jeden
einzelnen Abtastwert in den ganzen Anteil des
Quotienten [a_n/N] und den (ganzzahligen) Rest zu zerlegen.

Der ganze Anteil des gleitenden Mittelwertes ergibt
sich (in erster Näherung) erstmal aus der Summe der
ganzen Anteile aller dividierten Abtastwerte. Das ist
nicht problematisch.
Gesucht ist eine (adaptive) Strategie, wie man die
Reste auf die Ausgabewerte aufteilt.
Schätzungsweise sollte man die Reste akkumulieren und
bei Überschreiten bestimmter Schwellwerte den Ausgabe-
wert erhöhen und den Reste-Akku entsprechend korrigieren.

Akkumulation von Rundungsfehlern kann dabei nicht
auftreten, weil ja (bisher) nicht gerundet wird.

von roehrenvorheizer (Gast)


Lesenswert?

b) FIR Filter

Beitrag #5839142 wurde von einem Moderator gelöscht.
von Egon D. (Gast)


Lesenswert?

Nachtrag:


Egon D. schrieb:

> Gesucht ist eine (adaptive) Strategie, wie man die
> Reste auf die Ausgabewerte aufteilt.
> Schätzungsweise sollte man die Reste akkumulieren und
> bei Überschreiten bestimmter Schwellwerte den Ausgabe-
> wert erhöhen und den Reste-Akku entsprechend korrigieren.

Möglicherweise funktioniert eine viel einfachere Strategie:
Wenn Divisionsrest k auftritt, dann werden die k jüngsten
Reste incrementiert. Es ist ja für die Summe egal, ob ein
Ausgabewert umd k Digit oder k Ausgabewerte um ein Digit
vergrößert werden.
Vorausgesetzt wird, dass das FIR-Filter als Warteschlange
aufgefasst wird, d.h. dass nicht der mittlere, sondern der
letzte (älteste) Wert aus Ausgabewert verwendet wird.

Da bei einer Filterlänge von N der größte Rest (N-1) ist,
wird höchstens der zweitälteste Rest incrementiert, aber
nie der älteste.

Zur Ausgabe wird einfach zur gleitenden Summe der älteste
"Rest" addiert, das Resultat ausgegeben, und von der Summe
der älteste ganze Anteil subtrahiert.

von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

wenn man eine Nacht darüber geschlafen hat, sieht es eigentlich ganz 
einfach aus. Man muß eigentlich nur den Rest mitschleppen. So 
funktioniert es dann auch:
1
    #define N_FIR_ORDER 5
2
3
    int32_t firConservative(int32_t u)
4
    {
5
        static int32_t merker[N_FIR_ORDER];
6
        static int64_t summe = 0;
7
        summe -= merker[N_FIR_ORDER-1];
8
        memcpy( &(merker[1]), &(merker[0]), (N_FIR_ORDER-1)*sizeof(int32_t));
9
        merker[0] = u;
10
        summe += u;
11
12
        int32_t y =    summe/N_FIR_ORDER; // Noch ziemlich teuer
13
        int32_t rest = summe%N_FIR_ORDER;
14
        summe += rest;
15
        merker[0] += rest;
16
        return y;
17
    }
18
19
20
21
    TEST(filter_fiir, conservative)
22
    {
23
        // Warteschlange leeren
24
        for( int i = 0; i < N_FIR_ORDER; i++)
25
            firConservative(0);
26
27
        // Gefilterte Werte
28
        int32_t y = 0;
29
        y = firConservative(10);
30
        y += firConservative(20);
31
        y += firConservative(30);
32
        y +=  firConservative(-10);
33
34
        // Warteschlange leeren
35
        for( int i = 0; i < N_FIR_ORDER; i++)
36
        {
37
            y+= firConservative(0);
38
        }
39
40
        TEST_ASSERT_EQUAL( 50, y );
41
    }

Danke für die Denkanstöße!

: Bearbeitet durch User
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.