Применение подходящего фильтра Баттерворта к необработанному сигналу с использованием Python

1

Я приобрел 10-секундный сырой сигнал PPG (Photoplethysmogram) от моего TI AFE4490. Мое оборудование откалибровано, и я использую 250 выборок в секунду для записи этого сигнала. Я приобрел 2500 очков в конце.

Я использовал полосовой фильтр Баттерворта с lowcut = 0,5, highcut = 15 и order = 2. Вы можете увидеть мои сырые и отфильтрованные сигналы:

Изображение 174551

Я также попытался отфильтровать это, используя фильтр нижних частот Баттерворта с lowcut = 15 и order = 2. Как вы можете видеть, мои сырые и отфильтрованные сигналы звучат ниже:

Изображение 174551

В некоторых статьях я читал, что 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.

Теги:
scipy
signal-processing

1 ответ

1
Лучший ответ

Начальный переходный период, который вы видите на своих графиках, - это ответ шага фильтра, когда внезапный ввод применяется к фильтру в его состоянии покоя. Если вы только что подключили физический прибор, включающий такой полосовой фильтр, датчик прибора мог бы получить образцы входных данных, переходя от 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

Изображение 174551

Еще одна вещь, которая стоит отметить на этом этапе, - вы называете 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)

Изображение 174551

Наконец, вы можете заметить, что этот последний результат немного меньше треугольника, чем вход. Это вызвано тем, что часть низкочастотного содержимого около 0,5 Гц отфильтровывается. Если это то, что вы ожидали, тогда было бы здорово. В противном случае вы можете поиграть с частотой среза, чтобы получить то, что, по вашему мнению, дает наилучшие результаты. Например (и я не хочу сказать, что это лучший диапазон частот), если вы должны установить lowcut=0.25 вы получите более треугольный график, например:

Изображение 174551

  • 0
    Спасибо за Ваш ответ. Еще один вопрос, этот lfilter_zi связан с y = signal.filtfilt(b, a, xn) ?
  • 1
    По умолчанию signal.filtfilt использует lfilter_zi чтобы получить начальные условия фильтра для проходов прямой и обратной фильтрации.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню