信号处理在做什么:从采样到频谱
信号处理的日常工作,说白了就是两件事:看清信号里藏着什么频率成分(频谱分析),以及把不想要的成分去掉(滤波)。麦克风录音、传感器读数、雷达回波,本质都是一串随时间变化的数字序列,MATLAB 就是处理这种序列最顺手的工具。
一个连续信号先被"采样"成一串离散数字——每隔一小段时间取一个值,这个间隔对应的频率叫采样率 Fs。有了采样率,你才知道一个波形里到底是 50 赫兹还是 500 赫兹。
先定采样率,再谈信号。采样率决定了你能看清多快的变化,也决定了哪些频率会被"折叠"看不清。
- Fs采样率,每秒采样点数,是一切频谱分析的基准。
- sin(2*pi*f*t)生成一个频率为 f 的正弦测试信号。
- fft把时域波形变换成频域,看清各频率能量。
- butter/filter设计并应用数字滤波器,保留目标频段。
生成测试信号:正弦波加噪声
没有真实采集数据也能练:用公式自己造一个"干净信号加噪声"。下面造一段一秒钟、采样率 1000 的信号,主体是 50 赫兹正弦波,再叠一点随机噪声模拟真实干扰。
Fs = 1000; % 采样率 1000 Hz
t = 0 : 1/Fs : 1; % 时间轴,1 秒
x = sin(2*pi*50*t); % 干净的 50Hz 正弦波
n = 0.5*randn(size(t)); % 高斯噪声
y = x + n; % 带噪观测信号
randn 生成均值为 0 的随机数,乘 0.5 控制噪声强度。这一行行向量就是"传感器采回来的一整段数据",后面所有分析和滤波都围绕它展开。
画时域波形与频谱
先画时域波形看长什么样,再用 fft 转成频谱,看能量集中在哪个频率。频域图会在 50 赫兹处冒出一个明显的峰——这就是信号的真身。
subplot(2,1,1);
plot(t, y); title('时域波形'); xlabel('时间(s)');
Y = fft(y);
f = (0:length(Y)-1)*(Fs/length(Y)); % 频率轴
subplot(2,1,2);
plot(f(1:Fs/2), abs(Y(1:Fs/2))); % 只画前半段
title('频谱'); xlabel('频率(Hz)');
频谱只画前半段(Fs/2 以内),是因为实信号的频谱关于 Fs/2 对称,后半段是镜像。你会看到 50 赫兹处一个尖峰,其余是噪声铺出的低能量底——肉眼就能把信号和噪声分开。
滤波:把噪声挡在外面
既然知道信号在 50 赫兹附近,就设计一个滤波器把它留住、把别的频率压掉。butter 生成滤波器系数,filter 把它应用到信号上。
% 设计一个带通滤波器,只留 40~60Hz
[b, a] = butter(4, [40 60]/(Fs/2));
z = filter(b, a, y); % 滤波后的信号
plot(t, z); title('滤波后波形'); xlabel('时间(s)');
第二个参数 [40 60]/(Fs/2) 要除以奈奎斯特频率(Fs/2)归一化,这是 butter 固定的约定。滤波后再看波形,噪声明显小了,50 赫兹那一段干净地立出来——这就是滤波器的效果。
仿真:给信号过一个系统
所谓"仿真",就是先在电脑里模拟一个真实系统会怎么改变信号,而不用真的搭硬件。滤波器本身就是一种系统;你也可以用它模拟更复杂的环节,比如信号通过一个会衰减、会延迟的通道。
% 模拟一个简单系统:信号先衰减一半、再延迟
h = [0.5 zeros(1,10) 0.5]; % 系统脉冲响应
out = filter(h, 1, y); % 信号通过该系统
plot(t, y, t, out); legend('输入','输出');
仿真的价值在于"可重复、可调参":改一改系数立刻看到输出怎么变,代价远低于真去调硬件。工程上很多算法都是先在 MATLAB 里仿真验证,再移植到嵌入式设备上。
采样率与混叠的直觉
有一条不能违反的线:采样率必须高于信号最高频率的两倍,否则高频会被"折叠"成低频假象,这叫混叠。直观理解——采样太慢,你连信号一个完整周期都没采到,自然画不出它真实的快慢。
- 要看清 f 赫兹的信号,采样率至少要 2f,工程上常取 5 到 10 倍更稳妥。
- 采样率定错,频谱分析就全错——它是所有后续计算的地基。
- 真实采集里,采样前常加一个模拟低通滤波器,先把超过 Fs/2 的频率挡掉,从源头防混叠。
实战:带噪信号滤波全流程
把整条链串起来:造信号、画频谱找峰、设计滤波、看结果。这就是一个信号处理工程师每天在做的最小闭环。
Fs = 1000; t = 0:1/Fs:1;
x = sin(2*pi*50*t) + 0.5*randn(size(t)); % 带噪信号
% 1. 看频谱找信号频率
Y = fft(x); f = (0:length(Y)-1)*(Fs/length(Y));
plot(f(1:Fs/2), abs(Y(1:Fs/2)));
% 2. 围绕找到的峰设计带通滤波
[b,a] = butter(4, [40 60]/(Fs/2));
z = filter(b, a, x);
% 3. 对比输入输出
plot(t, x, t, z); legend('带噪','滤波后');
从这几步出发,你可以换成自己关心的频率:想分离电机的振动频率、想提取心电里的心搏节律,方法完全一样——先看频谱定位,再按频段滤波,最后对比效果。
信号处理检查清单
把 MATLAB 信号处理记成一条链:定采样率 → 造/读信号 → fft 看频谱 → 设计滤波 → 对比输入输出。提交结果前对照自查:
- 采样率 Fs 是否明确,并且高于信号最高频率的两倍?
- 频谱只画了 Fs/2 以内,还是不小心把对称的后半段也当成新信息?
- 滤波器的归一化频率是否除以了 Fs/2?
- 滤波前后是否画在一起对比,让人一眼看到噪声被压下去了?
- 仿真参数是否可复现,每一步改了什么、输出怎么变都说得清?
信号处理的直觉,是"在时域看到的混乱,到频域往往清清楚楚"。先做频谱,再谈怎么滤,方向就不会错。
下一篇我们回到 C++ 的另一面:当多个线程同时跑起来,怎么用互斥与原子操作让它们既快又不打架。