云深之无迹
纵是相见,亦如不见,潇湘泪雨,执念何苦。
1677篇原创内容
公众号
我每天看大家评论区的锐评都不缺稿子:
我写频谱折叠的时候,老哥的评论
ok,他就说了个简单来说,那我这里就得展开说说;揭示了采样电路中模拟开关的“混频本质”。
在理想情况下,采样电路(Sample and Hold)中的模拟开关可以被视为一个乘法器,其作用是将输入模拟信号 与一个周期性的门控信号 相乘:
其中:
:模拟输入信号;
:脉冲序列(周期为采样周期 ),可以近似看作周期性的 Dirac comb:
因此,
这就是理想采样,也就是“乘以脉冲列”。
根据傅里叶变换的性质,时域乘法等于频域卷积:
而 是一个周期为 的冲激串:
所以:
这就是频域中的混频(频谱搬移)——把信号的频谱重复地搬移到了每个 处。
就是这个公式体现了:“采样相当于用一个本振 对输入信号混频”的频域体现。
模拟开关在打开和关闭之间“乘以1或0”;乘以一个方波,其频谱包含了基频 和奇次谐波成分;等效于将输入信号同时与多个频率的“本振”混频;所以本质上,采样行为就是一种混频行为(Mixing)。
直观地展示了采样前后的频域变化过程
上图:时域波形
黄色线是输入正弦波 ,频率为 120 Hz;红色小圆点是以 200 Hz 采样频率采样得到的离散点;可以看到采样点“乘以了一个门控函数”,即本质上形成了乘法(混频)行为。
红色小圆点:每隔一段时间去“看一眼”的点。比如你设置每 1/200 秒采一次,就是用 200Hz 的频率在“盲人摸象”;这叫“采样”——你没法持续看,只能定时瞄一下。
下图:频域谱图
黄色实线是原始信号的频谱(单一频率分量 120Hz);
橙色虚线是采样后的频谱,展示了以采样频率 为间隔的频谱重复(混频)现象;是采样后“频域发生的事”:原始的频率被复制了好多份,间隔是采样频率 200Hz。
可以清楚看到:频谱在 处重复,形成典型的混叠图案。
也就是说:
采样的本质,就像拿一个“频率锤子”(采样频率)去敲信号,信号就会把自己的频谱往左右各个方向反弹出去(复制频谱)。
你用一个“门控信号”来控制什么时候采样,这个门控信号在频域上其实含有好多频率(就像一个正弦波、三角波叠起来);当你把输入信号乘上这个门控信号,就像混频器里用一个“本振”去跟输入信号混合,结果就是——频率搬家了!(其实就是方波,开关等效,然后不纯)
这就是为什么说:
采样就像在用一个频率为采样率的本振,对信号进行混频。
如果采样得不够快(比如采样率比信号频率低),频率“搬家”后会撞到一起、搞混,这就是混叠(aliasing);这就是我们常说的:“采样频率要至少是信号最大频率的两倍”(奈奎斯特定理),否则你就不是在“采样”,而是在“破坏信号”。
混叠现象(Aliasing
上图:时间域采样
你原本的信号频率是 120 Hz;现在我们用 180 Hz 的采样频率采样(低于奈奎斯特要求的 240 Hz);你会发现:红绿点(采样点)好像沿着一个“慢慢变化”的曲线走,这不是原来的正弦波形,而是一个“假频率”!
这就是混叠的本质:你以为采到了低频信号,实际上是高频信号伪装的样子。
下图:频域解释
黄色是原始频谱(一个 120 Hz 的尖峰);绿色是采样后的频谱;可以看到原来的频率成分被复制到了 180Hz 附近,然后反折回来,落入了低频区,导致你以为这个信号频率不到 60Hz。
这个“错认频率”的现象就是混叠(aliasing):
高频信号在低采样率下,会变成错误的低频成分出现在频谱中。如果你采样太慢,信号就会“乔装打扮”,骗过你的ADC,让你以为它是另一个频率。
这就是为什么采样频率要至少是信号最高频率的2倍,否则你将永远无法知道信号的真实模样。
前面说了无数次,要加混叠滤波器:
上图:时间域波形
黄色波形:原始的 120 Hz 正弦信号;
紫色曲线:使用抗混叠滤波器后得到的信号(低通滤波,限制在 90 Hz 以下);
紫色圆点:在 180 Hz 采样率下对滤波后的信号采样的点。
可以看到:
滤波后信号变慢了,不再有高频成分,采样点准确反映了它的趋势,没有被“误导”。
下图:频率域对比
黄色线:原始信号频谱,主峰在 120Hz;
绿色虚线:没有滤波直接采样的频谱,出现了严重混叠(假频率出现在低频段);
紫色虚线:先滤波后采样的频谱,没有混叠,能保留真实频率信息。
加一个“低通滤波器”,就像在采样前给信号戴上了安全帽: 它砍掉了所有超过采样率一半的成分(> 90Hz),让你采样时不至于被“高速分量”迷惑。
几个低通滤波器,把搞不定的高频挡住:
用于抗混叠的**低通滤波器的幅频响应**(Frequency Response)
黄色色曲线:滤波器的增益(单位是 dB),表示不同频率信号通过滤波器后的“衰减程度”。
红色虚线:截止频率 ,在这个频率处,增益大约下降到 -3dB,信号能量约为一半。
低频段(<90Hz):几乎没有衰减(接近 0 dB),信号可以原样通过;
高频段(>90Hz):迅速衰减(-40 dB、-60 dB…),高频成分被压制,避免混叠;
这个滤波器是 6 阶 Butterworth,特点是:通带平坦(不会“鼓包”);过渡带不算陡峭但非常平滑;适合做前端抗混叠滤波(低噪声、相位变化小)。
from scipy.signal import butter, lfilter, freqz
# 设计一个低通滤波器(抗混叠滤波器),截止频率略低于 Nyquist = fs/2
def butter_lowpass(cutoff, fs, order=5):
nyq = 0.5 * fs # 奈奎斯特频率
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low')
return b, a
def apply_lowpass_filter(data, cutoff, fs, order=5):
b, a = butter_lowpass(cutoff, fs, order)
y = lfilter(b, a, data)
return y
# 应用抗混叠滤波器:对原始信号进行滤波后再采样
cutoff_freq = 90 # 限制最高频率 < 90Hz,避免混叠
x_filtered = apply_lowpass_filter(x, cutoff=cutoff_freq, fs=fs, order=6)
# 对滤波后的信号用低采样率采样
sampled_filtered = np.zeros_like(x)
sampled_filtered[sample_indices_low] = x_filtered[sample_indices_low]
X_sampled_filtered = fftshift(np.abs(fft(sampled_filtered)))
# 画图对比
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, x, label="Original Signal", alpha=0.4)
plt.plot(t, x_filtered, label="Filtered Signal (<90 Hz)", linewidth=1.2)
plt.stem(t[sample_indices_low], sampled_filtered[sample_indices_low], linefmt='m-', markerfmt='mo', basefmt=" ", label="Sampled (Filtered)", use_line_collection=True)
plt.title("Time Domain: Anti-Aliasing Filter Before Sampling")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.legend()
plt.subplot(2, 1, 2)
plt.plot(freq, X / N, label="Original Spectrum", alpha=0.4)
plt.plot(freq, X_sampled_low / N, label="Sampled w/o Filter", linestyle='--', color='green')
plt.plot(freq, X_sampled_filtered / N, label="Sampled w/ Filter", linestyle='--', color='magenta')
plt.title("Frequency Domain: With and Without Anti-Aliasing Filter")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Magnitude")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# 获取滤波器频响(幅频特性)
b, a = butter_lowpass(cutoff=cutoff_freq, fs=fs, order=6)
w, h = freqz(b, a, worN=8000)
freq_resp = (fs * 0.5 / np.pi) * w # 转换频率单位为 Hz
# 绘图
plt.figure(figsize=(10, 4))
plt.plot(freq_resp, 20 * np.log10(abs(h)), label="Low-pass Filter (cutoff=90Hz)")
plt.axvline(cutoff_freq, color='r', linestyle='--', label="Cutoff Frequency")
plt.title("Frequency Response of Anti-Aliasing Low-Pass Filter")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Gain [dB]")
plt.ylim(-80, 5)
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()