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

Arduino Uno处理BIDMC(PPG)信号无输出问题排查

Arduino Uno处理PPG信号无串口输出的问题排查与修复

1. 全局数组数据污染导致后续计算异常

inreal是全局数组,loop()每次循环都会直接修改它的数值(均值减法、归一化操作直接覆盖原始数据)。第一次循环后数组已变为预处理结果,第二次循环基于错误数据计算,会出现数值异常(如极大/极小值、NaN),最终导致程序卡住无法输出。

修复方案:
在loop()中创建局部数组复制原始数据,避免修改全局数组;或直接将所有处理逻辑移到setup()中,仅执行一次(固定样本测试无需循环)。

void setup() {
  Serial.begin(9600);
  while(!Serial); // 等待串口连接
  
  int n=250;
  double signal[250];
  // 复制原始数据到局部数组
  for(int j=0;j<n;j++){
    signal[j] = inreal[j];
  }
  // 后续预处理、滤波均使用signal数组
}

2. 变量类型不匹配导致maxVal为垃圾值

inreal是double类型数组,均值减法后结果为浮点型,但maxVal被定义为int类型,会截断浮点数值,导致maxVal成为错误的垃圾值。后续归一化时除以错误值,会引发数值异常,中断程序输出。

修复方案:
将maxVal改为double类型:

// 寻找最大值
double maxVal = signal[0];
for (int i = 0; i < n; i++) {
    if (signal[i] > maxVal) {
       maxVal = signal[i];
    }
}

3. 缺少串口连接等待导致输出丢失

Serial.begin(9600)后未等待串口监视器连接,初始输出会直接丢失,甚至引发后续输出异常。

修复方案:
在setup()中添加等待逻辑:

void setup() {
  Serial.begin(9600);
  while(!Serial); // 等待串口连接
}

4. DFT计算量过大导致程序卡顿

250点DFT为O(n²)运算,每次loop()需执行62500次循环,且包含三角函数计算,Arduino Uno处理能力有限,会导致CPU长时间占用,无法及时响应串口输出,表现为无输出。

修复方案:

  • 将处理逻辑移到setup()中,仅执行一次;
  • 替换手动DFT为Arduino官方FFT库,大幅降低计算耗时。

5. 呼吸率计算逻辑错误(附带修复)

当前直接取DFT实部最大值乘以60的逻辑完全错误:频谱幅度应为sqrt(outreal[k]^2 + outimg[k]^2),峰值对应频率为k * 采样率 / 样本数,呼吸率为频率乘以60(BIDMC PPG采样率为125Hz)。

修复方案:

// 寻找频谱峰值对应的频率
double maxAmplitude = 0;
int peakIndex = 0;
double Fs = 125.0; // BIDMC PPG采样率
for (int i = 0; i < n/2; i++) { // 仅处理正频率部分
    double amplitude = sqrt(outreal[i]*outreal[i] + outimg[i]*outimg[i]);
    if (amplitude > maxAmplitude) {
        maxAmplitude = amplitude;
        peakIndex = i;
    }
}
// 计算呼吸率
double resp_rate = (peakIndex * Fs / n) * 60;
Serial.println(resp_rate);

修复后的核心代码示例

#include <SPI.h>
#include<SD.h>
#define PI 3.1415926535897932384626433832795

void dft(const double inrealn[],double outreal[],double outimg[],size_t n);

double inreal[251]={-9577,
// 原始数据保持不变,此处省略
-2082
};

void setup() {
  Serial.begin(9600);
  while(!Serial);

  int n=250;
  double signal[250];
  // 复制原始数据
  for(int j=0;j<n;j++){
    signal[j] = inreal[j];
  }
  
  // 均值减法
  float sum1=0;
  for(int j=0;j<n;j++){ 
    sum1=sum1+signal[j];
  }
  float mean=sum1/n;
  for(int i=0;i<n;i++){
    signal[i]=signal[i]-mean;
  }
  
  // 寻找最大值
  double maxVal = signal[0];
  for (int i = 0; i < n; i++) {
      if (signal[i] > maxVal) {
         maxVal = signal[i];
      }
  }
  
  // 归一化
  for(int j=0;j<n;j++){
    signal[j]=signal[j]/maxVal;
  }

  // 谐振器系数计算与滤波
  float a1=-2*0.997*cos(0.3*2*PI);
  float a2=0.997*0.997;
  float b=(1-0.997)*sqrt(1+(0.997*0.997)-2*0.997*cos(2*0.3));
  double inrealn[250]={0};
  for(int j=2;j<n;j++){
    inrealn[j]=-a1*inrealn[j-1]-a2*inrealn[j-2]+b*signal[j];
  }
  
  // DFT与呼吸率计算
  double outreal[250]={0};
  double outimg[250]={0};
  dft(inrealn,outreal,outimg,n);

  double maxAmplitude = 0;
  int peakIndex = 0;
  double Fs = 125.0;
  for (int i = 0; i < n/2; i++) {
      double amplitude = sqrt(outreal[i]*outreal[i] + outimg[i]*outimg[i]);
      if (amplitude > maxAmplitude) {
          maxAmplitude = amplitude;
          peakIndex = i;
      }
  }
  double resp_rate = (peakIndex * Fs / n) * 60;
  Serial.println(resp_rate);
}

void loop() {
  // 空转即可
}

void dft(const double inrealn[],double outreal[],double outimg[],size_t n){
    for (int k=0;k<n;k++){
      double sumreal=0;
      double sumimg=0;
      for (int t=0;t<n;t++){
          double angles=2*PI*t*k/n;
          sumreal+=inrealn[t]*cos(angles);
          sumimg+=-inrealn[t]*sin(angles);
      }
      outreal[k]=sumreal;
      outimg[k]=sumimg;
    }
}

内容的提问来源于stack exchange,提问作者rks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 19:59:52