嵌入式工程师视角的傅里叶变换完全指南

下面用嵌入式工程视角来讲傅里叶变换。你可以先记住一句话:

傅里叶变换就是:把一个随时间变化的信号,拆成一堆不同频率的正弦波,看看每个频率占多少。


1. 先从一个生活类比开始

假设你听到一段声音:

嗡嗡嗡——滋滋滋——

你在时间上看到的只是一个复杂波形,就像示波器上的曲线:

时域信号: 电压/电流/声音幅值 随 时间 变化

但你真正关心的可能是:

  • 里面有没有 50 Hz 工频干扰?
  • 有没有 1 kHz 的噪声?
  • 电机有没有某个异常振动频率?
  • 通信信号的主频是多少?
  • 音频里面是哪几个音调?

这时候你不想只看“波形长什么样”,你想知道:

这个信号由哪些频率组成?
每个频率有多强?

这就是傅里叶变换要做的事情。


2. 时域 vs 频域

嵌入式里你平时采 ADC,拿到的是这样的数据:

1
adc_buf[0], adc_buf[1], adc_buf[2], ...

这叫时域数据,也就是信号随时间变化的情况。

例如:

时间 n0123456
ADC值203021002200210020301950

傅里叶变换之后,你得到的是频域数据

频率0Hz50Hz100Hz200Hz500Hz
幅值31205602

意思是:

  • 50 Hz 成分很强
  • 200 Hz 成分也有
  • 其他频率较弱

所以:

  • 时域:看波形怎么随时间变化
  • 频域:看信号由哪些频率组成

3. 傅里叶变换的核心直觉

任何复杂的周期信号,都可以看成很多正弦波叠加出来的。

例如下面这个信号:

$$ x(t) = 1.0 \cdot \sin(2\pi \cdot 50t) + 0.5 \cdot \sin(2\pi \cdot 200t) $$

它的意思是:

  • 有一个 50 Hz 的正弦波,幅值是 1.0
  • 有一个 200 Hz 的正弦波,幅值是 0.5
  • 两个波叠加后,形成一个复杂波形

在示波器上,你看到的是叠加后的结果,可能不容易看出里面有什么。

但做傅里叶变换后,会看到:

  • 50 Hz 位置有一个峰
  • 200 Hz 位置有一个峰

也就是说,傅里叶变换帮你把“混在一起的频率”分开了

注:正弦函数公式 $$y=A \cdot sin(ωx+φ)+k$$ 中:

A 为振幅(纵向伸缩)

ω 决定周期(横向伸缩)

φ 为初相(左右平移)

k 为垂直位移(平衡位置)


4. 傅里叶变换到底怎么“找频率”?

先不用看公式,先理解它的工作方式。

傅里叶变换会拿很多不同频率的“标准正弦波”去和你的信号做匹配:

  1. 10 Hz 的正弦波去试一下:像不像?
  2. 20 Hz 的正弦波去试一下:像不像?
  3. 50 Hz 的正弦波去试一下:像不像?
  4. 100 Hz 的正弦波去试一下:像不像?

如果你的原始信号里真的有 50 Hz 成分,那么用 50 Hz 的正弦波去匹配时,结果会很大

如果没有 80 Hz 成分,用 80 Hz 去匹配时,正负会互相抵消,结果接近 0

所以傅里叶变换本质上是在问:

我的信号里,和某个频率的正弦波有多像?


5. 对嵌入式工程师来说,真正常用的是 DFT / FFT

严格来说,傅里叶变换有好几种:

名称用途
傅里叶级数分析周期连续信号
连续傅里叶变换 (FT)分析连续时间信号
离散傅里叶变换 (DFT)分析采样后的离散数据
快速傅里叶变换 (FFT)DFT 的快速算法

在 MCU / DSP / 上位机程序里,你一般不是直接用“连续傅里叶变换”,而是用:

ADC采样数据 → DFT/FFT → 频谱

实际项目中你最常见的是 FFT

FFT 不是一种新的变换,它只是快速计算 DFT 的算法

简单理解:

  • DFT:计算频谱,比较慢
  • FFT:计算同样的频谱,但速度快很多

6. DFT 的公式,看懂含义就行

DFT 公式是:

$$ X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j \cdot 2\pi \cdot k \cdot n / N} $$

你不需要一开始就死记这个公式,只要理解每个量的含义:

符号含义
$x[n]$第 n 个采样点
$N$总采样点数
$k$第 k 个频率点,也叫 bin
$X[k]$第 k 个频率上的复数结果
$e^{-j \cdot 2\pi \cdot k \cdot n / N}$可以理解为一组正弦/余弦测试波

对嵌入式工程来说,最重要的是这个关系:

第 k 个频点对应的实际频率:

$$ f[k] = k \cdot \frac{F_s}{N} $$

其中:

符号含义
$F_s$采样率 (Hz)
$N$FFT 点数
$k$频率索引 (0 ~ N-1)
$f[k]$第 k 个频点对应的 Hz

这个公式非常重要!


7. 例子:采样率 1000 Hz,FFT 点数 1000

假设你用 ADC 采样:

  • $F_s = 1000 \text{ Hz}$
  • $N = 1000$ 点

那么采样总时长是:

$$ T = \frac{N}{F_s} = \frac{1000}{1000} = 1 \text{ 秒} $$

频率分辨率是:

$$ \Delta f = \frac{F_s}{N} = \frac{1000}{1000} = 1 \text{ Hz} $$

也就是说 FFT 结果中:

k 索引对应频率
k = 00 Hz (DC)
k = 11 Hz
k = 22 Hz
k = 5050 Hz
k = 200200 Hz

如果你的信号是:

$$ x(t) = \sin(2\pi \cdot 50t) + 0.5 \cdot \sin(2\pi \cdot 200t) $$

那么 FFT 之后,你会在:

  • $k = 50$
  • $k = 200$

附近看到明显峰值。


8. 频率分辨率是什么?

频率分辨率是 FFT 中非常重要的概念。

公式:

$$ \Delta f = \frac{F_s}{N} $$

它表示两个相邻频点之间相隔多少 Hz

例如:

  • $F_s = 1000 \text{ Hz}$
  • $N = 256$

那么:

$$ \Delta f = \frac{1000}{256} \approx 3.90625 \text{ Hz} $$

频率点分别是:

k频率 (Hz)
00
13.90625
27.8125
311.71875
415.625

这意味着 FFT 不是每一个 Hz 都能精确显示,而是按一个个 bin 显示。

如果你的信号是 50 Hz,它不一定刚好落在某个 bin 上。

比如:

  • $k = 12 \rightarrow 46.875 \text{ Hz}$
  • $k = 13 \rightarrow 50.78125 \text{ Hz}$

50 Hz 位于这两个 bin 附近,所以频谱能量可能会扩散到多个 bin 上。

这就是后面要讲的频谱泄漏


9. 采样率 $F_s$ 该怎么选?

采样率必须满足奈奎斯特定理(采样频率必须至少是信号最高频率的两倍,才能无失真地还原原始信号)

$$ F_s > 2 \cdot f_{\text{max}} $$

也就是说,如果你想分析最高 5 kHz 的信号,那么采样率至少要大于:

$$ 10 \text{ kHz} $$

工程上通常会留裕量,比如:

  • 目标最高频率:5 kHz
  • 采样率:20 kHz 或更高

如果采样率不够,会发生混叠


10. 什么是混叠?

假设你的采样率是:

$$ F_s = 1000 \text{ Hz} $$

理论上你最多只能正确分析:

$$ 0 \sim 500 \text{ Hz} $$

因为 Nyquist 频率 是:

$$ \frac{F_s}{2} = 500 \text{ Hz} $$

如果实际信号里有一个 600 Hz 的成分,它会被错误地看成:

$$ 400 \text{ Hz} $$

这就是混叠

因为:

  • 600 Hz 超过了 $F_s/2$
  • 在采样后会折叠回来
  • 看起来像 400 Hz
  • 折叠计算公式:$$F混叠=|F_s-F真实|=|1000-600|=400Hz$$

所以在 ADC 前面,很多项目会加:模拟低通滤波器 也叫 抗混叠滤波器

混叠就是:高频信号“伪装”成低频信号,混进你的ADC数据里,让你看走眼。

在数字世界里,采样率$$ F_s $$就像一台相机的“快门速度”。如果被拍的东西动得比快门还快,照片就拍出“慢动作”甚至“倒转”的效果。

混叠就是这种“频率上的慢动作错觉”。


11. FFT 输出为什么是复数?

FFT 的输出通常是复数:

$$ X[k] = \text{real} + j \cdot \text{imag} $$

也就是:

  • real[k]
  • imag[k]

为什么是复数?

因为一个频率成分不只有“强度”,还有“相位”。

可以理解为:

  • 幅值:这个频率有多强
  • 相位:这个频率的波形从哪里开始

从复数得到幅值:

$$ \text{magnitude}[k] = \sqrt{\text{real}[k]^2 + \text{imag}[k]^2} $$

从复数得到相位:

$$ \text{phase}[k] = \text{atan2}(\text{imag}[k], \text{real}[k]) $$

很多嵌入式项目里,你只关心频率强弱,所以只看 magnitude 就够了。


12. 实数 FFT 的结果只看一半

ADC 采样到的数据通常是实数:

$$ x[n] \text{ 是实数} $$

对实数信号做 FFT 后,频谱是对称的

如果 FFT 点数是 $N$,那么你通常只需要看:

$$ 0 \sim \frac{N}{2} $$

对应频率范围是:

$$ 0 \sim \frac{F_s}{2} $$

例如:

  • $F_s = 1000 \text{ Hz}$
  • $N = 1024$

那么你只看:

$$ k = 0 \sim 512 $$

频率范围:

$$ 0 \sim 500 \text{ Hz} $$

后半部分是镜像信息,一般不用看。


13. 幅值怎么算?这是工程里很容易错的点

假设你做了 $N$ 点 FFT,得到:

  • real[k]
  • imag[k]

先算模长:

$$ \text{mag}[k] = \sqrt{\text{real}[k]^2 + \text{imag}[k]^2} $$

如果你想得到接近原始正弦波的幅值,常见处理是:

$$ \text{amplitude}[k] = \frac{2 \cdot \text{mag}[k]}{N} $$

但有两个例外:

情况计算公式
DC 分量 $k = 0$$\text{amplitude}[0] = \text{mag}[0] / N$
Nyquist 点 $k = N/2$$\text{amplitude}[N/2] = \text{mag}[N/2] / N$

为什么中间频点要乘 2?

因为实数 FFT 的能量原本分布在正频率和负频率两边,你只看单边频谱时,需要把另一半合并过来。

不过注意:如果你加了窗函数,幅值还要考虑窗函数的增益修正

直流分量(DC):

DC是‌Direct Current(直流)‌的缩写,在信号处理语境下特指信号中的‌直流分量‌,即信号的‌平均值‌或‌恒定偏移量‌。‌‌

  • 物理含义‌:代表信号中不随时间变化的恒定部分(基线偏移),数学上等于信号在一个周期或整段数据上的算术平均值。
  • 命名来源‌:源自“直流电”概念,类比于电流中方向大小不变的分量;在交流信号分析中,该恒定偏移被视为“直流成分”。
  • 去 DC 操作‌:即从原始信号每个采样点减去其平均值,使处理后的信号均值为零,仅保留交流波动部分

14. DC 分量是什么?

FFT 里的 $k = 0$ 代表 0 Hz,也就是直流分量

对于 ADC 数据来说,DC 分量通常就是平均值

例如你的 ADC 数据围绕 2048 上下波动:

$$ 2048 + \text{交流信号} $$

那么 FFT 的 0 Hz 会非常大。

很多时候你不关心 DC,而是关心交流频率,所以 FFT 前会先去均值:

1
2
3
4
5
mean = average(adc_buf);
for (i = 0; i < N; i++)
{
adc_buf[i] -= mean;
}

这样可以减少 DC 对频谱显示的影响。


15. 什么是频谱泄漏?

假设:

  • $F_s = 1000 \text{ Hz}$
  • $N = 256$
  • $\Delta f = 3.90625 \text{ Hz}$

FFT 的频点是:

k频率 (Hz)
00
13.90625
27.8125
1246.875
1350.78125
1454.6875

如果输入信号是 50 Hz,它并没有刚好落在 FFT 的某一个频点上。

结果会怎样?

它的能量不会只集中在一个 bin,而是扩散到周围很多 bin。

这就叫:频谱泄漏

表现为:

  • 原本应该是一个尖峰
  • 结果变成一片拖尾

16. 窗函数是干什么的?

FFT 默认假设你截取的这 $N$ 个点是一个完整周期片段,并且会无限重复。

如果你的采样片段开头和结尾接不上,就会产生突变。

这种突变会导致频谱泄漏

窗函数的作用是:把采样片段两端压低,让边界更平滑

常见窗函数:

窗函数特点
矩形窗不处理,频率刚好对齐时最好,泄漏严重
Hann 窗通用,泄漏较小
Hamming 窗通用,旁瓣较低
Blackman 窗泄漏更低,但主瓣更宽

对于很多嵌入式频谱分析项目,初学时可以先用:Hann窗(汉宁窗)

典型流程:

1
2
3
4
for (i = 0; i < N; i++)
{
x[i] = x[i] * hann[i];
}

然后再做 FFT。


17. 窗函数的代价

窗函数不是免费午餐。

它有好处

  • 减少频谱泄漏

但也有代价

  • 会让主峰变宽
  • 会影响幅值

所以选择窗函数时要看你的目标:

目标建议
信号频率刚好落在 bin 上矩形窗也可以
想减少泄漏Hann / Hamming
想看小信号,不想被大信号旁瓣淹没Blackman 类
想精确测幅值要做窗增益修正

18. 零填充有什么用?

很多人会在 FFT 前补零:

  • 原始 $N = 256$ 点
  • 补零到 $1024$ 点

这样频谱看起来更细。

但是要注意:

零填充不会真正提高频率分辨率

真正的频率分辨率由采样时长决定:

$$ T = \frac{N}{F_s} $$

$$ \Delta f = \frac{1}{T} = \frac{F_s}{N} $$

补零只是让频谱曲线插值更平滑,看峰值位置更方便。

如果你真的想提高频率分辨率,应该:

增加采样点数 $N$

也就是采更长时间


19. 嵌入式项目中的典型 FFT 流程

一个实用的 FFT 处理流程通常是:

  1. 定时器触发 ADC 采样
  2. DMA 存入采样缓冲区
  3. 收集 $N$ 个点
  4. 去除平均值(去 DC)
  5. 乘窗函数
  6. 执行 FFT
  7. 计算幅值谱
  8. 找峰值或做阈值判断
  9. 输出频率、幅值、报警结果等

对应伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define FFT_N   1024
#define FS 10000.0f

float adc_buf[FFT_N];
float fft_in[FFT_N];
float fft_out[FFT_N];
float mag[FFT_N / 2];

void process_fft(void)
{
float mean = 0.0f;

// 1. 计算平均值,去 DC
for (int i = 0; i < FFT_N; i++)
{
mean += adc_buf[i];
}
mean /= FFT_N;

// 2. 去均值 + 加窗
for (int i = 0; i < FFT_N; i++)
{
float x = adc_buf[i] - mean;
fft_in[i] = x * hann_window[i];
}

// 3. 执行 FFT
// fft_execute(fft_in, fft_out);

// 4. 计算幅值
for (int k = 0; k < FFT_N / 2; k++)
{
float real = get_real(fft_out, k);
float imag = get_imag(fft_out, k);

mag[k] = sqrtf(real * real + imag * imag);
}

// 5. 找主频
int peak_k = 1;
for (int k = 2; k < FFT_N / 2; k++)
{
if (mag[k] > mag[peak_k])
{
peak_k = k;
}
}

float peak_freq = peak_k * FS / FFT_N;
}

实际项目里你可以用 DSP 库,比如 ARM Cortex-M 上常见的 CMSIS-DSP,避免自己手写 FFT。


20. 例子一:检测 50 Hz 工频干扰

假设你的系统采集传感器信号,怀疑有 50 Hz 干扰。

设置:

  • $F_s = 1000 \text{ Hz}$
  • $N = 1000$

频率分辨率:

$$ \Delta f = \frac{1000}{1000} = 1 \text{ Hz} $$

那么:

  • 50 Hz 对应 $k = 50$

你做完 FFT 后检查:

  • mag[50]

如果它明显比周围频点大,就说明 50 Hz 干扰明显。

也可以同时检查:

  • 100 Hz
  • 150 Hz
  • 200 Hz

因为工频干扰有时会带谐波。


21. 例子二:电机振动检测

假设电机转速是:

$$ 3000 \text{ RPM} $$

换算成 Hz:

$$ \frac{3000}{60} = 50 \text{ Hz} $$

如果电机正常,振动频谱可能主要在:

  • 50 Hz
  • 100 Hz
  • 150 Hz

如果轴承、偏心、松动等问题出现,可能会在某些特定频率附近出现异常峰值。

嵌入式设备可以这样做:

加速度传感器采样 → FFT → 找峰值 → 判断是否异常

这种场景下,FFT 非常常用。


22. 例子三:电流纹波分析

假设你在做电源或者电机驱动,采样电流信号。

时域里看到的电流可能只是:

上下波动的一条曲线

但做 FFT 后,你可能发现:

  • 20 kHz 附近有明显峰值

这可能对应 PWM 开关频率

如果还有:

  • 40 kHz
  • 60 kHz

这些可能是 PWM 谐波。

所以 FFT 可以帮你分析:

  • 纹波频率
  • 开关噪声
  • 谐波分布
  • 滤波效果

23. 例子四:判断一个信号是不是正弦波

理想正弦波的频谱很简单:

只有一个主频峰

例如 1 kHz 正弦波,频谱主要在:

  • 1 kHz

如果信号失真了,就会出现谐波:

  • 2 kHz
  • 3 kHz
  • 4 kHz

所以可以通过 FFT 看谐波大小来判断失真程度。

这在音频、电机、电源、通信里都很常见。


24. 频域图怎么看?

FFT 后常见的频谱图横轴是频率,纵轴是幅值:

1
2
3
4
5
6
7
8
9
10
幅值
^
|
| *
| *
| *
| * *
| * *
|_____*_____*____________> 频率
50Hz 200Hz

这表示:

  • 50 Hz 有一个频率成分
  • 200 Hz 有一个频率成分

如果 50 Hz 的峰比 200 Hz 高,说明 50 Hz 成分更强。


25. 频率 bin 的概念一定要掌握

假设:

  • $F_s = 8000 \text{ Hz}$
  • $N = 1024$

则:

$$ \Delta f = \frac{8000}{1024} = 7.8125 \text{ Hz} $$

每个 bin 对应:

k频率 (Hz)
k = 00
k = 17.8125
k = 215.625
k = 1281000

如果你想找 1 kHz 信号:

$$ k = \frac{1000}{7.8125} = 128 $$

所以看:

  • mag[128]

就可以了。


26. FFT 点数 N 怎么选?

FFT 点数越大:

  • 频率分辨率越高

因为:

$$ \Delta f = \frac{F_s}{N} $$

但是 $N$ 越大:

  • 需要更多 RAM
  • 需要更多计算时间
  • 响应速度更慢

例如:

$F_s$$N$$\Delta f$采样时长
10 kHz25639.06 Hz25.6 ms
10 kHz10249.77 Hz102.4 ms
10 kHz40962.44 Hz409.6 ms

所以工程上要权衡:

  • 频率分辨率
  • 实时性
  • RAM
  • CPU算力

如果你只是判断有没有大概 1 kHz 附近的信号,$N$ 不用太大。

如果你要区分:

  • 1000 Hz 和 1005 Hz

那你需要很高的频率分辨率,也就是更长的采样时间


27. FFT 为什么常要求 N 是 2 的幂?

很多 FFT 算法喜欢:

$$ N = 256, 512, 1024, 2048, 4096 $$

因为 2 的幂 可以让 FFT 算法最高效(基2时分抽取/频分抽取)。

虽然有些库支持非 2 的幂长度,但在 MCU 上一般优先选:

$2^n$

例如:

  • 256 点
  • 512 点
  • 1024 点
  • 2048 点

28. 实际项目中最常见的坑

坑 1:采样率不准

FFT 的频率轴依赖 $F_s$。

如果你以为采样率是 10 kHz,但实际是 9.8 kHz,那么频率计算就会偏。

建议:用硬件定时器触发 ADC,不要用软件 delay 采样。


坑 2:没有抗混叠滤波

如果 ADC 前没有低通滤波,高频噪声会混叠到低频。

结果你会在频谱里看到一些“莫名其妙”的频率峰。


坑 3:忘记去 DC

ADC 信号如果有很大的直流偏置,0 Hz 分量会非常大,影响观察。

FFT 前建议先:

减去平均值


坑 4:频率不落在 bin 上

如果信号频率不是 bin 的整数倍,会出现频谱泄漏。

解决方法:

  • 使用窗函数
  • 增加采样长度
  • 或者让采样窗口包含整数个周期

坑 5:误以为补零提高真实分辨率

补零只是让图更平滑,不会让你真的区分更近的两个频率。

真正提升分辨率要增加采样时长。


坑 6:幅值标定错误

很多项目中 FFT 峰值看起来“不对”,原因是:

  • 没有除以 $N$
  • 没有乘 2
  • 没有考虑窗函数增益
  • ADC 原始值没有换算成电压/电流

坑 7:用 float 还是 fixed-point 没想清楚

Cortex-M4F / M7 / M33F 这类带 FPU 的 MCU 上,用 float FFT 比较方便。

没有 FPU 的小 MCU 上,可以考虑:

  • Q15
  • Q31
  • 定点 FFT

但定点 FFT 要注意:

  • 溢出
  • 缩放
  • 动态范围
  • 精度损失

29. 给你一个嵌入式项目中的实战设计模板

假设你的需求是:

采集一个传感器信号,分析 0~2 kHz 内的频率成分。


第一步:确定最高分析频率

最高频率 = 2 kHz

根据奈奎斯特定理:

$$ F_s > 4 \text{ kHz} $$

工程上取:

$$ F_s = 8 \text{ kHz 或 } 10 \text{ kHz} $$


第二步:选择 FFT 点数

如果取:

  • $F_s = 10 \text{ kHz}$
  • $N = 1024$

则:

$$ \Delta f = \frac{10000}{1024} \approx 9.77 \text{ Hz} $$

可以分辨约 10 Hz 间隔的频率。

采样时长:

$$ T = \frac{1024}{10000} = 102.4 \text{ ms} $$

这意味着每 102.4 ms 可以更新一次频谱。


第三步:ADC 采样

建议:

  • 定时器触发 ADC
  • ADC + DMA 双缓冲

不要用 while 循环 delay 采样。


第四步:处理数据

处理顺序:

原始 ADC → 转换成电压/电流 → 去均值 → 加窗 → FFT → 幅值谱


第五步:找目标频率

比如要检测 1 kHz:

$$ k = \frac{f \cdot N}{F_s} $$

$$ k = \frac{1000 \cdot 1024}{10000} \approx 102.4 $$

所以应该检查:

  • $k = 102$ 或 $k = 103$ 附近

不要只死盯一个 bin,最好检查附近几个 bin 的最大值。


30. 你可以这样理解傅里叶变换

从示波器角度

  • 示波器看到的是:信号随时间怎么变化
  • 频谱仪看到的是:信号里面有哪些频率

傅里叶变换就是把示波器视角变成频谱仪视角。


从滤波器角度

傅里叶变换像是很多个窄带滤波器:

  • 一个滤波器专门看 10 Hz
  • 一个滤波器专门看 20 Hz
  • 一个滤波器专门看 30 Hz

每个滤波器输出一个强度。

这些强度合起来就是频谱。


从匹配角度

傅里叶变换是在做匹配:

  • 这个信号像不像 50 Hz?
  • 像不像 100 Hz?
  • 像不像 200 Hz?

像,就有峰。

不像,就接近 0。


31. 最小知识闭环

如果你暂时不想学太多数学,只需要先掌握下面这些:

  1. FFT 用来把时域信号变成频域信号
  2. ADC 采样得到的是时域数据
  3. FFT 输出的每个 bin 对应一个频率
  4. $f[k] = k \cdot F_s / N$
  5. $\Delta f = F_s / N$
  6. 最高可分析频率是 $F_s / 2$
  7. FFT 前通常要去 DC、加窗
  8. FFT 后通常算 magnitude
  9. 找频谱峰值就能找到主要频率
  10. 工程中要注意采样率、混叠、泄漏、幅值标定

32. 一个完整的小例子

假设:

  • $F_s = 8000 \text{ Hz}$
  • $N = 1024$

那么:

  • 最高分析频率 = 4000 Hz
  • 频率分辨率 = $8000 / 1024 = 7.8125 \text{ Hz}$

你采到一个信号,做 FFT 后发现:

  • mag[128] 最大

那么主频是:

$$ f = \frac{k \cdot F_s}{N} $$

$$ f = \frac{128 \cdot 8000}{1024} = 1000 \text{ Hz} $$

所以你可以判断:

这个信号的主频大约是 1 kHz

如果又发现:

  • mag[256] 也比较大

对应:

$$ f = \frac{256 \cdot 8000}{1024} = 2000 \text{ Hz} $$

那说明信号里面还有 2 kHz 成分,可能是二次谐波。


33. 项目中应该怎么开始用?

你可以按这个顺序实践:

步骤内容
第一步用 Python 或上位机生成一个 50 Hz + 200 Hz 的测试信号
第二步对它做 FFT,看频谱峰值
第三步把同样逻辑移植到 MCU
第四步用 ADC 采真实信号
第五步处理去 DC、加窗、幅值归一化
第六步根据目标频率做检测或判断

在 MCU 上,一般不要一开始就自己写 FFT。

建议:

优先使用成熟 DSP 库

比如 ARM Cortex-M 项目中常用 CMSIS-DSP 这一类库。


34. 最后用一句工程化的话总结

傅里叶变换在嵌入式里通常不是为了“研究数学”,而是为了把问题从:

这个波形看起来很乱

变成:

这个信号主要包含 50 Hz、200 Hz 和 1 kHz 成分

这样你就可以做:

  • 频率检测
  • 故障诊断
  • 噪声分析
  • 滤波设计
  • 电机状态监测
  • 振动分析
  • 音频分析
  • 通信解调
  • 谐波分析

真正落地时,你重点掌握这几个公式就够用了:

$$ f[k] = \frac{k \cdot F_s}{N} $$

$$ \Delta f = \frac{F_s}{N} $$

$$ f_{\text{max}} = \frac{F_s}{2} $$

$$ \text{幅值} \approx \frac{2 \cdot \sqrt{\text{real}^2 + \text{imag}^2}}{N} $$

其中最关键的是:

  • **$F_s$**:采样率
  • **$N$**:FFT 点数
  • **$k$**:频率 bin

把这三个搞清楚,傅里叶变换在嵌入式项目里就已经能用起来了。

以上内容来源于ChatGPT,仅供参考