Как написать фильтр нижних частот для дискретизированного сигнала в Python?


16

У меня есть сигнал, который дискретизируется каждые 1 нс (1e-9 сек) и имеет, скажем, 1e4 балла. Мне нужно отфильтровать высокие частоты от этого сигнала. Допустим, мне нужно отфильтровать частоты выше 10 МГц. Я хочу, чтобы для частот ниже частоты среза сигнал передавался без изменений. Это означает, что усиление фильтра будет 1 для частот ниже частоты среза. Я хотел бы иметь возможность указать порядок фильтров. Я имею в виду, фильтр первого порядка имеет наклон 20 дБ / декад (спад мощности) после частоты среза, фильтр второго порядка имеет наклон 40 дБ / декар после частоты среза и так далее. Высокая производительность кода важна.

Ответы:


19

Частотная характеристика для фильтра, разработанного с использованием функции масла, равна:

Ответ фильтра Баттерворта

Но нет причин ограничивать фильтр постоянной монотонной конструкцией фильтра. Если вы хотите более высокое затухание в полосе останова и более крутой полосе перехода, существуют другие варианты. Для получения дополнительной информации об указании фильтра с использованием iirdesing см. Это . Как видно из графиков частотной характеристики для конструкции масла, частота отсечки (точка -3 дБ) далека от цели. Это может быть уменьшено путем понижающей дискретизации перед фильтрацией (функциям дизайна будет трудно с таким узким фильтром, 2% полосы пропускания). Давайте посмотрим на фильтрацию исходной частоты дискретизации с указанным отсечением.

import numpy as np
from scipy import signal
from matplotlib import pyplot as plt

from scipy.signal import fir_filter_design as ffd
from scipy.signal import filter_design as ifd

# setup some of the required parameters
Fs = 1e9           # sample-rate defined in the question, down-sampled

# remez (fir) design arguements
Fpass = 10e6       # passband edge
Fstop = 11.1e6     # stopband edge, transition band 100kHz
Wp = Fpass/(Fs)    # pass normalized frequency
Ws = Fstop/(Fs)    # stop normalized frequency

# iirdesign agruements
Wip = (Fpass)/(Fs/2)
Wis = (Fstop+1e6)/(Fs/2)
Rp = 1             # passband ripple
As = 42            # stopband attenuation

# Create a FIR filter, the remez function takes a list of 
# "bands" and the amplitude for each band.
taps = 4096
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

# The iirdesign takes passband, stopband, passband ripple, 
# and stop attenuation.
bc, ac = ifd.iirdesign(Wip, Wis, Rp, As, ftype='ellip')  
bb, ab = ifd.iirdesign(Wip, Wis, Rp, As, ftype='cheby2') 

Оригинальные фильтры частоты дискретизации

Как уже упоминалось, поскольку мы пытаемся отфильтровать такой небольшой процент полосы пропускания, у фильтра не будет резкого обрезания. В этом случае, фильтр нижних частот, мы можем уменьшить пропускную способность, чтобы получить более привлекательный фильтр. Функция повторной выборки python / scipy.signal может использоваться для уменьшения пропускной способности.

Обратите внимание, что функция повторной выборки будет выполнять фильтрацию для предотвращения наложения имен. Предварительная фильтрация также может быть выполнена (чтобы уменьшить алиасинг), и в этом случае мы могли бы просто сделать повторную выборку на 100 и сделать это , но вопрос задавался о создании фильтров. Для этого примера мы снизим выборку на 25 и создадим новый фильтр

R = 25;            # how much to down sample by
Fsr = Fs/25.       # down-sampled sample rate
xs = signal.resample(x, len(x)/25.)

Если мы обновим параметры проекта для КИХ-фильтра, новый ответ будет.

# Down sampled version, create new filter and plot spectrum
R = 25.             # how much to down sample by
Fsr = Fs/R          # down-sampled sample rate
Fstop = 11.1e6      # modified stopband
Wp = Fpass/(Fsr)    # pass normalized frequency
Ws = Fstop/(Fsr)    # stop normalized frequency
taps = 256
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

Ответ фильтра с пониженной выборкой

Фильтр, работающий с данными пониженной дискретизации, имеет лучший отклик. Другое преимущество использования FIR-фильтра заключается в том, что у вас будет линейный фазовый отклик.


1
Спасибо. Как вы создаете график спектра сигнала?
Алекс

Большое спасибо за отличный ответ! Интересно, не могли бы вы объяснить, как применить КИХ-фильтр на основе коэффициентов, вычисленных с помощью Ремеза? У меня проблемы с пониманием того, что filtfiltхочет для aпараметра.
ali_m

После того как вы коэффициенты от конструкции фильтра, ( б для КИХ Ь и для ИДК) , вы можете использовали несколько различных функций для выполнения фильтрации: lfilter , скручивать , filtfilt . Как правило, все эти функции работают аналогично: у = фильтрфильтр (б, а, х). Если у вас есть КИХ-фильтр, просто установите а = 1 , х - это входной сигнал, а б - коэффициенты КИХ. Этот пост также может помочь.
Кристофер Фелтон

5

Это работает?

from __future__ import division
from scipy.signal import butter, lfilter

fs = 1E9 # 1 ns -> 1 GHz
cutoff = 10E6 # 10 MHz
B, A = butter(1, cutoff / (fs / 2), btype='low') # 1st order Butterworth low-pass
filtered_signal = lfilter(B, A, signal, axis=0)

Вы правы, хотя, документация не очень полная. Похоже, butterэто обертка для iirfilter, которая лучше задокументирована :

N: int Порядок фильтра. Wn: array_like Скалярная последовательность или последовательность длины 2, дающая критические частоты.

Большая часть этого материала клонирована из Matlab, так что вы также можете посмотреть их документацию :

нормализованная частота отсечки Wn должна быть числом от 0 до 1, где 1 соответствует частоте Найквиста, π радиан на выборку.

Обновить:

Я добавил документацию для этих функций. :) Github делает это легко.


1

Не уверен, что ваше заявление, но вы можете проверить Gnuradio: http://gnuradio.org/doc/doxygen/classgr__firdes.html

Блоки обработки сигналов написаны на C ++ (хотя потоковые графы Gnuradio на Python), но вы сказали, что важна высокая производительность.


1

У меня хорошие результаты с этим фильтром FIR. Заметил, что он применяет фильтр дважды, двигаясь вперед и назад, чтобы компенсировать смещение сигнала ( filtfiltфункция не работает, не знаю почему):

def firfilt(interval, freq, sampling_rate):
    nfreq = freq/(0.5*sampling_rate)
    taps =  sampling_rate + 1
    a = 1
    b = scipy.signal.firwin(taps, cutoff=nfreq)
    firstpass = scipy.signal.lfilter(b, a, interval)
    secondpass = scipy.signal.lfilter(b, a, firstpass[::-1])[::-1]
    return secondpass

ЭТО - отличный ресурс для разработки и использования фильтров, откуда я взял этот код и откуда можно взять примеры полосовых и высокочастотных фильтров .


Я не верю, что есть прямая и обратная фильтрация КИХ-фильтра. БИХ-фильтр может выиграть от прямого / обратного (фильтрация), потому что вы можете получить линейную фазу из нелинейного фазового фильтра с помощью обратной фильтрации.
Кристофер Фелтон

2
@ChristopherFelton Я просто переворачиваюсь, чтобы синхронизировать электромиографический сигнал RAW со сглаженной версией самого себя. Я знаю, что могу просто сдвинуть сигнал, но фильтрация дважды приводит к меньшим проблемам. Стоит заметить, что второй проход почти не меняет уже отфильтрованный первый проход ... Спасибо, что заметили!
Хелтонбайкер

Ааа да Чтобы убрать задержку (групповую задержку), хороший момент.
Кристофер Фелтон

1

У меня нет прав на комментарии ...

@endolith: Я использую то же самое, что и вы, за исключением использования scipy.signal.filtfilt (B, A, x), где x - это входной вектор, который необходимо отфильтровать - например, numpy.random.normal (size = (N)) . FilterFilt делает прямой и обратный проход сигнала. Для полноты картины (большинство из них совпадают с @endolith):

import numpy as np
import scipy.signal as sps

input = np.random.normal(size=(N)) # Random signal as example
bz, az = sps.butter(FiltOrder, Bandwidth/(SamplingFreq/2)) # Gives you lowpass Butterworth as default
output = sps.filtfilt(bz, az, input) # Makes forward/reverse filtering (linear phase filter)

Для фильтра, как и в @heltonbiker, требуются массивы коэффициентов, как мне кажется. В случае, если вам необходимо выполнить полосовую фильтрацию в сложной основной полосе частот, необходима более сложная конфигурация, но здесь это не является проблемой.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.