Vyhladzovanie analógového merania pomocou plávajúceho priemeru

Zápisník experimentátora

Počas prípravy pokračovania článku o hodinách z NeoPixel Ring som napísal tento kód. Je to rýchla implementácia plávajúceho priemeru, ktorý sa hodí na vyhladzovanie meraní, ktoré mierne oscilujú okolo jednej hodnoty. Princíp plávajúceho priemeru je jednoduchý. Zaznamenávate si X hodnôt dozadu a výsledok je suma hodnôt / X. Podrobnosti o plávajúcom priemere si môžete naštudovať na stránke na Wikipédií.

Plávajúci priemer si vysvetlíme na kóde, ktorý využíva fotorezistor a analógový pin na Arduíne na meranie intenzity svetla. O meraní intenzity cez fotorezistor si môžete prečítať podrobnosti v samostatnom článku. Tu sa budeme venovať už len samotnému procesu vyhladzovania.

Ako zapojenie využijeme zapojenie z obrázka vpravo. Vcc je tam pripojené na fotorezistor, čo nám dodáva výsledky v podobe:

  • Malé hodnoty pre slabé osvetlenie.
  • Veľké hodnoty pre silné osvetlenie.

Zaujímavý je pre nás kód plávajúceho priemeru. Napísal som ho v podobe šablóny, ktorej nastavíte dátový typ a počet záznamov v bufferi.

///
/// Moving average
///
/// Simple moving average with fast calculation.
/// typ - any type (int, float, ...)
/// sz - size of buffer
///
template<typename typ, int sz> class MovingAverage
{
  typ data[sz]; // buffer
  int pos;      // actual position in buffer
  typ sum;      // precalculated sum
  
public:
  MovingAverage()
  : pos(0), sum(0)
    {
    memset(data,0,sizeof(data));
    }
    
  void Push(typ value)
    {
    sum-=data[pos];  
    sum+=value;
    data[pos]=value;
    pos++;
    if(pos==sz)
      pos=0;
    }
    
  typ MA()
    {
    return sum/sz;
    }
};

MovingAverage<uint16_t,16> ma;

A použitie v kóde je nasledovné. Meriame analógové hodnoty, zasielame ich do buffera a na ďalšie výpočty už používame hodnotu vypočítaného plávajúceho priemeru.

void loop() {
uint16_t sv = analogRead(A0);
ma.Push(sv);
uint16_t sva = ma.MA();
int svc=constrain(sva,100,900);
int m=map(svc,100,900,0,255);
Serial.print(sv);
Serial.print(", ");
Serial.print(sva);
Serial.print(", ");
Serial.print(svc);
Serial.print(", ");
Serial.print(m);
Serial.println("");
delay(1000);
}

Toto je elementárna implementácia plávajúcich priemerov, ktorá ale splní všetky bežné požiadavky. Môžete vyskúšať aj iné implementácie zo stránok Arduina.



Download

07.08.2015


Menu