Sida 1 av 6

Lågpassfilter i mjukvara!

Postat: 11 april 2011, 00:28:20
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).

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 08:03:44
av victor_passe
Borde det inte vara "filter_output" på sista raden?

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 14:56:37
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);
}

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 15:24:48
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?

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 15:37:13
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).

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 15:47:59
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."

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 16:08:37
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.

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 16:20:09
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!

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 16:35:24
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?

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 18:01:29
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

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 19:39:59
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; 
}


Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 20:41:58
av jesse
Ta indata minus filter_output på lågpassfiltret så borde det väl bli högpass? (eller har jag helt fel?)

Re: Lågpassfilter i mjukvara!

Postat: 11 april 2011, 20:58:55
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.

Re: Lågpassfilter i mjukvara!

Postat: 12 april 2011, 10:42:19
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

Re: Lågpassfilter i mjukvara!

Postat: 12 april 2011, 12:34:06
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!