Arduino Due信号处理项目串口输出极慢无结果求助
Arduino Due信号处理串口输出缓慢/无输出问题排查与解决
问题背景
在Arduino Due上开展呼吸率计算项目,使用125Hz采样率采集7500个样本(对应1分钟的bidmc03m pleth信号),编写代码后串口监视器输出极慢甚至无输出,无法获取呼吸率结果。
原代码
#include <SPI.h> #include <SD.h> #define PI 3.1415926535897932384626433832795 /* 样本来自bidmc数据集的bidmc03m pleth信号 */ void dft(const double inrealn[],double outreal[],double outimg[],double magi[],size_t n);//for n number of samples float maxi(float a, float b); double inreal[7500]={// 此处填入样本值 }; void setup() { Serial.begin(115200); } void loop() { // 预处理 int n=7500; double sum1=0; for(int j=0;j<n;j++){ sum1=sum1+inreal[j]; } double mean=sum1/n; for(int i=0;i<n;i++) { inreal[i]=inreal[i]-mean; } // 寻找最大值 double maxVal = inreal[0]; for (int i = 1; i < n; i++) { if (inreal[i] > maxVal) { maxVal = inreal[i]; } } // 信号归一化 for(int j=0;j<n;j++){ inreal[j]=inreal[j]/maxVal; } // 谐振器系数计算 double a1=-2*0.997*cos(0.3*2*PI); double a2=0.997*0.997; double b=(1-0.997)*sqrt(1+(0.997*0.997)-2*0.997*cos(2*0.3)); // 谐振器滤波 double inrealn[7500]={}; for(int j=2;j<n;j++){ inrealn[j]=-a1*inrealn[j-1]-a2*inrealn[j-2]+b*inreal[j]; } // DFT计算 double outreal[7500]={}; double outimg[7500]={}; double magi[7500]={}; dft(inrealn,outreal,outimg,magi,n); // 寻找频谱峰值 double fmaxi = -100000.00; for (int i = 0; i < n; i++) { fmaxi = maxi(magi[i],fmaxi); } // 计算呼吸率 double resp_rate=fmaxi*60; Serial.println(resp_rate); } // DFT实现 void dft(const double inrealn[],double outreal[],double outimg[],double magi[],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; magi[k]=sqrt(pow(outreal[k],2)+pow(outimg[k],2)); } } float maxi(float a, float b){ return (a>b) ? a : b; }
核心问题分析
- DFT计算量过载:采用O(n²)复杂度的DFT,7500个样本需要执行5600万次循环,每次循环包含三角函数、平方、开方等高开销浮点运算,Arduino Due的运算能力无法在短时间内完成,程序长时间卡在DFT计算中无响应。
- loop()无限重复计算:所有处理逻辑放在loop()中,程序会无限重复执行均值计算、归一化、谐振器、DFT等步骤,永远无法稳定输出结果。
- 内存溢出风险:多个7500元素的double数组(每个占8字节)总内存占用超过300KB,而Arduino Due的SRAM仅96KB,内存溢出会导致程序崩溃,直接无输出。
- 浮点运算效率低下:使用double类型进行运算,相比float会占用更多内存且运算速度更慢,对于呼吸率计算的精度需求完全没必要。
解决方案
1. 转移处理逻辑到setup()
样本是固定的,不需要重复计算,将所有处理代码移到setup()中,仅执行一次,完成后输出结果即可。
2. 替换DFT为FFT
使用FFT(O(n log n)复杂度)替代DFT,计算量大幅降低。可以使用Arduino官方生态的arduinoFFT库。
3. 优化内存使用
- 用float替代double,将内存占用减半;
- 移除不必要的数组,仅保留计算必需的存储单元;
- 限制峰值搜索的频率范围(呼吸率对应0.1-0.5Hz,对应频率点索引6-30),减少遍历次数。
4. 预计算常量
将谐振器的三角函数常量提前计算为数值,避免每次循环重复计算。
5. 优化串口输出
仅在所有计算完成后一次性输出结果,避免计算过程中频繁IO操作拖慢程序。
修改后的示例代码
#include <SPI.h> #include <arduinoFFT.h> #define PI 3.141592653589793 #define SAMPLES 7500 #define SAMPLING_RATE 125.0 arduinoFFT FFT = arduinoFFT(); float inreal[SAMPLES] = { /* 填入你的样本数据 */ }; float vReal[SAMPLES]; float vImag[SAMPLES]; void setup() { Serial.begin(115200); while(!Serial); // 等待串口连接完成 // 去均值预处理 float sum = 0.0; for(int i = 0; i < SAMPLES; i++){ sum += inreal[i]; } float mean = sum / SAMPLES; for(int i = 0; i < SAMPLES; i++){ vReal[i] = inreal[i] - mean; vImag[i] = 0.0; // FFT虚部初始化为0 } // 归一化 float maxVal = vReal[0]; for(int i = 1; i < SAMPLES; i++){ if(vReal[i] > maxVal) maxVal = vReal[i]; } for(int i = 0; i < SAMPLES; i++){ vReal[i] /= maxVal; } // 谐振器处理(预计算常量) float cosVal = cos(0.3 * 2 * PI); float a1 = -2 * 0.997 * cosVal; float a2 = 0.997 * 0.997; float b = (1 - 0.997) * sqrt(1 + a2 - 2 * 0.997 * cos(2 * 0.3)); float filtered[SAMPLES] = {0}; for(int j = 2; j < SAMPLES; j++){ filtered[j] = -a1 * filtered[j-1] - a2 * filtered[j-2] + b * vReal[j]; vReal[j] = filtered[j]; } // FFT计算 FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD); FFT.ComplexToMagnitude(vReal, vImag, SAMPLES); // 仅在呼吸频率范围内搜索峰值(0.1-0.5Hz) float maxMag = 0.0; int peakIndex = 0; int startIdx = (0.1 * SAMPLES) / SAMPLING_RATE; int endIdx = (0.5 * SAMPLES) / SAMPLING_RATE; for(int i = startIdx; i < endIdx; i++){ if(vReal[i] > maxMag){ maxMag = vReal[i]; peakIndex = i; } } // 计算并输出呼吸率 float respFreq = (peakIndex * SAMPLING_RATE) / SAMPLES; float respRate = respFreq * 60; Serial.print("呼吸率:"); Serial.println(respRate); } void loop() { // 处理完成后闲置,无需重复计算 }
内容的提问来源于stack exchange,提问作者rks
相关产品推荐
相关产品推荐

