Эффект Jello при отображении отфильтрованного цифрового сигнала

Я хочу отображать записанный сигнал в реальном времени с некоторой базовой фильтрацией (остановка полосы, полосовой проход).

  • сигнал хранится в массиве numpy ( numpy.array )
  • граф matplotlib отображает массив numpy ( matplotlib.animation.FuncAnimation )
  • для каждого кадра FuncAnimation некоторые фильтры применяются к сигналу ( scipy.signal.butter , scipy.signal.filtfilt )

Когда я делаю это так, я получаю какой-то эффект джело (извините за плохое качество gif)

эффект желе

Даже если это не совсем то, что происходит, давайте притвориться, что я реализовал этот алгоритм в псевдокоде:

signal = np.array(some_data) plot = FuncAnimation(init_function, update_function) for each frame: - shift signal on the left # make space and discard oldest samples - add new samples on the right - filtered_signal = filter(signal) # using signal.butter and signal.filtfilt - update_plot(filtered_signal) # FuncAnimation update function 

Я ищу методы, чтобы избавиться от этого нежелательного эффекта. Есть идеи?

ИЗМЕНИТЬ 1

Без анимации это то, что добавляется для 20 последовательных отфильтрованных сигналов.

введите описание изображения здесь

  • Первый график – signal (необработанный сигнал)
  • Второй сюжет содержит 20 последних filtered_signal
  • Третий сюжет содержит 20 последних filtered_signal сдвинутых для лучшей визуализации

EDIT 2

  • signal представляет собой буфер фиксированного размера, содержащий N = 1000 выборок
  • filtered_signal создается с нуля для каждого кадра
  • частота дискретизации равна fs = 250 Гц
  • Применяются 2 фильтра: полоса пропускания 50 Гц и полоса пропускания [0,5 Гц, 120 Гц]

EDIT 3 Вот полный рабочий пример:

  • Первый график – это необработанный сигнал
  • Второй график – отфильтрованный сигнал (полоса пропускания [0,5 Гц, 120 Гц])

Исходный код:

 import matplotlib.pyplot as plt import numpy as np from scipy import signal import matplotlib.animation as animation import time N = 1000 fs = 250 last_update = time.time() sample_id = 0 def all_samples(length=10000): # generate a dummy signal st = 1.0 / fs t = np.arange(length) * st sig1 = 1*np.sin(2*np.pi * 2*t) + 2 sig2 = 0.25*np.sin(2*np.pi * 3*t) + 4 sig3 = 2*np.sin(2*np.pi * 4*t) + 5 return sig1 + sig2 + sig3 def band_pass(low_cut, high_cut, order=2): # compute butterworth b, a coefficients band = np.array([low_cut, high_cut]) Wn = band / (float(fs/2.0)) b, a = signal.butter(order, Wn, 'bandpass') return b, a def filter(raw_signal): # apply filter b, a = band_pass(0.5, 120) return signal.filtfilt(b, a, raw_signal) def init(): # init function for FuncAnimation blit=True global axe_raw, line_raw global axe_filt, line_filt line_filt.set_visible(False) line_raw.set_visible(False) axe_raw.set_xlim(0, 1000) axe_raw.set_ylim(5, 15) axe_filt.set_xlim(0, 1000) axe_filt.set_ylim(-5, 5) return line_raw, line_filt, def update(n): global raw_signal, axe_raw, line_raw global axe_filt, line_filt global last_update, fs, sample_id if n == 1: line_raw.set_visible(True) line_filt.set_visible(True) # add new samples now = time.time() sample_count = int((now - last_update) * fs) raw_signal = np.roll(raw_signal, -sample_count) raw_signal[-sample_count:] = all_samples[sample_id:sample_id + sample_count] last_update = now sample_id += sample_count # update plot (raw + filtered) line_raw.set_ydata(raw_signal) line_filt.set_ydata(filter(raw_signal)) return line_raw, line_filt all_samples = all_samples() raw_signal = np.zeros(N) # matplotlib animation figure = plt.figure() axe_raw = figure.add_subplot(211) axe_filt = figure.add_subplot(212) line_raw, = axe_raw.plot(raw_signal) line_filt, = axe_filt.plot(np.zeros(N)) anim = animation.FuncAnimation(figure, update, init_func=init, interval=5, blit=True) plt.show()