You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算Arduino Due经FFT处理后的信号幅值并打印?

解决Arduino Due FFT信号幅值计算与打印问题

嘿,很高兴看到你已经成功用FFT提取了频率!计算信号幅值其实只需要对FFT的结果做一点校准和转换就行,我来一步步给你讲清楚怎么改代码:

关键知识点说明

你代码里的FFT.ComplexToMagnitude(vReal, vImag, SAMPLES)已经算出了每个频率bin的原始幅度值,但这个值是未校准的,需要两步处理才能得到实际的信号幅值:

  1. ADC读数转实际电压:Arduino Due的ADC是12位的,analogRead()返回0-4095的整数,对应输入电压0V到3.3V。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:12:24