MATLAB 采样混叠实验 20 Hz 和 80 Hz 为什么会得到相同数据

实测环境:MATLAB 25.2.0.2998904(R2025b),Windows 64 位(PCWIN64),2026-10-15。基础 MATLAB 即可,无额外工具箱、无外部数据。把文末代码保存为 aliasing_demo.m,将当前文件夹切换到文件所在目录,运行:

matlab 复制代码
results = aliasing_demo;

程序执行数值断言并生成 results/01_aliasing.png 和 results/01_aliasing.mat。本次实际运行的 5 个断言均通过,日志输出 ALIASING PASS,下图由同次 MATLAB 运行导出。

采样率设置为 100 Hz 时,一个 80 Hz 的余弦信号,可能与一个 20 Hz 的余弦信号留下完全一样的样本。两条连续曲线的变化速度不同,采样时刻的数值却相同。仅凭这些样本,后续算法就无法唯一判断原信号是哪一个。

这个实验只用确定性的单频信号,在时域直接检查样本相等,并进一步观察提高采样率和改变相位的影响。

先把频率和时间写清楚

连续信号取为:

x f ( t ) = cos ⁡ ( 2 π f t ) x_f(t)=\cos(2\pi f t) xf(t)=cos(2πft)

其中,f 的单位为 Hz,t 的单位为秒,余弦的输入是弧度。MATLAB 的 cos 按元素计算弧度余弦,相关接口见 R2025b cos 文档。

均匀采样时,t[n]=n/Fs,得到:

x f n = cos ⁡ ( 2 π f F s n ) x_fn=\cos\left(2\pi\frac{f}{F_s}n\right) xfn=cos(2πFsfn)

本例令 Fs=100,依次计算 f=[20,80,120]。时间向量写成 (0:100)'/Fs,因此是从 0 秒到 1 秒的 101 个样本,包含两个端点。代码使用整数样本序号再除以采样率,读者可以直接核对采样间隔与样本数。

cos(2*pi*t*f) 中的 t 是 101×1 列向量,f 是 1×3 行向量,两者相乘形成 101×3 的相位矩阵,每一列对应一个频率。

为什么三个频率会重合

把 80 Hz 写成 100-20,有:

cos ⁡ ( 2 π 80 100 n ) = cos ⁡ ( 2 π n − 0.4 π n ) = cos ⁡ ( 0.4 π n ) \cos\left(2\pi\frac{80}{100}n\right)=\cos(2\pi n-0.4\pi n)=\cos(0.4\pi n) cos(2π10080n)=cos(2πn−0.4πn)=cos(0.4πn)

把 120 Hz 写成 100+20,同样有:

cos ⁡ ( 2 π 120 100 n ) = cos ⁡ ( 2 π n + 0.4 π n ) = cos ⁡ ( 0.4 π n ) \cos\left(2\pi\frac{120}{100}n\right)=\cos(2\pi n+0.4\pi n)=\cos(0.4\pi n) cos(2π100120n)=cos(2πn+0.4πn)=cos(0.4πn)

对每一个整数 n,两式都成立。这里不依赖画图分辨率,也不依赖采集持续时间。即便继续按同一个采样率采集更多样本,这三个理想信号仍然无法区分。

代码额外计算折叠到 [0, Fs/2] 内的频率:

matlab 复制代码
foldedHz = abs(mod(f + Fs/2, Fs) - Fs/2);

本例应得到 [20 20 20]。这是实信号频率折叠的一种方便写法。它只表示等效频率位置,不能替代幅度、符号和相位分析。例如,零相位的 80 Hz 正弦在这里对应负的 20 Hz 正弦;本文选择零相位余弦,是为了让样本相等关系最直观。

理论相等为什么还要给容差

MATLAB 使用浮点数表示 pi 并计算三角函数,理论上相同的两个值可能出现最后几位的差异。因此程序检查最大绝对误差小于 1e-11,而不使用逐元素严格相等。

这项断言适用于本例的频率、样本数量和双精度计算。不要把 1e-11 直接当作所有采集数据的误差标准,真实传感器还有噪声、量化和时间抖动。

刚好达到两倍频率也要检查相位

再令信号频率为 50 Hz,仍以 100 Hz 采样:

s n = sin ⁡ ( π n + ϕ ) = ( − 1 ) n sin ⁡ ϕ sn=\sin(\pi n+\phi)=(-1)^n\sin\phi sn=sin(πn+ϕ)=(−1)nsinϕ

当 phi=0 时,理论上的所有样本都是 0;当 phi=pi/2 时,样本交替为 +1 和 -1。连续信号都在振荡,采样结果却可能一个几乎全零,一个幅度为 1。

这个边界例子说明:对包含未知相位正弦分量的一般测量任务,不能只把采样率设成最高关注频率的两倍就结束设计。若采用通常的低通信号采样场景,应让实际最高有效频率严格低于 Fs/2,并给模拟滤波器过渡带和频率不确定性留余量。MathWorks 的采样质量说明也强调了高频分量造成混叠,以及采样前信号调理的重要性。

把采样率提高到 400 Hz 后会怎样

程序重新用 FsHigh=400 计算样本。此时三个测试频率都低于 200 Hz 的奈奎斯特频率,20 Hz 与 80 Hz 的样本不再重合。两列样本的最大差值理论上为 2,程序以大于 1.9 作为区分检查。

这里的"重新计算"对应重新采集连续信号。对已经以 100 Hz 获得的那组数据做插值,无法重新得到被混叠隐藏的真实 80 Hz 变化。延长记录、把曲线画得更密、换一个频率估计算法,也不能凭空补上这些信息。

真实采集时通常需要同时处理两件事:按照有效带宽选择采样率,并在采样前抑制不应进入测量带宽的高频成分。如果先以很高采样率取得数字数据,再降低采样率,则应在降采样之前进行合适的数字抗混叠滤波。滤波的位置必须在产生新混叠的采样操作之前。

运行后核对哪些结果

本次 MATLAB 实测结果与前面的解析推导一致:

  • 折叠频率为 [20 20 20] Hz
  • 100 Hz 采样时,80 Hz 和 120 Hz 样本相对 20 Hz 样本的最大误差均为 8.948397578478762e-14
  • 50 Hz 零相位正弦的样本最大绝对值为 3.136581359345036e-14
  • 400 Hz 新采样下,20 Hz 与 80 Hz 样本的最大差值为 2
  • 全部数值检查和图片导出成功,日志记录 ALIASING PASS

图 01_aliasing.png 包含四个面板:左上对比 20 Hz、80 Hz 连续曲线及共同的低速样本;右上叠加三组低速样本;左下展示 400 Hz 重新采样后的差别;右下比较奈奎斯特频率处的两个相位。图中连线只辅助观察样本,不声称是从样本唯一重建出的连续信号。

把实验迁移到自己的采集任务

先列出真正需要测量的最高频率,以及可能混入的工频干扰、振动谐波或开关噪声。然后检查采样前的模拟带宽和滤波条件,再决定采样率。若发现异常低频峰,应把可能的高频来源按采样率折叠计算,核对它是否可能是混叠结果。

本例证明的是采样信息不唯一这一件事。它没有模拟 ADC 动态范围、时钟抖动或真实抗混叠滤波器,也不能据此给某台设备直接确定采样参数。

完整可运行代码

保存为 aliasing_demo.m,文件名必须与第一行函数名一致。

matlab 复制代码
function results = aliasing_demo(outDir)
% Deterministic sampling-aliasing experiment. Base MATLAB, R2025b target.
% Run: results = aliasing_demo;
if nargin < 1
    outDir = fullfile(fileparts(mfilename('fullpath')), 'results');
end
if ~exist(outDir, 'dir'), mkdir(outDir); end
Fs = 100;
f = [20 80 120];
t = (0:100)' / Fs;
x = cos(2*pi*t*f);
foldedHz = abs(mod(f + Fs/2, Fs) - Fs/2);
err80 = max(abs(x(:,1)-x(:,2)));
err120 = max(abs(x(:,1)-x(:,3)));
assert(isequal(foldedHz, [20 20 20]), 'Frequency folding failed.');
assert(err80 < 1e-11 && err120 < 1e-11, 'Alias samples differ.');
% At exactly Fs/2 the sampled amplitude depends on phase.
xNyquist = sin(2*pi*(Fs/2)*t);
xNyquistPhase = sin(2*pi*(Fs/2)*t + pi/2);
assert(max(abs(xNyquist)) < 1e-11, 'Nyquist zero-phase test failed.');
assert(max(abs(xNyquistPhase - (-1).^(0:100)')) < 1e-11, ...
    'Nyquist phase test failed.');
% This is a NEW acquisition at a higher sampling rate, not interpolation.
FsHigh = 400;
tHigh = (0:400)' / FsHigh;
xHigh = cos(2*pi*tHigh*f);
separationHigh = max(abs(xHigh(:,1)-xHigh(:,2)));
assert(separationHigh > 1.9, 'Higher-rate samples should differ.');
tFine = linspace(0, 0.1, 2001)';
fig = figure('Color','w','Position',[100 100 1100 750]);
tiledlayout(2,2,'TileSpacing','compact','Padding','compact');
nexttile;
plot(tFine, cos(2*pi*20*tFine), '-', ...
     tFine, cos(2*pi*80*tFine), '--', 'LineWidth',1.2); hold on;
stem(t(1:11),x(1:11,1),'k','filled','MarkerSize',4);
xlabel('Time (s)'); ylabel('Amplitude'); grid on;
title('20 Hz and 80 Hz: Fs = 100 Hz');
legend('20 Hz continuous','80 Hz continuous','Shared samples', ...
    'Location','southoutside');
nexttile;
plot(t(1:11),x(1:11,1),'o-',t(1:11),x(1:11,2),'x--', ...
     t(1:11),x(1:11,3),'+:','LineWidth',1.2);
xlabel('Time (s)'); ylabel('Amplitude'); grid on;
title('20, 80, 120 Hz give the same samples');
legend('20 Hz','80 Hz','120 Hz','Location','southoutside');
nexttile;
plot(tHigh(1:41),xHigh(1:41,1),'o-', ...
     tHigh(1:41),xHigh(1:41,2),'x-', 'LineWidth',1.0);
xlabel('Time (s)'); ylabel('Amplitude'); grid on;
title('New acquisition: Fs = 400 Hz');
legend('20 Hz','80 Hz','Location','southoutside');
nexttile;
stem(0:10,xNyquist(1:11),'filled'); hold on;
stem(0:10,xNyquistPhase(1:11),'x');
xlabel('Sample index n'); ylabel('Amplitude'); grid on;
title('50 Hz at Fs = 100 Hz: phase matters');
legend('Phase 0','Phase pi/2','Location','southoutside');
exportgraphics(fig,fullfile(outDir,'01_aliasing.png'),'Resolution',160);
results = struct('Fs',Fs,'foldedHz',foldedHz,'maxError80',err80, ...
    'maxError120',err120,'maxAbsNyquist',max(abs(xNyquist)), ...
    'higherRateMaxDifference',separationHigh);
fprintf('ALIASING folded_Hz = [%g %g %g]\n',foldedHz);
fprintf('ALIASING max_error_80 = %.16g; max_error_120 = %.16g\n',err80,err120);
fprintf('ALIASING max_abs_Nyquist = %.16g; higher_rate_max_difference = %.16g\n', ...
    results.maxAbsNyquist,separationHigh);
fprintf('ALIASING PASS\n');
save(fullfile(outDir,'01_aliasing.mat'),'results');
end

参考资料

文中的数据和实验代码均为本文构造。

相关推荐
weixin_307779131 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab
weixin_307779133 小时前
C++代码实现MATLAB中的spectralcluster函数功能
开发语言·c++·算法·matlab
孙琦Ray3 小时前
开源MATLAB工具LIBquiv:650小时老化仿真仅139秒
matlab·开源·锂离子电池·等效电路模型·电池老化·libquiv·电池建模
phoenix@Capricornus4 小时前
LDA 协方差矩阵(MATLAB `fitcdiscr`)
算法·matlab·矩阵
lisw055 小时前
当生物阻抗遇上生物力学:EIM技术实现肌肉状态精准感知
驱动开发·信号处理
星夜离尘5619 小时前
MATLAB 线性去趋势为何损伤低频正弦:联合最小二乘估计幅相与漂移
开发语言·matlab
思茂信息19 小时前
电磁仿真学涉及哪些算法?
人工智能·算法·数学建模·matlab·emc·电磁仿真·仿真辐射
星夜离尘561 天前
MATLAB 定点量化误差实验 半个 LSB 的界限为什么挡不住削顶失真
开发语言·matlab
鲁邦通物联网1 天前
PLC远程监控系统怎么设计边缘计算网关:从状态机、心跳到异常恢复的工程方法
边缘计算·数据采集·工业数据采集·边缘计算网关·5g数采·plc数采·厂内数采