STM32正弦信号FFT滤波详解:获取纯净信号的方法
25.1.14更新:加窗后幅值校正,可以在不增加FFT点数下,对幅值进行修正,请参考大佬的文章:窗函数补偿 – 飞云之下的文章
对于加什么窗,这位大佬文章不错:什么是窗函数? – linmue-谭祥军的文章
1.先看处理结果
1.1 目的:
注意:FFT滤波部分在单片机上处理的(单片机型号STM32),从串口发送数据到MATLAB进行分析,我在实际项目中是处理外部采集的信号,这里只是为了理论研究跟各位学术探讨。
模式:单片机处理信号+MATLAB分析。
为了从一段叠加信号(20hz+150hz+高斯随机噪声)中提取出目标信号(150hz)。(请忽略注释,因为代码已经修改过了)
信号长度跟FFT点数一致FFT_NUM = 1024;
arm_sin_f32();函数是DSP库函数,与<math.h>里面的sin();函数一样。
Fs = 2048;采样率,/2048就是一秒采样2048个点,这里只生成了1024个点,就是0.5秒的正弦信号。
n/Fs:前面的n就是谐波的频率,150hz跟20hz。
最前面的10,20就是该谐波的幅值(幅度,振幅)。

1.2 带高斯噪声的滤波结果:
1.2.1 时域分析
成功获得幅值为10的正弦信号,误差较小。带噪声跟不带噪声有一点影响。
①带高斯噪声的信号处理

1.2.2 频域分析
使用MATLAB对原始信号跟处理过的信号进行频谱图绘制。(横坐标是频率,纵坐标代表该频率的振幅/幅值)
蓝色:原始信号(噪声+20hz+150hz);
黑色:目标信号(150hz)。
注意:黑色线把蓝色遮住了,实际上在x = 150那部分下面是蓝色线。

1.3 不带高斯噪声的滤波结果:
1.3.1 时域分析

1.3.2 频域分析
可以看到,没有噪声的滤波结果很接近目标信号。

1.4 分析
通过对信号的1024点FFT处理,我们获得了目标信号,有误差的原因:频率分辨率太低
频率分辨率 = 采样率 / FFT点数
例如,我们当前分辨率 = Fs2048 / 1024 = 2;
实际项目中,我们采样率做不到2048,只能是2000这样的(0.5ms采集一次),所以提高FFT点数是一个好办法。

1.能量泄露
可以看到,当更改目标信号150 –> 149hz时,因为涉及到了频率分辨率(采样率2048 / FFT点数1024 = 2),意思我们的算法每2个hz算一个谐波,在FFT处理后的数组[75*2]位置(实际位置应该是150*Fs/FFT_num)就是存储了150hz的谐波分量与相位信息,[74*2]就是148,你149hz雨露均沾(实际不是均沾)。
有些同学可能就要问了,你74*2跟75*2两个数据之间不还有个74*2+1的数据吗,这是因为FFT变换得到的数组是一个复数数组,比如数组[0]跟数组[1]是频率为0hz时的结果,0位置是实数,1位置是复数。
以下是抽象解说:
就好比世界分两条船,男人船跟女人船,如果你非要去一趟泰国,你要脚踏两条船,我们FFT处理就像一把刀,它要把两条船分开,一刀下去,就把你砍成了两部分,这两部分它还不平均,如果你重心都在男人船,那砍掉你的可能只有一条腿。
而这才是最气的,我们处理数据的时候就怕这种情况,要么你整个人都在一条船上,要么你就砍成刚好两半,nnd你留条腿给我,我怎么计算?现在这个149hz的能量就泄露到了附近的谐波上面,好笑的是它还拉了一坨大的,就跟被砍了一刀,你的雪还一直流,溅到哪里都是。
2. FFT进阶
那可能有兄弟就要问了:主播主播,你这个FFT太吃操作了,有没有简单又强势的解决办法?
我(/歪头,嘴角微翘):有的兄弟,有的,像这么强势又有效果的办法还有9个。(在古代,9是最大的数,意思是还有很多,就等你发现了,读者博士)。
1、提高FFT点数
我当前使用的是STM32F103VCT6,103的C系列内存RAM只有48kb,使用FFT无可避免需要创建几个很大的flaot数组(如果你有办法你,求你一定告诉我 /给跪了),我目前1024点FFT,RAM已经来到了近40kb(下面有查看RAM的办法)。

所以某些人已经在偷偷逛嘉立创了。

双击工程可以打开.map文件,滑到最底下可以看得到

谈笑间,RAM又长大了(朕顿感欣慰),长到43.5kb了( 无奈苦笑 /扶额 )。
2、弃用FFT滤波,使用MATLAB生成的滤波器进行滤波
有人就要问了,主播主播,你为什么不用matlab的陷波、带通、这些滤波器
用过了,不行啊,感觉还是FFT简单,快
还有小波变换滤波,别说了,看都看不太懂。
作者:今日方知我非我