3. 采样定理验证

任务目标

对高频正弦波以不同采样率进行采样,通过观察频谱中混叠成分的位置与幅度,直观验证奈奎斯特采样定理:当采样率 fs < 2·f0 时,信号频率将折叠至 [0, fs/2] 区间内,无法由采样值唯一重建。

环境与依赖

Python 3.8+,仅需 NumPy 与 Matplotlib。无需其他第三方库。

核心原理与实现

设连续正弦波频率为 f0 = 800 Hz,分别采用三种采样率:

  • fs1 = 2000 Hz(满足奈奎斯特条件,fs1 > 2·f0)
  • fs2 = 1200 Hz(不满足,fs2 < 2·f0)
  • fs3 = 1000 Hz(临界欠采样,fs3 = 2·f0/1.6,混叠最严重)

对每种采样率生成 N = 2048 个采样点,计算 FFT 并绘制幅度谱。混叠频率的理论值为 |f0 - k·fs| 中落在 [0, fs/2] 的最小值,代码中直接标注该理论值供比对。

完整代码

import numpy as np
import matplotlib.pyplot as plt

# 参数设置
f0 = 800.0          # 原始信号频率 (Hz)
N = 2048            # 采样点数
fs_list = [2000.0, 1200.0, 1000.0]   # 三种采样率 (Hz)
t_max_list = [N / fs for fs in fs_list]  # 每种采样率对应的总时长

# 生成连续时间基准(用于绘制原始波形,仅作参考)
t_cont = np.linspace(0, 0.01, 2000)
x_cont = np.sin(2 * np.pi * f0 * t_cont)

fig, axes = plt.subplots(3, 1, figsize=(10, 9))
fig.suptitle(f'采样定理验证:f0 = {f0:.0f} Hz', fontsize=14)

for idx, fs in enumerate(fs_list):
    # 采样
    n = np.arange(N)
    t = n / fs
    x = np.sin(2 * np.pi * f0 * t)

    # FFT 与频率轴
    X = np.fft.fft(x) / N
    freq = np.fft.fftfreq(N, d=1.0 / fs)
    half = N // 2
    mag = np.abs(X[:half])
    freq_half = freq[:half]

    # 理论混叠频率(折叠到 [0, fs/2])
    alias = np.abs(f0 - np.round(f0 / fs) * fs)
    if alias > fs / 2:
        alias = fs - alias

    # 绘制频谱
    ax = axes[idx]
    ax.stem(freq_half, mag, linefmt='C0-', markerfmt='C0o', basefmt='C1-', label='FFT幅度谱')
    ax.axvline(fs / 2, color='red', linestyle='--', linewidth=1.2, label=f'fs/2 = {fs/2:.0f} Hz')
    ax.axvline(f0, color='green', linestyle=':', linewidth=1.2, label=f'原始频率 {f0:.0f} Hz')
    if alias != f0:  # 有混叠时标注
        ax.axvline(alias, color='purple', linestyle='-.', linewidth=1.5,
                   label=f'混叠频率 ≈ {alias:.1f} Hz')
    ax.set_xlim(0, fs / 2 + 200)
    ax.set_ylim(0, 0.6)
    ax.set_xlabel('频率 (Hz)')
    ax.set_ylabel('幅度')
    ax.set_title(f'采样率 fs = {fs:.0f} Hz({"满足" if fs >= 2*f0 else "不满足"}奈奎斯特条件)')
    ax.legend(fontsize=8)
    ax.grid(alpha=0.3)

plt.tight_layout()
plt.savefig('sampling_theorem.png', dpi=150)
plt.show()

# 控制台输出验证信息
print('=== 采样定理验证结果 ===')
for fs in fs_list:
    alias = np.abs(f0 - np.round(f0 / fs) * fs)
    if alias > fs / 2:
        alias = fs - alias
    status = '满足' if fs >= 2 * f0 else '不满足'
    print(f'fs = {fs:5.0f} Hz | {status} | 理论混叠频率 = {alias:6.1f} Hz')

运行方式

python sampling_theorem.py

验证方法与判定标准

  1. 频谱峰值位置:观察每个子图中幅度谱的最大峰值频率。当 fs = 2000 Hz 时,峰值应位于 800 Hz;当 fs = 1200 Hz 时,峰值应位于 400 Hz(即 800 - 1200×round(800/1200) = 800-1200×1 = -400,取值后折叠到 400 Hz);当 fs = 1000 Hz 时,峰值应位于 200 Hz(800 - 1000×1 = -200,折叠到 200 Hz)。
  2. 控制台输出比对:程序打印的理论混叠频率应与频谱图中紫色虚线位置一致,且与上述手算值吻合。
  3. 判定通过条件:三个子图的峰值频率分别等于各自的理论混叠频率(误差小于一个频率分辨率 fs/N),且 fs = 2000 Hz 时峰值位于原始频率处无偏移,即验证了采样定理。

预期输出(确定性结论)

=== 采样定理验证结果 ===
fs =  2000 Hz | 满足 | 理论混叠频率 =  800.0 Hz
fs =  1200 Hz | 不满足 | 理论混叠频率 =  400.0 Hz
fs =  1000 Hz | 不满足 | 理论混叠频率 =  200.0 Hz

该输出由代码逻辑直接唯一确定,非实测数据。频谱图中三个峰值将分别出现在 800 Hz、400 Hz、200 Hz 处,直观展示欠采样导致的频率折叠现象。

奈奎斯特采样定理示意图(f₀ = 800 Hz) fs = 2000 Hz(满足) fs/2=1000 800 Hz ✓ 无混叠 fs = 1200 Hz(不满足) fs/2=600 400 Hz ✗ 混叠 800 fs = 1000 Hz(不满足) fs/2=500 200 Hz ✗ 混叠 800 当 fs < 2f₀ 时,频谱峰值从 f₀ 折叠至 |f₀ - k·fs| 处(k 为整数),无法恢复原始信号