Код превышает память платы Uno

Я долго застрял на куске кода Arduino, который выполняет БПФ на сигнале фотоплетизмограммы и после этого решает качество сигнала.

#include <math.h>
#include "arduinoFFT.h"
#include <EEPROM.h>

//N=10 pt скользящая средняя
//Для данных bidmc первых 10 пациентов MA = 0,4613849
double baseline=0.4613849;
float lm=1.0; //амплитудный порог
float ref_voltage=1.0; //опорное напряжение насыщения
float cutoff=4.0;
float fs=0.02;
float zero_amp=0.015;

float pi=3.14159265;  //Универсальные константы
float e=2.71828;
const int flen=61;

#define SAMPLES 128           //Должна быть степень 2
#define SAMPLING_FREQUENCY 50 //Hz, должно быть меньше 10000 из-за АЦП

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() {
  //float arr[]={0.43597,0.43206,0.42815,0.42424,0.42131,0.41838,0.4174,0.41642,0.41544,0.41447,0.41251,0.41153,0.40958,0.40762,0.40665,0.40665,0.40665,0.40762,0.4086,0.4086,0.41056,0.41153,0.41153,0.41056,0.4086,0.40665,0.40469,0.40469,0.40567,0.4086,0.41251,0.42033,0.43206,0.45259,0.4741,0.4956,0.51808,0.5347,0.55034,0.56403,0.57576,0.58456,0.59238,0.59629,0.59922,0.60117,0.60215,0.60313,0.60313,0.60117,0.59824,0.59335,0.58553,0.57771,0.57185,0.56207,0.54741,0.53372,0.52102,0.50929,0.50244,0.49756,0.4956,0.49462,0.49365,0.49365,0.49365,0.49169,0.48876,0.4868,0.48387,0.48094,0.47801,0.47507,0.47116,0.46823,0.46334,0.45846,0.45357,0.44868,0.44379,0.43891,0.435,0.43109,0.4262,0.42326,0.42033,0.41838,0.41642,0.41447,0.41447,0.41349,0.41251,0.41251,0.41251,0.41251,0.41251,0.41349,0.41544,0.4174,0.41935,0.42033,0.42131,0.42131,0.42033,0.41935,0.41642,0.41447,0.41349,0.41349,0.41642,0.42131,0.42815,0.43695,0.45064,0.46823,0.4868,0.50538,0.52395,0.54154,0.55816,0.57185,0.58162,0.58749,0.59042,0.59042,0.5914,0.59042,0.58944,0.58651,0.5826,0.57771,0.57087,0.56305,0.5523,0.54057,0.52884,0.51711,0.50733,0.49756,0.48778,0.47996,0.4741,0.47019,0.46823,0.46725,0.46628,0,46432,0,46041,0,4565}; 
  float arr[]={0.30303,0.29814,0.29326,0.28837,0.28446,0.28152,0.27859,0.27468,0.26979,0.26393,0.25806,0.25611,0.25904,0.27175,0.29521,0.32942,0.37341,0.42522,0.47116,0.51417,0.55914,0.6002,0.63539,0.66373,0.68622,0.70381,0.71652,0.72532,0.73118,0.73314,0.73314,0.73021,0.7263,0.72043,0.71359,0.70479,0.69501,0.68524,0.67351,0.6608,0.64614,0.6305,0.61388,0.59726,0.58065,0.56207,0.54252,0.52395,0.50538,0.48778,0.47312,0.46041,0.44868,0.43988,0.43304,0.42815,0.42522,0.42229,0.41935,0.4174,0.41544,0.41349,0.41056,0.40762,0.40469,0.40176,0.39785,0.39394,0.39003,0.38612,0.38221,0.37634,0.37146,0.36559,0.35973,0.35386,0.348,0.34213,0.33627,0.3304,0.32551,0.32258,0.31769,0.31281,0.30792,0.30303,0.29814,0.29423,0.28935,0.28446,0.27957,0.2737,0.26784,0.26393,0.261,0.26197,0.26882,0.28446,0.31183,0.34897,0.39589,0.44868,0.50147,0.55132,0.59629,0.6305,0.65591,0.67937,0.69795,0.71261,0.72434,0.73216,0.73607,0.73803,0.73803,0.73509,0.73021,0.72434,0.7175,0.70968,0.6999,0.69013,0.6784,0.66569,0.65103,0.63441,0.61681,0.59824,0.57771,0.55718,0.53959,0.52297,0.50635,0.48974,0.47507,0.46334,0.45259,0.44379,0.43695,0.43206,0.42815,0.42522,0.42229,0.42033,0.41838,0.41642,0.41447,0.41153,0.4086,0.40469,0.40078,0.39687,0.39198,0.3871,0.38221,0.3783,0.37439,0.3695,0.36364,0.35875,0.35288,0.348,0.34213,0.33724,0.33236,0.32747,0.32258,0.31769,0.31378,0.31085,0.30792,0.30401,0.3001,0.29619,0.29032,0.28446,0.27859,0.2737,0.2737,0.28055,0.29423,0.31672,0.35191,0.39785,0.45161,0.50733,0.56109,0.60899,0.65005,0.68328,0.7087,0.72727,0.74096,0.74878,0.75367,0.75562,0.7566,0.75464,0.75171,0.74585};
  //float обр.[]={0.30303,0.28446,0.26979,0.25904,0.37341,0.55914,0.68622,0.73118};
  int len=sizeof(arr)/sizeof(arr[0]);
  
  /*for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)  //отображение массива данных ppg
  {
    Serial.println(arr[i]);  
    delay(50);
  }*/
  
  //ОБРАЗЦЫ=len;
  for(int i=0; i<SAMPLES; i++)
    {
        microseconds = micros();    //Переполняется примерно через 70 минут!
        vReal[i] = arr[SAMPLES]; 
        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);
   
  /*PRINT RESULTS*/
  //Serial.println(peak); //Выведите, какая частота является наиболее доминирующей.
  
  for(int i=0; i<(SAMPLES/2); i++)
  {
       /*View all these three lines in serial terminal to see which frequencies has which amplitudes*/ 
       Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 2);
       Serial.print(" ");
       Serial.println(vReal[i], 1);  //Просмотр только этой строки в последовательном плоттере для визуализации бункеров
       delay(50);
  }
   
  //delay(1000); //Повторять процесс каждую секунду ИЛИ:
  //while(1); //Выполнить код один раз
  Serial.println("Hello");
  //int freq_N=61;
  for (int i = 0 ; i < EEPROM.length() ; i++) {
    EEPROM.write(i, 0);
  }
  
  /*double D[3][freq_N]; //1-я строка для частот, 2-я строка для величины, 3-я строка для фазы
  for(int i=0;i<freq_N;i++)
  {
    D[0][i]=-(pi/2)+i*0.1;
    double *d=fourier_tran(arr,len,D[0][i]);
    D[1][i]=*(d+0);
    D[2][i]=*(d+1); 
    Serial.println(i);
    delay(300);
  }*/
  
  int z_crsg=zero_cross(arr,len);
  //Serial.println(z_crsg);
  //delay(1000);
  float diff_arr[len-1];   //Дифференцированный PPG-сигнал
  for(int i=1;i<=len-1;i++)
  {
    diff_arr[i-1]=arr[i]-arr[i-1];
  }
  
  int rise=rise_count(arr,len);
  int fall=fall_count(arr,len);
  float fr=(float)fall/rise;      //коэффициент счета fall/rise
  uint16_t m=max_amplitude(arr,len);   //обнаружение максимальной амплитуды
  int sc[]={0,0,0};  //вектор подключения датчика
  if(fr>1.65 && fr<2.35)
  { 
    sc[0]=1;
  }
  if(m<lm)
  {
    sc[1]=1;
  }
  if(m>zero_amp)
  {
    sc[2]=1;;
  }
  int sd=sig_saturation(arr,len);
  //Serial.println(sd);
  //Serial.print(sc[0]);
  //Serial.print(sc[1]);
  //Serial.print(sc[2]);
 
  if(sum(sc,3)>=2)
  {
    Serial.println("Sensor well connected");
  }
  else
  {
    Serial.println("Sensor not connected properly");
  }
  delay(500);
  if(base_drift(arr,len)>=0.4612 && base_drift(arr,len)<=0.4614)
  {
    Serial.println("Motion detected");
  }
  else
  {
    Serial.println("No motion artefacts problems");
  }
  delay(500);
  if(sd==0)
  {
    Serial.println("Saturated signal");
  }
  else
  {
    Serial.println("Non-saturated signal");
  }
  delay(500); 
  
  int d_z_crsg=zero_cross(diff_arr,len-1);
  
}

//Обнаружение подключения датчика
int fall_count(float arr[],int l)
{
  int c=0;
  for(int i=0;i<l-1;i++)
  {
    if(arr[i]>arr[i+1])
    {
      c=c+1;
    }
  }
  return c;
}
int rise_count(float arr[],int l)
{
  int c=0;
  for(int i=0;i<l-1;i++)
  {
    if(arr[i]<arr[i+1])
    {
      c=c+1;
    }
  }
  return c;
}
int max_amplitude(float arr[],int l)
{
  float h=arr[0];
  for(int i=1;i<l;i=i+1)
  {
    if(arr[i]>h)
    {
      h=arr[i];
    }
  }
  if(h>lm)
  {
    return 0;
  }
  return 1;
  
}

//Обнаружение сегмента шума
int zero_cross(float arr[],int l)
{
  int c=0;
  for(int i=0;i<l-1;i++)
  {
    if((arr[i]==0.0) && arr[i+1]!=0.0)
    {
      c=c+1; 
    }
  }
  return c;
}
int local_maxima(float arr[], int l)
{
  int frame_len=15;
  int a=0;
  int frames=l/frame_len;
  int maxima[frames];
  for(int k=0;k<frames;k++)
  {
    uint16_t h=0;
    for(int i=a;i<a+frame_len;i++)
    {
      if(arr[i]>h)
      {
        h=arr[i];
      }
    }
    maxima[k]=h;
    a=a+frame_len;
  }
  for(int i=0;i<frames-1;i++)
  {
    if(maxima[i]>lm && maxima[i+1]>lm)
    {
      return 1;
    }
    if(i<=frames-4)
    {
      if(maxima[i]<lm && maxima[i+1]<lm && maxima[i+2]<lm && maxima[i+3]<lm)
      {
        return 1;
      }
    }
    if(i<=frames-3)
    {
      if(maxima[i]<lm && maxima[i+2]<lm)
      {
        return 1;
      }
    }
  }
  return 0;
}


//Обнаружение насыщения
int sig_saturation(float arr[],int l)
{
  int c=0;
  //uint16_t лев=200000;
  for(int i=0;i<l-1;i++)
  {
    if(arr[i]<10 && arr[i+1]<10)
    {
      c++;
    }
    
    if(arr[i]>lm && arr[i+1]>lm)
    {
      c++;
    }
  }
  if(c>=20)
  {
    return 0;
  }
  return 1;
}

//модуль обнаружения движения
double base_drift(float arr[],int l)
{
  int N=10; //N pt скользящая средняя
  int frame=l-N+1; //количество кадров
  float s=0;
  double sum=0;
  for(int i=0;i<frame;i++)
  {
   s=0;
   for(int j=i;j<N+i;i++)
   {
      s=s+arr[j];
   }
   sum=sum+(s/N);
  }
  double avg=sum/frame;
  return avg;
}

int sum(int n[],int l)
{
  float s=0;
  for(int i=0;i<l;i++)
  {
    s=s+n[i];
  }
  return s;
}

float butterworth(float f, float fc,int n)
{
  float h;
  h=1/sqrt(1+pow((fc/f),2*n)); 
  return h;
}

double *fourier_tran(float arr[],int n,float omega)
{
   double Xr=0.00;
   double Xi=0.00;
   double DTFT[2];
   for(int j=0;j<n;j++)
   {
     Xr=Xr+arr[j]*cos(omega*j);
     Xi=Xi+arr[j]*sin(omega*j);
   }
   float X=sqrt(pow(Xr,2)+pow(Xi,2));  //Величина преобразования Фурье
   float ph=-atan(Xi/Xr);  //Фаза преобразования Фурье
   
   DTFT[0]=X;
   DTFT[1]=ph;
   double *p=DTFT;
   Serial.println(*p);
   delay(100);
   Serial.println(*(p+1));
   delay(100);
   return p;
}
float *inv_fourier(float DTFT[][flen],int l,int flen,int n) //n-длина временного массива 
{
  float arr[n];
  for(int i=0;i<n;i++)
  {
    float X=0.000;
    for(int j=0;j<l;j++)
    {
      X=X+DTFT[1][j]*cos(DTFT[2][j]+DTFT[0][j]*n);
    }
    arr[i]=1/(2*pi) * X;
  }
  float *p=arr;
  return p;
}

Когда я загружаю код, сразу после выполнения БПФ, код больше ничего не отображает, подразумевая, что он перестает выполняться и показывает сообщение об ошибке - недостаточно памяти; см. http://www.arduino.cc/en/Guide/Troubleshooting#size для получения советов по сокращению вашего следа. раздел данных превышает доступное пространство на борту

Я не могу удалить ни один из разделов кода, так что же мне нужно изменить?

, 👍3

Обсуждение

Эта ошибка обычно отображается при попытке загрузить скетч, а не во время выполнения. Можете ли вы успешно загрузить скетч, и ошибка возникает только после считывания датчика?, @PMF

Это происходит во время загрузки(извините, я не упомянул, отредактировал)., @Divyayan Dey

Какие размеры программы и данных показывает компилятор сразу после компиляции вашего скетча?, @StarCat

Ваш опубликованный код не компилируется. Пожалуйста, опубликуйте весь скетч целиком. Мы не можем отлаживать фрагменты кода., @Nick Gammon

Пожалуйста, укажите последние строки выходных данных, где указаны размеры программы и данных., @PMF

Как определяются ОБРАЗЦЫ?, @Majenko

Итак, все, я публикую здесь весь код., @Divyayan Dey

Используя [F("")](https://www.arduino.cc/reference/en/language/variables/utilities/progmem/) макрос на строках Serial.println (""); (сохраняя их во флэш-памяти вместо копирования в оперативную память), я смог уменьшить глобальные переменные до 2038 байт, но это все равно оставляет слишком мало для запуска кода., @StarCat

Со всеми этими поплавками вы подумали, что 8-битный микро без FPU не является оптимальным MCU для этого?, @r_ahlskog


2 ответа


2

Вероятно, вы используете слишком много памяти SRAM:

float arr [], кажется, занимает 4 байта (для float) * 128 = 512 байт.

Тогда у вас есть vReal и `vImage, который принимает 4 (double, float/double одинаковы в Arduino) * 128 * 2 (vReal/vImage) = 1024 байта.

double vReal[SAMPLES];
double vImag[SAMPLES];

Это уже 1536 байт. Arduino имеет только 2048 байт, так что, скорее всего, вы превысите его (включая переданные аргументы и другие глобальные значения).

arr, по-видимому, является массивом только для чтения, поэтому проверьте использование F/PROGMEM для хранения их во Flash вместо SRAM (см. Комментарий StarCat). Это может привести вас хорошо под 2 КБ памяти Arduino Uno. Другие возможности-использовать Mega с 8 КБ или использовать внешнюю память SRAM, но это снижает производительность и требует больше усилий для программирования.

См. Также Мой anser при оптимизации кода для использования меньшего количества флэш-памяти и SRAM

,

См.Мой комментарий: строковые константы (например, используемые в Serial.print("");) также занимают ценную оперативную память. Вы можете использовать макрос F ("..."), чтобы уменьшить их площадь., @StarCat


0

Подумайте о том, чтобы использовать одного из членов семейства Teensy. Последняя версия Teensy 4.0 имеет встроенный FPU и множество других вкусностей и работает на частоте 600 МГц. Зачем ездить на "фольксвагене", когда можно запрыгнуть в "Феррари" за очень небольшие деньги?

Я использую Teensy модули 3.2/3.5 в течение многих лет и обнаружил, что они очень стабильны и просты в использовании.

,

Разве сравнение доступной памяти не было бы самой полезной точкой продажи этого вопроса?, @chicks

Лень переходить по ссылке?, @user3765883