使用FFT解码摩尔斯电码遇问题:恒定音调下频桶幅度波动
兄弟,我之前做摩尔斯电码音频解码项目的时候,一模一样的问题差点把我搞疯!恒定音调下FFT的目标Bin绝对值忽高忽低,根本没法靠阈值区分点划和停顿。结合我踩过的坑,给你几个最实用的解决方向:
1. 先给信号加个窗口函数,解决频谱泄漏
默认的FFT用的是矩形窗,如果你的音频帧长度刚好不是目标音调周期的整数倍,就会出现严重的频谱泄漏——简单说就是能量会“跑”到相邻的Bin里,导致目标Bin的数值波动。
解决办法很简单,在做FFT之前给每帧信号加一个平滑的窗口,比如汉明窗(Hamming)或者汉宁窗(Hanning):
# 假设用numpy处理信号 import numpy as np window = np.hamming(len(audio_frame)) windowed_frame = audio_frame * window
加窗后,频谱泄漏会大幅减少,目标Bin的数值会稳定很多。
2. 用帧重叠+滑动平均,平滑微小波动
即使加了窗,采样过程中的微小噪声、硬件的细微波动还是会让Bin值有小幅度跳动。这时候可以:
- 让连续的音频帧之间重叠50%(比如前一帧取0-1024,后一帧取512-1536),这样每一段信号都会被多次分析
- 对连续3-5帧的目标Bin值做滑动平均,比如取最近3帧的平均值作为判断依据,能有效过滤掉突发的小波动
3. 调整FFT长度,提升频率分辨率
如果你的目标音调刚好落在两个频率Bin的中间,那每次FFT的结果都会因为帧的起始位置不同而波动。这时候可以增大FFT的长度(尽量用2的幂次,比如1024→2048→4096),让每个Bin的宽度变窄,让目标音调尽可能贴近某个Bin的中心频率。
举个例子:如果采样率是44100Hz,FFT长度用2048的话,每个Bin的宽度是44100/2048≈21.5Hz;如果用4096,宽度就变成≈10.7Hz,分辨率提升一倍,目标音调的定位会更准确。
4. 不要只看单个Bin,看附近Bin的能量总和
有时候单Bin的数值波动还是存在,你可以换个思路:计算目标频率附近3-5个Bin的绝对值平方和(也就是能量),用这个总和来判断是否有音调。因为即使单个Bin波动,附近Bin的总能量会保持稳定,这样阈值判断就可靠多了。
我当时就是先加了汉明窗,再用50%帧重叠+3帧滑动平均,波动立刻就被压下去了,阈值判断一下子就好用了!你可以先从加窗这个最简单的步骤开始试,应该能看到明显效果。
内容的提问来源于stack exchange,提问作者Robbie Robertson

