Lågpassfilter i mjukvara!

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Lågpassfilter i mjukvara!

Inlägg av jesse »

Jag hittade en kort och väldigt bra artikel om lågpassfilter i mjukvara.
A simple software lowpass filter suits embeddedsystem applications
(EDIT: om ovanstående länk inte fungerar finns denna: Simple Software Lowpass Filter Preview)

(BUILDING A DIGITAL EQUIVALENT OF AN ANALOG
LOWPASS RC FILTER REQUIRES JUST A COUPLE
OF LINES OF FIXED-POINT C CODE)

De flesta av oss som jobbat med microcontrollers ett tag vet ju redan hur man gör lågpassfilter i mjukvara, men det finns säkert många här som kan ha nytta av artikeln. Ett lågpassfilter är perfekt om man tar in massor av samples från en ADC och vill plocka bort bruset. Filtret plockar inte enbart bort brus, det kan t.o.m öka antalet bitars noggrannhet utöver ADC:ns förmåga. En AVR-processor med 10-bitars ADC kan med ett sådant här filter läsa av signaler med noggrannhet upp till 11 eller ibland 12 bitar. Detta beror just på bruset! Genom att bruset är jämnt fördelat positivt och negativt så kommer man efter tillräckligt lång filtrering att ha fått fram medelvärdet på bruset -vilket resulterar i fler bitar. (Fast då får man modifiera koden lite och ta bort shiftoperationen i return-satsen - annars tappar man de vunna bitarna. Jag har själv bekräftat att det fungerar i praktiken med +2 bitar. (Förutsatt att den analoga biten inte har för mycket eller för litet brus. Det finns t.o.m exempel där man adderar brus för att skapa denna effekt om det saknas naturligt brus på ingången).

Koden: (Ändrade hans hemmasnickrade heltalstyper till AVR-typer)

Kod: Markera allt

#define FILTER_SHIFT 4                    //  Parameter  K
// int32_t = Specify  32-bit   integer
// int16_t Specify  16-bit   integer
int32_t  filter_reg;                       //  Delay  element  – 32 bits
int16_t  filter_input ;                    //  Filter   input  – 16 bits
int16_t  filter_output ;                   //  Filter  output  – 16 bits
//  Update filter with current sample.  
filter_reg =  filter_reg  -   ( filter_reg >> FILTER_SHIFT)  +  filter_input ;

//  Scale output   for  unity  gain.  
output  =  filter_reg >> FILTER_SHIFT
Och så här kan det filtrerade resultatet se ut: (FILTER_SHIFT = 3)

insignal går från 0 till 1000 som när en brytare sluts.
filter.png
insignalen brusig. observera eftersläpningen. (FILTER_SHIFT = 4)
filter2.png
samma insignal men FILTER_SHIFT = 3. Mindre eftersläpning men mer brus. Bruset är fortfarande bra dämpat.
filter3.png
Det var kurvor genererade med rand(). Men här kommer ett verkligt exempel:

En 20 bitars ADC (MCP3550) kopplad till en 100k kolbane-pot med utslaget +/- 100%. Poten är inställd på mitten, dvs nära 0, och y-skalan visar avvikelsen i procent på ett antal samples, och efter filtering i AVR ned FILTER_SHIFT = 3:
filter4.png
(100% utslag motsvarar 1.6 volt, så 0.0625% avvikelse motsvarar här 1 mV. Maximala avvikelsen efter filtrering är ca 0.0008% eller 0.128 mV (korrigerat för DC-offset som här låg på ca 0.087 mV).
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Senast redigerad av jesse 24 oktober 2014, 08:29:01, redigerad totalt 2 gånger.
victor_passe
Inlägg: 2436
Blev medlem: 28 januari 2007, 18:45:40
Ort: Kungsbacka

Re: Lågpassfilter i mjukvara!

Inlägg av victor_passe »

Borde det inte vara "filter_output" på sista raden?
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Lågpassfilter i mjukvara!

Inlägg av jesse »

tja.... jag kopierade i princip koden från artikeln. Visst kan det heta filter_output om man vill. Men jag skulle lagt det hela i en funktion och avslutat så här istället, så behöver man inte oroa sig vad det heter:

Kod: Markera allt

   ... kod ...
    return (filter_reg >> FILTER_SHIFT);
}
Användarvisningsbild
4kTRB
Inlägg: 20296
Blev medlem: 16 augusti 2009, 19:04:48

Re: Lågpassfilter i mjukvara!

Inlägg av 4kTRB »

Hur mycket bättre blir det med ett RC-filter i tillägg på ingången till ADC?
Eller borde det inte gå att ha ett fasskiftfilter på ingången så du kan ha
ett kraftigare digitalt LP-filter och slippa eftersläpning?
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Lågpassfilter i mjukvara!

Inlägg av jesse »

Filtrens gränsfrekvenser etc. måste ju bestämmas utifrån brusets frekvens...
Jag har alltid ett RC_filter före en ADC för att ta bort höga frekvenser som kan interferera med själva AD-omvandlingen. Men det kan finnas praktiska problem med för stora RC-filter på ingången också - om man inte har en buffrad ADC så måste man ha låg impedans in eller en OP med tillhörande problem som offset etc...

Själv har jag aldrig testat några andra mjukvarufilter än detta, men jag har klarat mig långt med det. Om man vill minska eftersläpningar kan det vara en idé att skaffa en snabbare ADC och helt enkelt köra fler samples per sekund - på så vis minskar man ju eftersläpningen lika mycket.

När jag har använt AVR:ens interna ADC så kan den lätt köra över 10000 smps, och ska man mest mäta DC-nivåer så har man all tid på sig att släta ut kurvan.

Fördelen med detta enkla filter är ju dess snabbhet + väldigt lite kod = bra i µC-miljö.

Är det nån som har kod och/eller formler / diagram för andra sorters mjukvarufilter så vore det kul om de kom med här i tråden. (Gärna med argument om fördelar och nackdelar med just det filtret).
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: Lågpassfilter i mjukvara!

Inlägg av SvenW »

Läste just igenom den här:
http://www.jfm3.org/c_handbook.pdf

Innehåller mycket klokt om än det finns olika meningar i många frågor.

Några citat:

"3.7 ­ Do Not Perform Bitwise Operations On Signed Data
Compilers are free to generate whatever results they want as the result
of the following expression.
signed int a = -7 >> 1;
In general, bitwise operations, >>, <<, &, |, ~, and their analogous
assignment operators, should happen only to unsigned integer types.
To do otherwise is to tie your code to a specific compiler and execution
platform. At worst, hide all your hakmem functions in a portability
module."

" For another example, n >> 1 is no faster than n / 2. Regardless
of machine architecture, any reasonable compiler will emit the fastest
sequence of instructions available for either case. The choice of using
one or the other should be based on whether an logical or arithmetic
operation is meant to the human reading the program, or whether the
program might one day be changed to n >> 2 or n / 3."
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Lågpassfilter i mjukvara!

Inlägg av jesse »

Ja, det stämmer ju. AVR-GCC låter MSB vara kvar om man gör shift åt höger, dvs. negativa tal förblir negativa. Men det är säkert något man måste se upp med. Om man har gott om tid kan man ju använda division. Annars får man helt enkelt göra om data till positiva först.
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: Lågpassfilter i mjukvara!

Inlägg av SvenW »

Generell division i AVR-GCC kan ta mycket lång tid! Den anropar en funktion.
Men division med 2-potens bör gå fort, åtminstone med optimering påslagen.
Men man måste alltid testa sådana saker. Jag har alltid ett oscilloskop framme om det handlar om AVR. Kompilatorn brukar i alla fall vara förvånansvärt smart!
Gimbal
Inlägg: 8570
Blev medlem: 20 april 2005, 15:43:53

Re: Lågpassfilter i mjukvara!

Inlägg av Gimbal »

Det var ju en listig variant, jag brukar alltid köra något sådan här (med float):

UT = (10*UT + IN)/11;

Inte snabbt, men långsamt.


Men hur gör man ett högpass?
Användarvisningsbild
4kTRB
Inlägg: 20296
Blev medlem: 16 augusti 2009, 19:04:48

Re: Lågpassfilter i mjukvara!

Inlägg av 4kTRB »

Moving average filter används flitigt i DSP sammanhang och är väldigt
lätt att koda. Det ska vara näst intill optimalt för att få bort brus när
det handlar om signaler med snabb stigtid. Behöver inte vara en DSP
för att det ska gå att använda.

http://en.wikipedia.org/wiki/Moving_average
http://logix4u.net/DSP/Digital_Filters/ ... ilter.html
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: Lågpassfilter i mjukvara!

Inlägg av SvenW »

Men hur gör man ett högpass?
Såhär kanske. Varning. Funktionen är otestad!

Kod: Markera allt


int16_t  
filter(int16_t  input )
{  
  const uint8_t a = 1 << 4 ;        //  Tvåpotens går snabbt
  static int32_t accu = 0;           //  Delay  element – 32 bits
  int16_t  low_pass, high_pass;

  accu +=  input ;
  accu -=  accu / a;

  low_pass = accu / a;
  high_pass =  input - low_pass ;

  return high_pass; 
}

Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Lågpassfilter i mjukvara!

Inlägg av jesse »

Ta indata minus filter_output på lågpassfiltret så borde det väl bli högpass? (eller har jag helt fel?)
Användarvisningsbild
kimmen
Inlägg: 2042
Blev medlem: 25 augusti 2007, 16:53:51
Ort: Stockholm (Kista)

Re: Lågpassfilter i mjukvara!

Inlägg av kimmen »

jesse skrev:Ja, det stämmer ju. AVR-GCC låter MSB vara kvar om man gör shift åt höger, dvs. negativa tal förblir negativa. Men det är säkert något man måste se upp med. Om man har gott om tid kan man ju använda division. Annars får man helt enkelt göra om data till positiva först.
De flesta "normala" plattformarna använder aritmetisk skift där och i och med att det också rör sig om 2-komplementsrepresentation blir resultatet division med tvåpotens med avrundning mot minus oändligheten.

Heltalsdivision t.ex. (-3) / 2 var väl inte heller definierat förräns nyligen. Den nyaste C-standarden (men inte C++ vad jag förstått!) kräver avrundning mot 0.

Men har man indata som är unsigned skulle det ju faktiskt gå att köra unsigned rakt igenom i det här fallet eftersom filtret inte har något översläng.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Lågpassfilter i mjukvara!

Inlägg av jesse »

eftersom all shift "trunkerar" bort de utshiftade bitarna (dvs inte avrundar alls) så kommer positiva heltal att avrundas neråt, och negativa också avrundas "neråt", dvs mot det mest negativa heltalet. Det innebär ju att resultatet blir perfekt i detta sammanhang, då alla tal avrundas neråt.

Vill man korrigera för detta kanske man kan göra så här vid output:

Kod: Markera allt

filter_output = (filter_reg + (1 <<(FILTER_SHIFT-1)) >> FILTER_SHIFT

Kod: Markera allt

i       i>>2    (i+2)>>2        i/4
-12     -3      -3      -3
-11     -3      -3      -2
-10     -3      -2      -2
-9      -3      -2      -2
-8      -2      -2      -2
-7      -2      -2      -1
-6      -2      -1      -1
-5      -2      -1      -1
-4      -1      -1      -1
-3      -1      -1      0
-2      -1      0       0
-1      -1      0       0
0       0       0       0
1       0       0       0
2       0       1       0
3       0       1       0
4       1       1       1
5       1       1       1
6       1       2       1
7       1       2       1
8       2       2       2
9       2       2       2
10      2       3       2
11      2       3       2
12      3       3       3
SvenW
Inlägg: 1155
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: Lågpassfilter i mjukvara!

Inlägg av SvenW »

Det stämmer faktiskt!
Det är alltså direkt olämpligt att använda i/16 i exemplet, medan i>>4 blir rätt.
Säkrast är kanske uint_t, men att blanda int_t och uint_t i samma beräkning leder lätt till misstag.

Lärdomen blir att man aldrig helt kan förlita sig på allmänna råd som
http://www.jfm3.org/c_handbook.pdf
Trots att de nog har rätt!
Skriv svar