Экстраполяция требует производительности MCU от Arduino MEGA2560.

atmega2560 performance

Я запускаю программу управления электровелосипедом на Arduino MEGA2560, которая использует этот микроконтроллер: http://www.atmel .com/devices/atmega2560.aspx. В настоящее время программа повторяется каждые 100 мс, что дает мне частоту дискретизации 10 Гц для измерений и управляющего сигнала. Мне бы хотелось как минимум в 10 раз больше, так что 100Гц. Как я могу определить, какие характеристики мне нужны, исходя из Arduino MEGA2560 и производительности, которую он мне дает? Спасибо!

Вот мой код, он в основном рассчитывает емкость аккумулятора, запас хода, а также управляет и сглаживает входные сигналы газа:

#include <EEPROM.h>
#include <memorysaver.h>
//#include <TFT_HX8357.h> // Аппаратная библиотека
#include <URTouch.h>
#include <URTouchCD.h>
#include <memorysaver.h>
#include <UTFT.h>


//TFT_HX8357 myGLCD = TFT_HX8357(); // Вызов пользовательской библиотеки#include <TFT_HX8357.h> // Аппаратная библиотека
//
//TFT_HX8357 myGLCD = TFT_HX8357(); // Вызов пользовательской библиотеки

UTFT myGLCD(ILI9481,38,39,40,41);
URTouch myTouch(6, 5, 4, 3, 2);

int buttonst, horn=5, throttle=8, powermode=1, alarmsystem=1, xtouch, ytouch, k1=0, k2=0,modechange,ncycles, p=9, divider=12, addresstd=0;
float pushed, pushedt, timesp, timespm1, powtimesm, factor, powtimesp, start, elapsed, powtime, powresttime, shuntvoltage, batvoltage,  internresistance=0.00112, current, power, consumah, capacstore, consumwh, consumbatper, consumbatrange, distance, distanceint, throttlein, throttleout, distancedisp, Totaldist, velocity, Totaldistcheck;
float throttlesmooth, capaccheck, capacactual,consumahstore, kpow[3]={0,0,0}, kcur[3]={0,0,0}, ksp[3]={0,0,0}, errorpower[2], errorpoweri, errorpowerli, errorcuri, errorcurrent[2], errorpowerl[2], diff, errorspeed[2], errorisp, outspeed, outcurrentctl, outspeedctl, elapsedtot, errorsmooth[2];

float batteryspec[6][56]={{4.2,4,3.94,3.90,3.876,3.86,3.852,3.844,3.826,3.809,3.795,3.783,3.765,3.744,3.728, 3.710,3.692,3.672,3.650,3.631,3.613,3.591,3.573,3.556,3.532,3.508,3.488,3.463,3.439,3.417,3.399,3.380,3.366,3.352,3.332,3.312,3.291,3.273,3.253,3.237,3.217,3.196,3.176,3.150,3.123,3.087,3.051,2.994,2.929,2.868,2.796,2.727,2.650,5.261,2.502},//5А разряд 18650GA SPEC
  {0.001,0.002,0.015,0.047,0.085,0.133,0.186,0.244,0.309,0.383,0.447,0.506,0.554,0.612,0.681,0.735,0.793,0.852,0.921,1.007,1.081,1.150,1.236,1.300,1.358,1.438,1.518,1.587,1.667,1.747,1.822,1.888,1.955,2.014,2.072,2.152,2.232,2.317,2.389,2.466,2.524,2.594,2.658,2.722,2.786,2.847,2.925,2.983,3.063,3.133,3.186,3.245,3.293,3.330,3.357,3.368},
  {4.20,4.02,3.980,3.945,3.927,3.914,3.904,3.890,3.874,3.860,3.843,3.821,3.797,3.776,3.756,3.734,3.713,3.695,3.675,3.652,3.634,3.614,3.593,3.569,3.543,3.522,3.5,3.480,3.459,3.441,3.419,3.398,3.378,3.360,3.339,3.321,3.301,3.278,3.254,3.230,3.199,3.164,3.128,3.085,3.037,2.990,2.937,2.878,2.837,2.795,2.746,2.693,2.648,2.598,2.549,2.496},
  {0,0.007,0.033,0.084,0.151,0.223,0.294,0.377,0.457,0.521,0.574,0.646,0.718,0.785,0.851,0.931,1.011,1.091,1.171,1.251,1.323,1.397,1.469,1.547,1.632,1.701,1.778,1.853,1.928,1.994,2.088,2.175,2.258,2.333,2.413,2.487,2.562,2.634,2.706,2.775,2.842,2.908,2.697,3.023,3.074,3.116,3.159,3.205,3.234,3.261,3.287,3.314,3.333,3.352,3.365,3.379},//3А разряд 18650GA SPEC
  {4.2,4.131,4.094,4.073,4.054,4.038,4.028,4.015,4.003,3.985,3.965,3.945,3.921,3.894,3.870,3.850,3.830,3.811,3.795,3.775,3.759,3.739,3.716,3.692,3.674,3.648,3.623,3.601,3.578,3.561,3.542,3.522,3.502,3.486,3.467,3.445,3.423,3.401,3.374,3.350,3.324,3.291,3.253,3.210,3.168,3.123,3.075,3.026,2.964,2.897,2.838,2.781,2.715,2.652,2.585,2.502},
  {0,0,0.036,0.084,0.154,0.223,0.289,0.356,0.420,0.489,0.553,0.620,0.686,0.761,0.838,0.908,0.985,1.054,1.134,1.214,1.283,1.358,1.438,1.523,1.584,1.667,1.755,1.835,1.920,2.002,2.085,2.170,2.255,2.327,2.402,2.482,2.567,2.644,2.724,2.791,2.850,2.908,2.964,3.015,3.058,3.095,3.132,3.167,3.207,3.245,3.271,3.295,3.320,3.336,3.354,3.365}}; //1А разряд 18650GA СПЕЦ.

float cyclicspec[2][44]={{0,0.894,7.151,16.09,26.4,37.5,50.95,61.68,74.19,85.36,96.54,107.26,118.88,130.95,142.12,154.64,168.04,182.35,194.86,208.27,219.89,234.2,246.7,262.8,276.2,291.4,303.9,317.3,327.15,341.5,354,364,374.5,383.46,395.08,405.8,418.3,431.7,444,455.9,468.8,481.9,491.6,499.7},
  {3441.3,3435.7,3342.9,3292.9,3235.7,3178.6,3121.4,3078.6,3014.28,2957.1,2921.4,2892.9,2842.9,2792.9,2750,2721.4,2678.6,2642.9,2607.1,2564.3,2542.9,2507.1,2478.6,2435.7,2407.1,2378.6,2357.1,2335.7,2335.7,2335.7,2335.7,2335.7,2335.7,2321.4,2300,2278.6,2271.4,2242.9,2228.6,2214.3,2192.9,2178.6,2164.3,2157.1}};

extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];

void EEPROMWritelong(int address, long value)
      {
      //Разложение длинного значения на 4 байта с помощью битового сдвига.
      //Один = Наиболее значимый -> Четыре = младший значащий байт
      byte four = (value & 0xFF);
      byte three = ((value >> 8) & 0xFF);
      byte two = ((value >> 16) & 0xFF);
      byte one = ((value >> 24) & 0xFF);

      //Записываем 4 байта в память EEPROM.
      EEPROM.write(address, four);
      EEPROM.write(address + 1, three);
      EEPROM.write(address + 2, two);
      EEPROM.write(address + 3, one);
      }

float EEPROMReadlong(int address)
      {
      //Читаем 4 байта из памяти EEPROM.
      long four = EEPROM.read(address);
      long three = EEPROM.read(address + 1);
      long two = EEPROM.read(address + 2);
      long one = EEPROM.read(address + 3);

      //Возвращаем перекомпонованную длину с помощью битового сдвига.
      return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
      }

float pidcurrent_control() {
 float dt, outcurrent=0, outpower=0;
 int i=0, descurrent, despower;
 i++;
 dt=millis()-powtime;
 powtime = millis();
 if (powermode==1) { //Режим Furo по умолчанию
  if (current>40) {
    descurrent=40;
    errorcurrent[1]=descurrent-current;
    if (i>=2) {
      //Интегрируем ошибку
    errorcuri+=errorcurrent[1]*dt;

    outcurrent=(kcur[0]*errorcurrent[1]+kcur[1]*errorcuri+kcur[2]*((errorcurrent[1]-errorcurrent[0])/dt));
    }
    errorcurrent[0]=errorcurrent[1];
    return outcurrent;
    }  
    if ((batvoltage*current)>2000) {
     despower=2000;
      errorpower[1]=despower-batvoltage*current;
      if (i>=2) {
    //Интегрируем ошибку
    errorpoweri+=errorpower[1]*dt;
    outpower=kpow[0]*errorpower[1]+kpow[1]*errorpoweri+kpow[2]*((errorpower[1]-errorpower[0])/dt);
    }
    errorpower[0]=errorpower[1];
    return outpower;
    } 
    }
  else { //Эко-режим
   despower=550;
   errorpowerl[1]=despower-batvoltage*current;
      if (i>=2) {
      //Интегрируем ошибку
      errorpowerli+=errorpowerl[1]*dt;
      outpower=kpow[0]*errorpowerl[1]+kpow[1]*errorpowerli+kpow[2]*((errorpowerl[1]-errorpowerl[0])/dt);
    }
    errorpowerl[0]=errorpowerl[1];
    return outpower;
    }  
    }

void cyclescaling() {
int j=1;
 if (ncycles<=499) {
 while ((ncycles<cyclicspec[0][j-1])||(ncycles>cyclicspec[0][j])){    
           j++;
    }
    factor=(cyclicspec[1][j-1]+(ncycles-cyclicspec[0][j-1])*(cyclicspec[1][j]-cyclicspec[1][j-1])/(cyclicspec[0][j]-cyclicspec[0][j-1]))/cyclicspec[1][0];
}
else {
    factor=(cyclicspec[1][41]+(ncycles-cyclicspec[0][41])*(cyclicspec[1][42]-cyclicspec[1][41])/(cyclicspec[0][42]-cyclicspec[0][41]))/cyclicspec[1][0];
}
}

float pidspeed_control() {
int isp, dtsp;
isp++;
dtsp=millis()-powtimesp;
powtimesp = millis();
errorspeed[1]=46-velocity;
if (isp>=2) {
    errorisp+=errorspeed[1]*dtsp;  
    outspeed=(ksp[0]*errorspeed[1]+ksp[1]*errorisp+ksp[2]*((errorspeed[1]-errorspeed[0])/dtsp));
    }
    errorspeed[0]=errorspeed[1];
    return outspeed;
}



void throttlesmoothing() {
 int i=0, dtsm;
 i++;
 float currentmap=map(throttlein,0.8,3.6,0,40);
 dtsm=millis()-powtimesm;
 powtimesm = millis();
 errorsmooth[1]=currentmap-current;
      if (i>=2) {
      //Интегрируем ошибку
      throttlesmooth=kcur[0]*errorsmooth[1]+kcur[2]*((errorsmooth[1]-errorsmooth[0])/dtsm);
    }
    errorsmooth[0]=errorsmooth[1];
}

void SOC() {
  float currentref=current/p;
  int j=1,l=1;
  float stateofc1=0, stateofc2=0;

  if (currentref<=1){
    while (((batvoltage/14)>batteryspec[0][j-1])||((batvoltage/14)<batteryspec[0][j])){    
           j++;
    }
    capacactual=(batteryspec[1][55]-(batteryspec[1][j-1]+(batvoltage/14-batteryspec[0][j-1])*(batteryspec[1][j]-batteryspec[1][j-1])/(batteryspec[0][j]-batteryspec[0][j-1])))*p;

  }

 if ((currentref>1)&&(currentref<=3)){
    while (((batvoltage/14)>batteryspec[0][j-1])||((batvoltage/14)<batteryspec[0][j])){    
           j++;
    }
    stateofc1=(batteryspec[1][55]-(batteryspec[1][j-1]+(batvoltage/14-batteryspec[0][j-1])*(batteryspec[1][j]-batteryspec[1][j-1])/(batteryspec[0][j]-batteryspec[0][j-1])))*p;

     while (((batvoltage/14)>batteryspec[2][l-1])||((batvoltage/14)<batteryspec[2][l])){    
           l++;
    }
    stateofc2=(batteryspec[3][55]-(batteryspec[3][l-1]+(batvoltage/14-batteryspec[2][l-1])*(batteryspec[3][l]-batteryspec[3][l-1])/(batteryspec[2][l]-batteryspec[2][l-1])))*p;
    capacactual=stateofc1+(currentref-1)*(stateofc2-stateofc1)/(3-1);
  }

   if ((currentref>3)&&(currentref<=5)){
    while (((batvoltage/14)>batteryspec[2][j-1])||((batvoltage/14)<batteryspec[2][j])){    
           j++;
    }
// stateofc1=(batteryspec[3][55]-(batteryspec[3][j-1]+(batvoltage/14-batteryspec[2][j-1])*(batteryspec[3][j]-batteryspec [3][j-1])/(batteryspec[2][j]-batteryspec[2][j-1])))*p;

     while (((batvoltage/14)>batteryspec[2][l-1])||((batvoltage/14)<batteryspec[2][l])){    
           l++;
    }
// stateofc2=(batteryspec[3][55]-(batteryspec[3][l-1]+(batvoltage/14-batteryspec[2][l-1])*(batteryspec[3][l]-batteryspec [3][l-1])/(batteryspec[2][l]-batteryspec[2][l-1])))*p;
    capacactual=(batteryspec[3][55]-(batteryspec[3][j-1]+(batvoltage/14-batteryspec[2][j-1])*(batteryspec[3][j]-batteryspec[3][j-1])/(batteryspec[2][j]-batteryspec[2][j-1])))*p+(currentref-1)*((batteryspec[5][55]-(batteryspec[5][l-1]+(batvoltage/14-batteryspec[4][l-1])*(batteryspec[5][l]-batteryspec[5][l-1])/(batteryspec[4][l]-batteryspec[4][l-1])))*p-(batteryspec[3][55]-(batteryspec[3][j-1]+(batvoltage/14-batteryspec[2][j-1])*(batteryspec[3][j]-batteryspec[3][j-1])/(batteryspec[2][j]-batteryspec[2][j-1])))*p)/(5-3);

  }


if (currentref>5) {
   while (((batvoltage/14)>batteryspec[4][j-1])||((batvoltage/14)<batteryspec[4][j])){    
           j++;
    }
    capacactual=(batteryspec[5][55]-(batteryspec[5][j-1]+(batvoltage/14-batteryspec[4][j-1])*(batteryspec[5][j]-batteryspec[5][j-1])/(batteryspec[4][j]-batteryspec[4][j-1])))*p;
}

}


void autonomy() {

  if (modechange==1) {
   consumah=0;
  }

    power=batvoltage*current;
    SOC();
    if (capacactual!=capaccheck) {
      capaccheck=(capacactual+capaccheck)/2;
    }

  if (abs(capacactual-capacstore)>=0.1){   
     EEPROMWritelong(8,roundf(capacactual*1000));
     capacstore=capacactual;
  }

  consumah += current*elapsed/1000/3600;
  capaccheck -= current*elapsed/1000/3600;
  consumwh += current*elapsed/1000/3600*batvoltage;
  elapsedtot += elapsed;
  consumbatper = (capaccheck)/(batteryspec[1][55]*p)*100;
  if (distance==0) {
    switch (powermode) {
      case 1: consumbatrange=45*batvoltage*capaccheck/980;
      break;
      case 2: consumbatrange=35*batvoltage*capaccheck/550;
      break;
    }

  } else {
  consumbatrange = distance/consumah*(capaccheck);
  }
  myGLCD.printNumF(consumbatrange, 2, 350,275);
  myGLCD.printNumF(power, 2, 350,205);
  myGLCD.printNumF(current, 1, 30,50);
  myGLCD.printNumF(ncycles, 1, 30,50+16*2);
  myGLCD.printNumF(consumbatper,1,350,50);
  myGLCD.printNumF(batvoltage,1,350,50+16*2);

  return 0;

}



void setup() {
  // поместите сюда свой код установки для однократного запуска:
  Serial.begin(9600);
  start=millis();
  pinMode(43, OUTPUT);// Скорость вращения
  pinMode(46, INPUT);//Спидометр
  pinMode(A2, INPUT);//Дроссель в
  pinMode(A7, INPUT);//Напряжение батареи
  pinMode(A4, INPUT);//Шунт
  pinMode(throttle, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(47, INPUT);

  analogWrite(10,255);
  analogWrite(11,255);

  myGLCD.InitLCD(LANDSCAPE);
  myTouch.InitTouch(LANDSCAPE);
  myGLCD.fillScr(0,0,0);
  myGLCD.setColor(VGA_WHITE);
  myGLCD.setBackColor(0,0,0);
  myGLCD.setFont(BigFont);
  myGLCD.print("FURO SYSTEMS",(480-16*12)/2,16);
  myGLCD.print("WELCOME",(480-16*7)/2,(320-16)/2);
  myGLCD.setDisplayPage(0);
  delay(2000);
  myGLCD.clrScr();
  myGLCD.print("FURO SYSTEMS",(480-16*12)/2,16);
  myGLCD.fillRoundRect((480-3*16-8)/2,320-16*2-4,(480-3*16-8)/2+3*16+6,228);
  myGLCD.print("ECO", (480-3*16)/2, 320-16*5);
// EEPROMWritelong(4,1);
  ncycles=EEPROMReadlong(4);
  cyclescaling();

  for (int m=1; m<=5;m+=2) {
  for (int n=0; n<=55;n++) {
    batteryspec[m][n]*=factor;
    }
  }


  current=analogRead(A4);
  current=current/internresistance*5/1023/75;;

  batvoltage=analogRead(A7);
  batvoltage=batvoltage*5*divider/1023;

  SOC();
  capaccheck=capacactual;
  capacstore=EEPROMReadlong(8)/1000;

  if (capacactual>capacstore) {
  ncycles++;
  EEPROMWritelong(4,ncycles);
  EEPROMWritelong(8,roundf(capacactual*1000));
  }

  if (capacactual<0.5) {
    powermode=2;
    myGLCD.fillRoundRect((480-4*16-8)/2,320-16*2-4,(480-4*16-8)/2+4*16+6,228);
    myGLCD.print("FURO", (480-4*16)/2, 320-16*5);
  }//3,21 В на ячейку
  EEPROMReadlong(addresstd);
  Totaldist=EEPROMReadlong(addresstd);
  Totaldistcheck=Totaldist;
  digitalWrite(43,HIGH);
}

void loop() {

   throttlein=analogRead(A2);
   throttlein=throttlein*5/1023;
   throttlesmoothing();
   throttleout=round((0.4+throttlein)*255/5);
   analogWrite(8,throttleout);


   modechange=0;
    if (digitalRead(47)==LOW)
    { 
      if (buttonst==1) {
      pushed = millis();
      }
      buttonst=0;

      if ((millis()-pushed)>=2000)
      {
       distancedisp=0;
      }
      }

     if (digitalRead(47)==HIGH) {
      if (buttonst==0) {
        modechange=1;
        pushedt=millis()-pushed;
        if (pushedt<1900){
       if (powermode==1) 
      { powermode=2;
       myGLCD.clrScr();
       myGLCD.print("FURO SYSTEMS",(480-16*12)/2,16);
       myGLCD.fillRoundRect((480-4*16-8)/2,320-16*2-4,(480-4*16-8)/2+4*16+6,228);
       myGLCD.print("FURO", (480-4*16)/2, 320-16*5);
      }
      else  
      { 
        powermode=1;
        myGLCD.clrScr();
        myGLCD.print("FURO SYSTEMS",(480-16*12)/2,16);
        myGLCD.fillRoundRect((480-3*16-8)/2,320-16*2-4,(480-3*16-8)/2+3*16+6,228);
        myGLCD.print("ECO", (480-3*16)/2, 320-16*5);
      }
      }
      }
      buttonst=1;
     }


batvoltage=analogRead(A7);
batvoltage=batvoltage*5*divider/1023;
current=analogRead(A4);
current=current/internresistance*5/1023/75;
elapsed = millis() - start;
start = millis();

if (modechange==1) {
   distance=0;
}
distanceint = (velocity/3.6*elapsed*0.001)/1000;
distance += distanceint;
distancedisp += distanceint;
Totaldist += distanceint;

if (abs(Totaldist-Totaldistcheck)>=0.1) { //позже сделаем это при выключении
  EEPROMWritelong(addresstd,round(Totaldist));
  Totaldistcheck=Totaldist;
}

autonomy();

if (velocity>45) {
outspeedctl=pidspeed_control();
outcurrentctl=pidcurrent_control();
float throttleout1=min(outspeedctl,outcurrentctl);
throttleout=min(throttleout1,throttlesmooth);
analogWrite(throttle, map(throttleout, 0,5,0,255));
} else{
outcurrentctl=5;
outcurrentctl=pidcurrent_control();
throttleout=min(throttlesmooth,outcurrentctl);
analogWrite(throttle, map(throttleout, 0,5,0,255));
}

myGLCD.setFont(SevenSegNumFont);
myGLCD.printNumI(velocity, (480-32*3)/2, (320-50)/2);
myGLCD.setFont(BigFont);
myGLCD.printNumF(distancedisp, 1, 30,175);
myGLCD.printNumI(Totaldist, 30,175+2*16);

}

, 👍0

Обсуждение

Почему ваш цикл такой медленный?, @Ignacio Vazquez-Abrams

У меня работают 3 ПИД-регулятора, сложные вычисления, а также несколько схем для выборки. Я отредактировал свой вопрос, чтобы отобразить свой код., @Eliott W

Прекратите использовать математику с плавающей запятой! Просто преобразуйте свои числа в целые числа любой точности, которая вам нужна. Процессоры AVR не имеют FPU, поэтому все математические вычисления с плавающей запятой реализуются программно. Простое сложение занимает много-много тактов, в отличие от нескольких для целых чисел., @bss36504


1 ответ


4

Я вижу три вещи, которые сдерживают ваш код.

AnalogRead в Arduino очень медленный. АЦП в чипе не самый лучший, но и функция AnalogRead будет удерживать вас там, пока не будет завершено полное чтение и преобразование. Возможно, удастся запустить выборку и преобразование и прочитать ответ позже. Ищите чип, который может производить выборку нескольких каналов АЦП без вмешательства процессора. Цифровой сигнальный процессор (DSP), такой как dspic33epxxxxxx, может быть очень полезен. У них очень высокая аналоговая выборка.

Кроме того, запись в EEPROM очень медленная. Поиск способа обойти их или использование внешней памяти может помочь увеличить вашу скорость. Запись в EEPROM должна удерживать ваш код до тех пор, пока запись не будет завершена. Это очень похоже на АЦП.

Вычисления с плавающей точкой внутри процессора, в котором нет математического ядра с плавающей точкой, подобны выстрелу себе в ногу, все, что вы можете сделать оттуда, это безвольно хромать. Найдите минимальное количество десятичных знаков, с которым вы можете жить, и перенесите их в мир целых чисел. Например, 12,7325 X 10000 вместо этого может быть 127325. Затем масштабируйте все ваши другие вычисления с учетом этого масштабного коэффициента. Математика более сложная, но 10–15 циклов для умножения целого числа лучше, чем 200–1000 циклов для умножения числа с плавающей точкой. Правильно включив какой-либо другой масштабный коэффициент, показания АЦП можно использовать напрямую, без преобразования.

,

Спасибо! Я так и сделаю! Если бы я рассчитал время для своих чисел с плавающей точкой на тысячу, а затем использовал бы функцию округления для получения целого числа, это было бы полезно или функция округления тоже была бы очень затратной с точки зрения вычислений?, @Eliott W

Все ли процессоры требуют этого? Я хочу позже отойти от Arduino и сделать свою собственную платформу, есть ли процессоры, которые позволят мне использовать плавающие точки, быстрее записывать в EEPROM, делать выборку без вмешательства процессора и т. д.?, @Eliott W

Вы почти никогда не захотите использовать числа с плавающей точкой во встроенной среде; инвестиции в MCU с FPU редко оправдывают себя. Запись в EEPROM всегда медленная. AVR могут выполнять выборку, не занимая CPU; ваша проблема в библиотеках Arduino, а не в MCU. Скорее всего, ATmega2560 отлично подойдет для этого приложения., @Ignacio Vazquez-Abrams