Come scrivere il filtro passa-basso per il segnale campionato in Python?


16

Ho un segnale che campionava ogni 1 ns (1e-9 sec) e ho, diciamo, 1e4 punti. Devo filtrare le alte frequenze da questo segnale. Diciamo che devo filtrare frequenze superiori a 10 MHz. Voglio che per frequenze inferiori alla frequenza di taglio il segnale venga passato invariato. Significa che il guadagno del filtro sarà 1 per frequenze inferiori alla frequenza di taglio. Vorrei poter specificare l'ordine dei filtri. Voglio dire, il filtro del primo ordine ha una pendenza di 20 db / decade (power off) dopo la frequenza di taglio, il filtro del secondo ordine ha una pendenza di 40 db / dec dopo la frequenza di taglio e così via. Le prestazioni elevate del codice sono importanti.

Risposte:


19

La risposta in frequenza per il filtro progettato utilizzando la funzione burro è:

Risposta del filtro di Butterworth

Ma non c'è motivo di limitare il filtro a un design di filtro monotonico costante. Se si desidera una maggiore attenuazione nella banda di arresto e nella banda di transizione più ripida, esistono altre opzioni. Per ulteriori informazioni sulla specifica di un filtro tramite iirdesing, consultare questo . Come mostrato dai grafici di risposta in frequenza per la progettazione del burro , la frequenza di taglio (punto -3 dB) è lontana dall'obiettivo. Questo può essere alleviato dal down-sampling prima del filtraggio (le funzioni di progettazione avranno un momento difficile con un filtro così stretto, il 2% della larghezza di banda). Vediamo come filtrare la frequenza di campionamento originale con il valore soglia specificato.

import numpy as np
from scipy import signal
from matplotlib import pyplot as plt

from scipy.signal import fir_filter_design as ffd
from scipy.signal import filter_design as ifd

# setup some of the required parameters
Fs = 1e9           # sample-rate defined in the question, down-sampled

# remez (fir) design arguements
Fpass = 10e6       # passband edge
Fstop = 11.1e6     # stopband edge, transition band 100kHz
Wp = Fpass/(Fs)    # pass normalized frequency
Ws = Fstop/(Fs)    # stop normalized frequency

# iirdesign agruements
Wip = (Fpass)/(Fs/2)
Wis = (Fstop+1e6)/(Fs/2)
Rp = 1             # passband ripple
As = 42            # stopband attenuation

# Create a FIR filter, the remez function takes a list of 
# "bands" and the amplitude for each band.
taps = 4096
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

# The iirdesign takes passband, stopband, passband ripple, 
# and stop attenuation.
bc, ac = ifd.iirdesign(Wip, Wis, Rp, As, ftype='ellip')  
bb, ab = ifd.iirdesign(Wip, Wis, Rp, As, ftype='cheby2') 

Filtri di frequenza di campionamento originali

Come accennato, poiché stiamo cercando di filtrare una percentuale così piccola della larghezza di banda, il filtro non avrà un netto taglio. In questo caso, filtro passa-basso, possiamo ridurre la larghezza di banda per ottenere un filtro più bello. La funzione di ricampionamento python / scipy.signal può essere utilizzata per ridurre la larghezza di banda.

Si noti che la funzione di ricampionamento eseguirà il filtro per impedire l'aliasing. Il prefiltro può anche essere eseguito (per ridurre l'aliasing) e in questo caso potremmo semplicemente ricampionare per 100 ed essere fatto , ma la domanda posta sulla creazione di filtri. Per questo esempio, eseguiremo un downsampling di 25 e creeremo un nuovo filtro

R = 25;            # how much to down sample by
Fsr = Fs/25.       # down-sampled sample rate
xs = signal.resample(x, len(x)/25.)

Se aggiorniamo i parametri di progettazione per il filtro FIR la nuova risposta è.

# Down sampled version, create new filter and plot spectrum
R = 25.             # how much to down sample by
Fsr = Fs/R          # down-sampled sample rate
Fstop = 11.1e6      # modified stopband
Wp = Fpass/(Fsr)    # pass normalized frequency
Ws = Fstop/(Fsr)    # stop normalized frequency
taps = 256
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

Risposta del filtro sottocampionato

Il filtro che opera sui dati sottocampionati ha una risposta migliore. Un altro vantaggio dell'utilizzo di un filtro FIR è che si avrà una risposta di fase lineare.


1
Grazie. Come si crea il grafico dello spettro del segnale?
Alex,

Grazie mille per un'ottima risposta! Mi chiedo se potresti spiegare come applicare un filtro FIR basato sui coefficienti calcolati usando Remez? Ho difficoltà a capire cosa filtfiltvuole per il aparametro.
ali_m

Una volta ottenuto i coefficienti di un filtro di progettazione, ( b FIR b ed una per IIR) è possibile utilizzare un paio di funzioni diverse per eseguire il filtraggio: lfilter , Convolve , filtfilt . In genere tutte queste funzioni funzionano in modo simile: y = filtfilt (b, a, x) Se si dispone di un filtro FIR, impostare semplicemente a = 1 , x è il segnale di ingresso, b è il coefficiente FIR. Anche questo post potrebbe essere d'aiuto.
Christopher Felton,

5

funziona?

from __future__ import division
from scipy.signal import butter, lfilter

fs = 1E9 # 1 ns -> 1 GHz
cutoff = 10E6 # 10 MHz
B, A = butter(1, cutoff / (fs / 2), btype='low') # 1st order Butterworth low-pass
filtered_signal = lfilter(B, A, signal, axis=0)

Hai ragione, però, la documentazione non è molto completa. Sembra che buttersia un wrapper per iirfilter, che è meglio documentato :

N: int L'ordine del filtro. Wn: array_like Una sequenza scalare o lunghezza-2 che fornisce le frequenze critiche.

La maggior parte di queste cose è clonata da Matlab, quindi puoi anche consultare la loro documentazione :

la frequenza di taglio normalizzata Wn deve essere un numero compreso tra 0 e 1, dove 1 corrisponde alla frequenza di Nyquist, π radianti per campione.

Aggiornare:

Ho aggiunto la documentazione per queste funzioni. :) Github lo rende facile.



1

Sto ottenendo buoni risultati con questo filtro FIR. Avvisi che applica il filtro due volte, andando "avanti" e "indietro", in modo da compensare l'offset del segnale (la filtfiltfunzione non ha funzionato, non so perché):

def firfilt(interval, freq, sampling_rate):
    nfreq = freq/(0.5*sampling_rate)
    taps =  sampling_rate + 1
    a = 1
    b = scipy.signal.firwin(taps, cutoff=nfreq)
    firstpass = scipy.signal.lfilter(b, a, interval)
    secondpass = scipy.signal.lfilter(b, a, firstpass[::-1])[::-1]
    return secondpass

Una grande risorsa per filtrare la progettazione e l'uso, da dove ho preso questo codice e da dove si possono prendere esempi di filtri passa-banda e passa-alto, è QUESTA .


Non credo che ci siano molti vantaggi nel filtrare in avanti e in senso inverso un filtro FIR. Un filtro IIR può trarre vantaggio da forward / reverse (filtfilt) perché è possibile ottenere una fase lineare da un filtro di fase non lineare filtrando in senso inverso.
Christopher Felton,

2
@ChristopherFelton Ho appena invertito per sincronizzare un segnale elettromiografico RAW con la versione smussata di se stesso. So che potrei semplicemente spostare il segnale, ma filtrare due volte finisce per essere meno problematico. Vale la pena notare che il secondo passaggio quasi non modifica il primo passaggio già filtrato ... Grazie per averlo notato!
Heltonbiker,

Ah, si Per rimuovere il ritardo (ritardo di gruppo), buon punto.
Christopher Felton,

1

Non ho i diritti di commento ...

@endolith: io uso lo stesso di te tranne che usando scipy.signal.filtfilt (B, A, x) dove x è il vettore di input da filtrare, ad esempio numpy.random.normal (size = (N)) . filtfilt effettua un passaggio avanti e indietro del segnale. Per completezza (la maggior parte è la stessa di @endolith):

import numpy as np
import scipy.signal as sps

input = np.random.normal(size=(N)) # Random signal as example
bz, az = sps.butter(FiltOrder, Bandwidth/(SamplingFreq/2)) # Gives you lowpass Butterworth as default
output = sps.filtfilt(bz, az, input) # Makes forward/reverse filtering (linear phase filter)

filtfilt come suggerito anche da @heltonbiker richiede array di coefficienti credo. Nel caso in cui sia necessario eseguire il filtro passa-banda in banda base complessa, è necessaria una configurazione più complessa, ma questo non sembra essere un problema qui.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.