注意
點擊 這裡 下載完整的範例程式碼
濾波器設計教學¶
作者: Moto Hira
本教學展示如何建立基本的數位濾波器 (脈衝響應) 及其屬性。
我們將研究基於視窗型 sinc 核心的低通、高通和帶通濾波器,以及頻率取樣方法。
警告
本教學需要原型 DSP 功能,這些功能可在 nightly build 中取得。
請參閱 https://pytorch.dev.org.tw/get-started/locally 以取得安裝 nightly build 的說明。
import torch
import torchaudio
print(torch.__version__)
print(torchaudio.__version__)
import matplotlib.pyplot as plt
2.6.0
2.6.0
from torchaudio.prototype.functional import frequency_impulse_response, sinc_impulse_response
視窗型 Sinc 濾波器¶
Sinc 濾波器 是一種理想化的濾波器,它可以移除高於截止頻率的頻率,而不影響較低的頻率。
Sinc 濾波器在分析解中具有無限的濾波器寬度。在數值計算中,sinc 濾波器無法精確表達,因此需要近似值。
視窗型 sinc 有限脈衝響應是 sinc 濾波器的近似值。它首先評估給定截止頻率的 sinc 函數,然後截斷濾波器裙部,並應用一個視窗,例如漢明視窗,以減少因截斷引入的人為雜訊。
sinc_impulse_response()
會為給定的截止頻率產生視窗型 sinc 脈衝響應。
低通濾波器¶
脈衝響應¶
建立 sinc IR 就像將截止頻率值傳遞給 sinc_impulse_response()
一樣簡單。
cutoff = torch.linspace(0.0, 1.0, 9)
irs = sinc_impulse_response(cutoff, window_size=513)
print("Cutoff shape:", cutoff.shape)
print("Impulse response shape:", irs.shape)
Cutoff shape: torch.Size([9])
Impulse response shape: torch.Size([9, 513])
讓我們視覺化產生的脈衝響應。
def plot_sinc_ir(irs, cutoff):
num_filts, window_size = irs.shape
half = window_size // 2
fig, axes = plt.subplots(num_filts, 1, sharex=True, figsize=(9.6, 8))
t = torch.linspace(-half, half - 1, window_size)
for ax, ir, coff, color in zip(axes, irs, cutoff, plt.cm.tab10.colors):
ax.plot(t, ir, linewidth=1.2, color=color, zorder=4, label=f"Cutoff: {coff}")
ax.legend(loc=(1.05, 0.2), handletextpad=0, handlelength=0)
ax.grid(True)
fig.suptitle(
"Impulse response of sinc low-pass filter for different cut-off frequencies\n"
"(Frequencies are relative to Nyquist frequency)"
)
axes[-1].set_xticks([i * half // 4 for i in range(-4, 5)])
fig.tight_layout()

頻率響應¶
接下來,讓我們看看頻率響應。簡單地將傅立葉轉換應用於脈衝響應將會得到頻率響應。
frs = torch.fft.rfft(irs, n=2048, dim=1).abs()
讓我們視覺化產生的頻率響應。
def plot_sinc_fr(frs, cutoff, band=False):
num_filts, num_fft = frs.shape
num_ticks = num_filts + 1 if band else num_filts
fig, axes = plt.subplots(num_filts, 1, sharex=True, sharey=True, figsize=(9.6, 8))
for ax, fr, coff, color in zip(axes, frs, cutoff, plt.cm.tab10.colors):
ax.grid(True)
ax.semilogy(fr, color=color, zorder=4, label=f"Cutoff: {coff}")
ax.legend(loc=(1.05, 0.2), handletextpad=0, handlelength=0).set_zorder(3)
axes[-1].set(
ylim=[None, 100],
yticks=[1e-9, 1e-6, 1e-3, 1],
xticks=torch.linspace(0, num_fft, num_ticks),
xticklabels=[f"{i/(num_ticks - 1)}" for i in range(num_ticks)],
xlabel="Frequency",
)
fig.suptitle(
"Frequency response of sinc low-pass filter for different cut-off frequencies\n"
"(Frequencies are relative to Nyquist frequency)"
)
fig.tight_layout()

高通濾波器¶
可以通過從 Dirac delta 函數中減去低通脈衝響應來獲得高通濾波器。
將 high_pass=True
傳遞給 sinc_impulse_response()
將會把傳回的濾波器核心變更為高通濾波器。
irs = sinc_impulse_response(cutoff, window_size=513, high_pass=True)
frs = torch.fft.rfft(irs, n=2048, dim=1).abs()
脈衝響應¶

頻率響應¶

帶通濾波器¶
帶通濾波器可以通過從較低頻帶的低通濾波器中減去較高頻帶的低通濾波器來獲得。
脈衝響應¶

頻率響應¶

頻率取樣¶
我們接下來要探討的方法從所需的頻率響應開始,並通過應用逆傅立葉變換來獲得脈衝響應。
frequency_impulse_response()
接受頻率的(未正規化的)幅度分佈,並從中構建脈衝響應。
但請注意,由此產生的脈衝響應不會產生所需的頻率響應。
在下文中,我們將創建多個濾波器,並比較輸入頻率響應和實際頻率響應。
磚牆濾波器¶
讓我們從磚牆濾波器開始
magnitudes = torch.concat([torch.ones((128,)), torch.zeros((128,))])
ir = frequency_impulse_response(magnitudes)
print("Magnitudes:", magnitudes.shape)
print("Impulse Response:", ir.shape)
Magnitudes: torch.Size([256])
Impulse Response: torch.Size([510])
def plot_ir(magnitudes, ir, num_fft=2048):
fr = torch.fft.rfft(ir, n=num_fft, dim=0).abs()
ir_size = ir.size(-1)
half = ir_size // 2
fig, axes = plt.subplots(3, 1)
t = torch.linspace(-half, half - 1, ir_size)
axes[0].plot(t, ir)
axes[0].grid(True)
axes[0].set(title="Impulse Response")
axes[0].set_xticks([i * half // 4 for i in range(-4, 5)])
t = torch.linspace(0, 1, fr.numel())
axes[1].plot(t, fr, label="Actual")
axes[2].semilogy(t, fr, label="Actual")
t = torch.linspace(0, 1, magnitudes.numel())
for i in range(1, 3):
axes[i].plot(t, magnitudes, label="Desired (input)", linewidth=1.1, linestyle="--")
axes[i].grid(True)
axes[1].set(title="Frequency Response")
axes[2].set(title="Frequency Response (log-scale)", xlabel="Frequency")
axes[2].legend(loc="center right")
fig.tight_layout()
plot_ir(magnitudes, ir)

請注意,在過渡頻帶附近存在偽影。 當窗口大小較小時,這更加明顯。
magnitudes = torch.concat([torch.ones((32,)), torch.zeros((32,))])
ir = frequency_impulse_response(magnitudes)
plot_ir(magnitudes, ir)

任意形狀¶
magnitudes = torch.linspace(0, 1, 64) ** 4.0
ir = frequency_impulse_response(magnitudes)
plot_ir(magnitudes, ir)

magnitudes = torch.sin(torch.linspace(0, 10, 64)) ** 4.0
ir = frequency_impulse_response(magnitudes)
plot_ir(magnitudes, ir)

參考文獻¶
https://www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch16.pdf
https://courses.engr.illinois.edu/ece401/fa2020/slides/lec10.pdf
https://ccrma.stanford.edu/~jos/sasp/Windowing_Desired_Impulse_Response.html
腳本的總運行時間:(0 分鐘 5.247 秒)