信号基础:时域与频域的基本概念,采样定理入门
各位同学,欢迎来到第二讲。
上一章我们聊了为什么要做滤波,以及噪声从哪来。今天咱们要打一个真正的地基——时域与频域。说实话,我当年在学校学信号与系统时,也觉得这东西太抽象了,不就是一堆正弦波嘛。直到后来做项目,遇到一个死活滤不掉的50Hz工频干扰,才真正明白:不懂频域,你连噪声长什么样都看不清楚。
好,咱们开始。
1. 时域:我们最熟悉的视角
时域,说白了就是信号随时间怎么变。
你拿示波器看一个波形,横轴是时间,纵轴是电压——这就是时域。我们平时采集到的传感器数据,比如温度、加速度、心率,都是时域信号。
举个例子:
// 一个简单的正弦波时域表示
// 幅度 A = 1.0,频率 f = 50Hz,采样率 fs = 1000Hz
for (int n = 0; n < 1000; n++) {
float t = n / 1000.0f; // 时间:0 ~ 1秒
float x = 1.0f * sin(2 * PI * 50 * t);
printf("%f\n", x);
}
这段代码输出的是50Hz正弦波在1秒内的采样点。你在纸上画出来,就是一条上下起伏的波浪线。嗯,这就是时域。
时域的核心问题:信号里有哪些频率成分?它们各自多大?——时域很难直接回答。
我在做电机振动监测时,采集到的加速度信号看起来乱糟糟的,根本分不清是机械共振还是电气噪声。时域波形像一锅粥。这时候,就得请出频域了。
2. 频域:换个角度看世界
频域看的是信号由哪些频率的正弦波组成。
你想想看,一个复杂的信号,其实可以拆成很多个不同频率、不同幅度、不同相位的正弦波加起来。这就是傅里叶变换的核心思想——任何周期信号都可以分解为一系列正弦波的叠加。
我习惯用一个比喻:时域是菜谱上的配料表,频域是告诉你每种调料放了多少克。你看一道菜的颜色(时域),很难猜出放了多少盐;但如果你看成分分析表(频域),一目了然。
来个直观的例子:
// 合成信号:50Hz + 120Hz 两个正弦波叠加
float signal[1000];
for (int n = 0; n < 1000; n++) {
float t = n / 1000.0f;
signal[n] = 1.0f * sin(2 * PI * 50 * t)
+ 0.5f * sin(2 * PI * 120 * t);
}
这个信号在时域里看起来是两个正弦波叠加后的复杂波形。但如果你对它做FFT(快速傅里叶变换),频域里会清晰地看到两个尖峰:一个在50Hz处,幅度1.0;另一个在120Hz处,幅度0.5。
个人经验:我刚开始做嵌入式滤波时,总想着在时域里把噪声“抠”出来。后来发现,先看频域再设计滤波器,效率高十倍。因为频域告诉你:哪些频率是信号,哪些是噪声,一目了然。
3. 时域与频域的关系:一枚硬币的两面
时域和频域不是对立的,它们是同一个信号的两种描述方式。就像一个人有中文名和英文名,指的是同一个人。
转换工具就是傅里叶变换:
- 时域 → 频域:傅里叶变换(或FFT)
- 频域 → 时域:逆傅里叶变换(IFFT)
我画了一张图,帮你理解这个关系:
这张图很关键。你每次做滤波,其实都是在做一件事:在频域里识别噪声,在时域里把它干掉。
4. 采样定理:数字世界的基石
好,现在问题来了:我们嵌入式系统里处理的都是数字信号,也就是对模拟信号进行采样得到的。那么,采样频率该设多高?
这就引出了数字信号处理里最重要的一条规则——奈奎斯特采样定理:
采样频率必须大于信号最高频率的两倍,否则会发生混叠(Aliasing)。
公式:fs > 2 * fmax
为什么会这样?我简单解释一下:
假设你有一个100Hz的正弦波,你用150Hz去采样。那么采样点看起来会像一个50Hz的波形——这就是混叠。高频信号“伪装”成了低频信号,你根本分不清。
我曾经踩过这个坑。做一个声音采集项目,麦克风能听到20kHz,我偷懒用了30kHz采样。结果录出来的声音里总有奇怪的“嗡嗡”声,怎么都滤不掉。后来一查,是20kHz附近的超声信号混叠到了10kHz附近,进入了可听频段。嗯,从那以后我再也不敢低于2.5倍采样了。
避坑指南:实际工程中,我建议采样率取信号最高频率的3~5倍。因为抗混叠滤波器不是理想砖墙,需要留过渡带。比如你要采集1kHz以内的信号,采样率至少设3kHz以上。
5. 一个完整的例子:从时域到频域
咱们用代码走一遍流程。假设你有一个传感器信号,包含50Hz的有用信号和200Hz的噪声:
#include <stdio.h>
#include <math.h>
#define FS 1000 // 采样率 1000Hz
#define N 1024 // 采样点数
int main() {
float signal[N];
float t;
// 生成信号:50Hz有用信号 + 200Hz噪声
for (int i = 0; i < N; i++) {
t = (float)i / FS;
signal[i] = 1.0f * sin(2 * PI * 50 * t) // 有用信号
+ 0.3f * sin(2 * PI * 200 * t); // 噪声
}
// 此时 signal[] 是时域数据
// 你可以对它做FFT,得到频域数据
// 在频域里你会看到:
// - 50Hz处有一个高峰(幅度1.0)
// - 200Hz处有一个小峰(幅度0.3)
// 然后你就可以设计低通滤波器,把200Hz干掉
return 0;
}
这个例子虽然简单,但包含了滤波的全部逻辑:时域采集 → 频域分析 → 设计滤波器 → 时域滤波。
6. 本章知识体系总览
最后,我用一张图把今天的内容串起来:
这张图把今天的内容串成了一个整体。你记住:时域是采集和滤波的战场,频域是分析和设计的参谋,采样定理是保证一切成立的底线。
我的建议:初学者可以先不用深究FFT的数学细节。先学会看频谱图,能分辨出信号和噪声的频率范围,这就够了。数学推导可以后面慢慢补。
好,今天就到这儿。下一章咱们正式进入滤波器设计,从最简单的移动平均滤波开始,一步步搭建你自己的滤波工具箱。
公众号:蓝海资料掘金营,微信deep3321