Я приобрел 10-секундный сырой сигнал PPG (Photoplethysmogram) от моего TI AFE4490. Мое оборудование откалибровано, и я использую 250 выборок в секунду для записи этого сигнала. Я приобрел 2500 очков в конце.
Я использовал полосовой фильтр Баттерворта с lowcut = 0,5, highcut = 15 и order = 2. Вы можете увидеть мои сырые и отфильтрованные сигналы:
Я также попытался отфильтровать это, используя фильтр нижних частот Баттерворта с lowcut = 15 и order = 2. Как вы можете видеть, мои сырые и отфильтрованные сигналы звучат ниже:
В некоторых статьях я читал, что 0,5 Гц и 15 Гц являются хорошими срезными и высокочастотными частотами для этого типа сигнала.
Прежде чем применить фильтры, я использовал алгоритм Scipy Butterworth (из scipy docs), чтобы показать мне ответ фильтра, и это было хорошо.
Мой отфильтрованный сигнал кажется хорошим после этого "начала" (высота в начале), но я не знаю, почему это начало. Кто-нибудь может сказать мне, если это "начнет" это нормально при фильтрах Баттерворта? Если да, есть какой-то способ его исправить?
Я ценю вашу помощь.
Мой код:
RED, IR, nSamples, sRate = getAFESignal()
period = 1/sRate # Signal period.
# Desired cutoff frequency (in Hz) and filter order.
lowcut = 0.5
highcut = 15
orders = 2
plt.figure(1)
x = np.linspace(0, nSamples*period, nSamples, endpoint=True)
plt.subplot(2,1,1)
y = IR
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.plot(x,y, label='Noisy signal')
plt.subplot(2,1,2)
yf = butter_bandpass_filter(IR, lowcut, highcut, nSamples, order=orders)
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.plot(x, yf, label='Filtered signal')
plt.grid()
plt.show()
Функция getAFEsignal()
- это просто функция для чтения файла.txt и размещения всех в два массива numpy.
Начальный переходный период, который вы видите на своих графиках, - это ответ шага фильтра, когда внезапный ввод применяется к фильтру в его состоянии покоя. Если вы только что подключили физический прибор, включающий такой полосовой фильтр, датчик прибора мог бы получить образцы входных данных, переходя от 0 (пока зонд отключен) до первого значения выборки ~ 0,126 В. Тогда отклик инструментального фильтра показал бы аналогичный переходный процесс.
Тем не менее, вы, вероятно, больше заинтересованы в установившемся отклике инструмента после того, как он больше не нарушается этими внешними факторами (например, подключенным зондом) и успел подойти к свойствам представляющего интерес сигнала.
Один из способов добиться этого - использовать образец данных, который достаточно длинный и отбросить начальный переходный процесс. Другой подход заключается в том, чтобы заставить исходное внутреннее состояние фильтра приблизиться к тому, что можно было ожидать, если бы сигнал аналогичной амплитуды был применен в течение некоторого времени до вашего первого входного образца. Это можно сделать, например, установив начальное условие с помощью scipy.signal.lfilter_zi
.
Теперь, я полагаю, вы использовали butter_bandpass_filter
из SciPy Cookbook, который не заботится об исходных условиях фильтра. К счастью, его можно легко модифицировать с этой целью:
from scipy.signal import butter, lfilter, lfilter_zi
def butter_bandpass_filter_zi(data, lowcut, highcut, fs, order=5):
b, a = butter_bandpass(lowcut, highcut, fs, order=order)
zi = lfilter_zi(b, a)
y,zo = lfilter(b, a, data, zi=zi*data[0])
return y
Еще одна вещь, которая стоит отметить на этом этапе, - вы называете butter_bandpass_filter
следующим:
yf = butter_bandpass_filter(IR, lowcut, highcut, nSamples, order=orders)
передавая nSamples
(общее количество выборок в вашем случае 2500) в качестве 4-го аргумента, тогда как функция ожидает частоту дискретизации (в вашем случае 250). Коэффициент 10 между двумя величинами имеет эффект, эквивалентный уменьшению отфильтрованного диапазона от [0.5,15]
Гц до [0.05,1.5]
Гц. Чтобы получить sRate
диапазон частот, вы должны передать sRate
в качестве четвертого аргумента:
yf = butter_bandpass_filter_zi(IR, lowcut, highcut, sRate, order=orders)
Наконец, вы можете заметить, что этот последний результат немного меньше треугольника, чем вход. Это вызвано тем, что часть низкочастотного содержимого около 0,5 Гц отфильтровывается. Если это то, что вы ожидали, тогда было бы здорово. В противном случае вы можете поиграть с частотой среза, чтобы получить то, что, по вашему мнению, дает наилучшие результаты. Например (и я не хочу сказать, что это лучший диапазон частот), если вы должны установить lowcut=0.25
вы получите более треугольный график, например:
lfilter_zi
связан сy = signal.filtfilt(b, a, xn)
?signal.filtfilt
используетlfilter_zi
чтобы получить начальные условия фильтра для проходов прямой и обратной фильтрации.