Filtragem - multiplicação no domínio da frequência

7

Estou tentando criar um filtro passa-baixo simples, mas obtive o que considero um resultado surpreendente ao analisar a resposta de frequência de um filtro Butterworth simples.

Copiei grande parte do exemplo abaixo deste outro post . Eu adicionei algum código na parte inferior do script para comparar os espectros de entrada e saída com a resposta de frequência do filtro. Eu esperaria que o espectro de saída fosse o produto do espectro de entrada e a resposta de frequência : BAH

B=HUMA

No entanto, o gráfico abaixo mostra que o filtro realmente aumenta alguns componentes de baixa frequência - veja como a linha vermelha está acima do verde abaixo em torno de .4 Hz

Alguém poderia explicar por que isso acontece?

import numpy as np
from scipy.signal import butter, lfilter, freqz
import matplotlib.pyplot as plt
from scipy.fftpack import fft as fft
def butter_lowpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

def butter_lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y


# Filter requirements.
order = 6
fs = 30.0       # sample rate, Hz
cutoff = 3.667  # desired cutoff frequency of the filter, Hz

# Get the filter coefficients so we can check its frequency response.
b, a = butter_lowpass(cutoff, fs, order)

# Plot the frequency response.
w, h = freqz(b, a, worN=8000)
plt.subplot(2, 1, 1)
plt.plot(0.5*fs*w/np.pi, np.abs(h), 'b')
plt.plot(cutoff, 0.5*np.sqrt(2), 'ko')
plt.axvline(cutoff, color='k')
plt.xlim(0, 0.5*fs)
plt.title("Lowpass Filter Frequency Response")
plt.xlabel('Frequency [Hz]')
plt.grid()


# Demonstrate the use of the filter.
# First make some data to be filtered.
T = 5.0         # seconds
n = int(T * fs) # total number of samples
t = np.linspace(0, T, n, endpoint=False)
# "Noisy" data.  We want to recover the 1.2 Hz signal from this.
data = np.sin(1.2*2*np.pi*t) + 1.5*np.cos(9*2*np.pi*t) + 0.5*np.sin(12.0*2*np.pi*t)

# Filter the data, and plot both the original and filtered signals.
y = butter_lowpass_filter(data, cutoff, fs, order)

plt.subplot(2, 1, 2)
plt.plot(t, data, 'b-', label='data')
plt.plot(t, y, 'g-', linewidth=2, label='filtered data')
plt.xlabel('Time [sec]')
plt.grid()
plt.legend()

plt.subplots_adjust(hspace=0.35)
plt.show()

def calculateFFT(time,signal):
    N=len(signal)      
    df=1/((time[-1]-time[0]))
    frequencies=[i*df for i in range(int(N/2.0))]
    fftValues = [2.0/N*abs(i) for i in fft(signal,N)[0:N/2.0] ]
    return frequencies,fftValues

plt.subplot(2, 1, 1)
originalfreqs,originalFFT=calculateFFT(t,data)
plt.plot(originalfreqs,originalFFT,"g",label="original")

filteredfreqs,filteredFFT=calculateFFT(t,y)
plt.plot(filteredfreqs,filteredFFT,"r",label="filtered")
plt.legend()

insira a descrição da imagem aqui

James Dilworth
fonte

Respostas:

4

De bom grado, votei esta questão, porque é assim que deve ser uma boa pergunta e também como espero que os alunos verifiquem sua compreensão das coisas que aprendem. É sempre bom estar interessado em entender os antecedentes de algo que você deseja usar posteriormente.

O problema que você enfrenta é o seguinte: Em princípio, você está certo, que o teorema da convolução implica que convolução em um domínio é multiplicação no outro domínio. Daí você tem

F{x(t)h(t)}=X(f)H(f)
onde denota a Transformada de Fourier.F

Então, o que é e em seu sistema? é o seu sinal de entrada (em azul), que é a soma de três senos, multiplicados por uma janela retangular. É multiplicado implicitamente por um retangular, porque seu tempo não (em simulação, não pode) chegar de a . Assim, o espectro de é a convolução de uma função sinc (da janela retangular) com três Diracs (na frequência dos senos). Claramente, este não é um espectro puramente discreto.x(t)h(t)x(t)-X(f)

O que é ? É a resposta ao impulso do filtro butterworth. O filtro Butterworth tem uma resposta de impulso infinitamente longa, portanto, seu produto de convolução também é infinitamente amplo. Portanto, em princípio, você não pode aplicar uma Transformada de Fourier finita (ou seja, discreta) (fft) e espera que seja semelhante ao caso contínuo.h(t)x(t)h(t)

Então, o que você vê é razoável. Você pode tentar zerar o sinal de entrada, de modo a obter (a parte principal) a resposta ao impulso em seu sinal. No entanto, mesmo assim, você verá as frequências, pois não é realmente discreto.X(f)

Maximilian Matthé
fonte
5

Para expandir a @ Maximilian Matthé 's resposta , você pode visualizar as vazamento espectral efeitos (convolução de por função sinc no domínio de frequência) por zero-acolchoar os insumos utilizados no calculateFFT. Por exemplo, a função a seguir zera as entradas com um comprimento vezes mais que a entrada original (onde, neste caso, ):kk=4

def calculateFFT(time,signal):
    k=4
    N=k*len(signal)      
    df=1/(k*(time[-1]-time[0]))
    frequencies=[i*df for i in range(int(N/2.0))]
    fftValues = [k*2.0/N*abs(i) for i in fft(signal,N)[0:int(N/2.0)] ]
    return frequencies,fftValues

Na plotagem do resultado, você deve ver que os componentes de baixa frequência realmente se sobrepõem (o que indica que a filtragem não aumenta realmente o sinal):

insira a descrição da imagem aqui

Então, de onde vieram aquelas ondulações ao redor do pico? Acontece que eles sempre estavam lá, mas, ao calcular a FFT, você estava obtendo o valor do espectro em um conjunto discreto de valores de frequência e essas ondulações passaram exatamente por zero nessas frequências. Se você tivesse escolhido uma frequência de sinal ligeiramente diferente que não é um múltiplo exato da resolução de frequência da FFT (0,2 Hz no seu caso), por exemplo 1,25 Hz em vez de 1,2 Hz, a amostragem do espectro de frequências pareceria bastante diferente (devido a amostragem de frequência em diferentes pontos da oscilação das ondulações):

insira a descrição da imagem aqui

SleuthEye
fonte