I am working on filtering a noisy heart rate signal using Python. Since heart rates should not exceed around 220 beats per minute, I want to filter out all noise above 220 bpm. I first converted 220 bpm into 3.66666666 Hertz and then to rad/s, which gives me 23.0383461 rad/sec. The sampling frequency of my data is 30Hz, so I converted that to rad/s to get 188.495559 rad/s.
After researching online, I found some bandpass filter code and adapted it into a lowpass filter. Here’s the code I came up with:
from scipy.signal import butter, lfilter
from scipy.signal import freqs
def butter_lowpass(cutOff, fs, order=5):
nyq = 0.5 * fs
normalCutoff = cutOff / nyq
b, a = butter(order, normalCutoff, btype='low', analog=True)
return b, a
def butter_lowpass_filter(data, cutOff, fs, order=4):
b, a = butter_lowpass(cutOff, fs, order=order)
y = lfilter(b, a, data)
return y
cutOff = 23.1 # cutoff frequency in rad/s
fs = 188.495559 # sampling frequency in rad/s
order = 20 # filter order
y = butter_lowpass_filter(data, cutOff, fs, order)
plt.plot(y)
I’m confused because I’m pretty sure the butter
function expects the cutoff and sampling frequency in rad/s, but the output seems odd. Is it actually expecting these values in Hz?
Also, I don’t fully understand the purpose of these two lines:
nyq = 0.5 * fs
normalCutoff = cutOff / nyq
I know this is related to normalization, but I thought the Nyquist frequency was 2 times the sampling frequency, not 0.5. Why is the Nyquist used as a normalizer?
Can someone explain how to properly create a low pass filter in Python with these functions?
I also plotted the frequency response of the filter using the following code:
w, h = signal.freqs(b, a)
plt.plot(w, 20 * np.log10(abs(h)))
plt.xscale('log')
plt.title('Butterworth filter frequency response')
plt.xlabel('Frequency [radians / second]')
plt.ylabel('Amplitude [dB]')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(100, color='green') # cutoff frequency
plt.show()
However, the plot doesn’t show the expected cutoff at 23 rad/s. Can someone help clarify this?