Pythonでサンプリングされた信号のローパスフィルタを書く方法は?


16

1 ns(1e-9秒)ごとにサンプリングした信号があり、たとえば1e4ポイントがあります。この信号から高周波をフィルタリングする必要があります。10 MHzより高い周波数をフィルタリングする必要があるとしましょう。カットオフ周波数より低い周波数では、信号は変更されずに渡されます。これは、カットオフ周波数より低い周波数ではフィルターのゲインが1になることを意味します。フィルター次数を指定できるようにしたいと思います。つまり、カットオフ周波数後の1次フィルターには20 db / decadeの勾配(電源ロールオフ)があり、カットオフ周波数後の2次フィルターには40 db / decの勾配があります。コードの高性能が重要です。

回答:


19

バター 関数を使用して設計されたフィルターの周波数応答は次のとおりです。

バタワースフィルター応答

ただし、フィルターを一定の単調フィルター設計に制限する理由はありません。遮断帯域とより急峻な遷移帯域でより高い減衰を望む場合、他のオプションが存在します。iirdesingを使用してフィルターを指定する方法の詳細については、こちらを参照してください。バター設計の周波数応答プロットが示すように、カットオフ周波数(-3dBポイント)は目標からはほど遠いです。これは、フィルタリングの前にダウンサンプリングすることで軽減できます(帯域幅の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 resample関数を使用して、帯域幅を削減できます。

resample関数は、エイリアスを防ぐためにフィルタリングを実行します。事前フィルタリングは(エイリアスを減らすために)実行することもできます。この場合、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つの利点は、線形位相応答が得られることです。


1
ありがとうございました。信号スペクトルのグラフを作成する方法は?
アレックス

すばらしい回答をありがとうございます!Remezを使用して計算された係数に基づいてFIRフィルターを適用する方法を説明できると思いますか?パラメータに何filtfiltが必要なのか理解aできません。
ali_m

あなたはフィルタ設計、(からの係数を持ってたら、B FIRのためのBおよびIIRのための)あなたは、フィルタリングを実行するカップル異なる機能を使用することができます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フィルターで良い結果が得られています。信号のオフセットを補正するために、「順方向」と「逆方向」に2回フィルタが適用されることに注意してください(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

このコードを取得した場所、およびバンドパスとハイパスフィルターの例を取得できるフィルターの設計と使用に最適なリソースは、THISです。


FIRフィルターの順方向および逆方向のフィルター処理には多くの利点があるとは思わない。IIRフィルターは、逆フィルター処理によって非線形位相フィルターから線形位相を取得できるため、順方向/逆方向(filtfilt)の恩恵を受けることができます。
クリストファーフェルトン

2
@ChristopherFelton私は、RAW筋電図信号をそれ自体の平滑化されたバージョンと同期させるために、単に逆にしています。信号をシフトするだけでよいことはわかっていますが、2回のフィルタリングで問題が少なくなります。2回目のパスでは、既にフィルター処理された1回目のパスはほとんど変更されないことに注意してください。
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)

@heltonbikerでも示唆されているfiltfiltには、係数の配列が必要だと思います。複雑なベースバンドでバンドパスフィルタリングを実行する必要がある場合、より複雑な設定が必要ですが、これはここでは問題に見えません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.