如何计算Arduino Due经FFT处理后的信号幅值并打印?
解决Arduino Due FFT信号幅值计算与打印问题
嘿,很高兴看到你已经成功用FFT提取了频率!计算信号幅值其实只需要对FFT的结果做一点校准和转换就行,我来一步步给你讲清楚怎么改代码:
关键知识点说明
你代码里的FFT.ComplexToMagnitude(vReal, vImag, SAMPLES)已经算出了每个频率bin的原始幅度值,但这个值是未校准的,需要两步处理才能得到实际的信号幅值:
- ADC读数转实际电压:Arduino Due的ADC是12位的,
analogRead()返回0-4095的整数,对应输入电压0V到3.3V。 - FFT幅度缩放:因为你用了汉明窗口,且实信号的FFT会把能量分到正负频率分量,所以需要对幅度值做缩放补偿。
修改后的完整代码
#include "arduinoFFT.h" #define SAMPLES 32 #define SAMPLING_FREQUENCY 1000 #define ADC_BITS 12 // Arduino Due是12位ADC #define ADC_REF_VOLTAGE 3.3 // 参考电压3.3V arduinoFFT FFT = arduinoFFT(); unsigned int sampling_period_us; unsigned long microseconds; double vReal[SAMPLES]; double vImag[SAMPLES]; void setup() { Serial.begin(9600); sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY)); } void loop() { /* 采样部分 */ for(int i=0; i<SAMPLES; i++) { microseconds = micros(); vReal[i] = analogRead(0); // 读取ADC值 vImag[i] = 0; while(micros() < (microseconds + sampling_period_us)){ // 等待采样周期结束 } } /* FFT处理 */ FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); // 提取主峰值频率并打印 double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY); Serial.print("主峰值频率: "); Serial.print(peak, 1); Serial.println(" Hz"); /* 计算并打印每个频率bin的实际幅值 */ Serial.println("--- 频率与幅值列表 ---"); for(int i=0; i<(SAMPLES/2); i++) { // 计算当前bin对应的频率 double frequency = (i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES; // 获取FFT原始幅度值 double rawMagnitude = vReal[i]; // 计算缩放因子:补偿窗口和FFT的特性 double scaleFactor; if (i == 0) { // DC分量(直流)没有负频率副本,不需要乘2 scaleFactor = 1.0 / (SAMPLES * 0.54); } else { // 交流分量需要乘2,同时补偿汉明窗口的能量损失(汉明窗口平均系数约0.54) scaleFactor = 2.0 / (SAMPLES * 0.54); } // 转换为实际电压幅值 double voltageAmplitude = rawMagnitude * scaleFactor * (ADC_REF_VOLTAGE / ((1 << ADC_BITS) - 1)); // 打印结果 Serial.print("频率: "); Serial.print(frequency, 1); Serial.print(" Hz | 幅值: "); Serial.print(voltageAmplitude, 3); Serial.println(" V"); } Serial.println("---------------------"); delay(1000); // 去掉下面这行,否则程序只会运行一次就停止 // while(1); }
代码修改要点解释
- 新增了
ADC_BITS和ADC_REF_VOLTAGE宏定义,方便适配不同的ADC参数。 - 对FFT后的原始幅度值做了缩放:
- 汉明窗口会降低信号能量,用0.54补偿窗口带来的衰减。
- 交流分量的FFT结果在正负频率各有一个副本,所以乘2还原实际幅值;DC分量不需要这一步。
- 把ADC读数转换成了实际的电压幅值,让结果更直观。
- 移除了
while(1);,这样程序可以持续采样并输出结果(如果需要单次运行可以再加回去)。
额外提示
- 如果你的噪声传感器输出的是交流信号,DC分量(i=0)的幅值可以忽略,或者根据需求处理。
- 采样点数
SAMPLES如果用2的幂(比如32、64、128),FFT的效率会更高,你的32是没问题的。
内容的提问来源于stack exchange,提问作者Bojan Mihajlovic
相关产品推荐
相关产品推荐

