Як написати фільтр низьких частот для вибіркового сигналу в Python?


16

У мене є сигнал, який взяв вибірки кожні 1 нс (1е-9 сек) і мав, скажімо, 1е4 бали. Мені потрібно фільтрувати високі частоти з цього сигналу. Скажімо, мені потрібно фільтрувати частоти вище 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.)

Якщо ми оновимо параметри проектування для фільтра FIR, новий відповідь є.

# 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
Дякую. Як ви створюєте графік спектру сигналу?
— Олексій

Дякую за чудову відповідь! Цікаво, чи можна пояснити, як застосувати фільтр FIR на основі коефіцієнтів, обчислених за допомогою Remez? У мене виникають проблеми з розумінням того, що filtfiltпотрібно для aпараметра.
— ali_m

Після того як ви коефіцієнти від конструкції фільтра, ( б для КИХ Ь і для ІДК) , ви можете використовували кілька різних функцій для виконання фільтрації: lfilter , скручувати , filtfilt . Зазвичай всі ці функції працюють аналогічно: y = filtfilt (b, a, x) Якщо у вас є фільтр FIR, просто встановіть a = 1 , x - вхідний сигнал, b - коефіцієнти FIR. Ця публікація також може допомогти.
— Крістофер Фелтон

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

Великий ресурс для розробки і використання фільтра, звідки я взяв цей код, і звідки можна взяти смуговий і приклади фільтрів привіт-пас, є ЕТА .


Я не вірю, що вперед та назад фільтрування фільтра FIR є багато вигоди. IIR-фільтр може отримати вигоду від прямого / зворотного (filtfilt), оскільки ви можете отримати лінійну фазу з нелінійного фазового фільтра шляхом зворотного фільтрування.
— Крістофер Фелтон

2
@ChristopherFelton Я просто повертаю назад, щоб синхронізувати RAW-електроміографічний сигнал із згладженою версією самого себе. Я знаю, що міг би просто перенести сигнал, але фільтрація двічі закінчується меншою проблемою. Варто зауважити, що другий прохід майже не змінює вже відфільтрований перший прохід ... Дякую за зауваження!
— heltonbiker

Ага, так. Щоб зняти затримку (групова затримка), добре.
— Крістофер Фелтон

1

Я не маю прав на коментарі ...

@endolith: Я використовую те саме, що і ви, за винятком використання scipy.signal.filtfilt (B, A, x), де x - вхідний вектор, який потрібно фільтрувати - наприклад, numpy.random.normal (size = (N)) . filtfilt робить прямий і зворотний прохід сигналу. Для повноти (більшість таких же, як @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)

filtfilt, як це також запропонував @heltonbiker, вимагає масивів коефіцієнтів, я вважаю. Якщо вам потрібно виконати смугову фільтрацію на складному базовому діапазоні, потрібна більш задіяна конфігурація, але це, здається, тут не є проблемою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.