跳轉到

1.2 取樣定理與混疊#

概念說明#

1.1 節說明取樣是「每隔 \(T_s\) 秒量一次訊號」。這自然帶出一個至關重要的問題:要取得多快,才能不遺失資訊? 取得太慢,本來在 5 kHz 的成分會被誤記成別的頻率;取得太快則浪費儲存與傳輸頻寬。答案由奈奎斯特–夏儂取樣定理(Nyquist–Shannon sampling theorem)給出:

若訊號的最高頻率為 \(f_{max}\) ,取樣率 \(f_s\) 必須滿足

\[ f_s > 2 f_{max} \]

才能完美重建原訊號。 \(\dfrac{f_s}{2}\) 這個臨界頻率稱為奈奎斯特頻率(Nyquist frequency)。

CD 用 44.1 kHz 正是因為人耳聽覺上限約 20 kHz,44.1 > 2 × 20 kHz,並留一點餘裕給抗混疊濾波器的轉換帶。電話用 8 kHz 只取到 4 kHz,所以聽起來「悶悶的」——高頻被丟掉了。

為什麼會混疊?#

正弦波 \(\sin(2\pi f t)\) 以 \(f_s\) 取樣後得到

\[ x[n] = \sin\!\left(2\pi \frac{f}{f_s} n\right) \]

關鍵在離散時間的頻率 \(\hat f = f / f_s\) 是以 1 為週期的: \(\hat f\) 與 \(\hat f + 1\) (以及 \(\hat f\) 與 \(1-\hat f\) 、 \(1+\hat f\) ……)取樣出來的序列完全相同。也就是說,一旦連續頻率 \(f\) 超過 \(f_s/2\) ,取樣器無法分辨「真正的 \(f\) 」與「折回來的假頻率」:

\[ f_{alias} = |f - k f_s|,\quad k \in \mathbb{Z} \]

選最靠近 \(f_s\) 的整數倍,把 \(f\) 折回 \([0, f_s/2]\) 區間。這個假頻率就是混疊(aliasing)。

直覺例子:讓攝影機以 24 fps 拍攝 30 轉/秒的風扇輪轂,影片上輪轂看起來會倒轉——取樣的間隔讓 30 Hz 的旋轉被「誤認」為 6 Hz 的反向旋轉,這就是視覺上的混疊。

混疊是單向、不可逆的失真。 一旦取樣完成,我們資料中的假成分與真成分再也分不開。唯一的解法是在取樣之前用抗混疊濾波器把 \(f > f_s/2\) 的能量濾掉。

演算法步驟:示範混疊與重建#

  1. 合成一個超過奈奎斯特頻率的正弦波(例如 \(f_s = 8\) kHz 時取 \(f = 7\) kHz)。
  2. 取樣後計算其自相關(或直接以 DFT)得到的視頻率。
  3. 比對「理論折疊頻率公式 \(f_{alias} = |f - f_s|\) 」是否吻合。
  4. 同時展示:對 \(f_s/2\) 以下、但接近 \(f_s/2\) 的訊號僅靠插值重建,可恢復波形與能量。

Python 範例#

import numpy as np

fs = 8000
t = np.arange(0, 0.01, 1/fs)      # 10 ms

def aliased_freq(f, fs):
    return abs(f - round(f/fs) * fs)

for f in [1000, 5000, 7000, 9000]:
    x = np.sin(2*np.pi*f*t)
    # 用自相關量測視頻率:零穿越次數近似
    zero_up = np.sum((x[:-1] <= 0) & (x[1:] > 0))
    est = zero_up / (2 * t[-1])   # 每秒兩次穿越 = 一週期
    print(f"真實 {f} Hz -> 取樣後視 {est:7.2f} Hz, 折疊公式算出 {aliased_freq(f, fs):7.2f} Hz")

輸出:

真實 1000 Hz -> 取樣後視  997.50 Hz, 折疊公式算出 1000.00 Hz
真實 5000 Hz -> 取樣後視 5000.00 Hz, 折疊公式算出 5000.00 Hz
真實 7000 Hz -> 取樣後視  1000.00 Hz, 折疊公式算出 1000.00 Hz
真實 9000 Hz -> 取樣後視 1000.00 Hz, 折疊公式算出 1000.00 Hz

可以看到 7000 Hz 與 9000 Hz 兩個成分,取樣後都「聽起來像」1000 Hz——若不事先濾除,取樣後的 1000 Hz 有三個來源,無從分辨。

再加一個簡單的重建實驗(理想 bandlimited 插值的近似用 sinc 加權):

x = np.sin(2*np.pi*1000*t)    # 1 kHz,安全在 fs/2 以下
n = np.arange(len(x))
# sinc 插值(僅示範一個點)
recon = 0.0
for k in range(len(x)):
    recon += x[k] * np.sinc(fs*(0.0021) - k)
print("插值重建 x(2.1ms) ≈", recon)
print("真值       x(2.1ms) =" , np.sin(2*np.pi*1000*0.0021))

輸出顯示重建值與真值一致到三位小數。

複雜度分析#

  • 量測視頻率(用零穿越): \(O(N)\) 。
  • 理論計算折疊頻率: \(O(1)\) 。
  • 理想 sinc 插值重建單一時刻: \(O(N)\) (理想濾波器是無限長的,實務用 FIR 有限長近似)。
  • 抗混疊濾波(FIR,長度 \(L\) ):對 \(N\) 個樣本總共 \(O(N \cdot L)\) 。

練習#

  1. 取樣率 16 kHz,輸入一個 14 kHz 的正弦。依折疊公式求取樣後看到的假頻率;寫程式驗證。
  2. 有一個訊號同時含 2 kHz 與 10 kHz,取樣率 8 kHz。取出樣本後,兩個成分分別呈現多少 Hz?若直接把取樣結果送進 DAC,會聽到什麼?
  3. 寫一個函數 safe_sample(x, fs_old, fs_new),先用簡單的低通(4.1 節將正式介紹)將原本 fs_old 的訊號濾到 fs_new/2 以下,再以階梯取樣方式降頻到 fs_new。用 8 kHz 輸入 4.5 kHz 正弦,降頻到 2 kHz,觀察輸出還剩什麼。