Vyhlazování analogového měření pomocí plovoucího průměru

Zápisník experimentátora

Během přípravy pokračování článku o hodinách z NeoPixel Ring jsem napsal tento kód. Je to rychlá implementace plovoucího průměru, který se hodí na vyhlazování měření, které mírně oscilují okolo jedné hodnoty. Princip plovoucího průměru je jednoduchý. Zaznamenáváte si X hodnot dozadu a výsledek je suma hodnot / X. Podrobnosti o plovoucím průměru si můžete nastudovat na stránce na Wikipedii.

Plovoucí průměr si vysvětlíme na kódu, který využívá fotorezistor a analogový pin na Arduinu na měření intenzity světla. O měření intenzity přes fotorezistor si můžete přečíst podrobnosti v samostatném článku. Zde se budeme věnovat už jen samotnému procesu vyhlazování.

Jak zapojení využijme zapojení z obrázku vpravo. Vcc je tam připojeno na fotorezistor, což nám dodává výsledky v podobě:

  • Malé hodnoty pro slabé osvětlení.
  • Velké hodnoty pro silné osvětlení.

Zajímavý je pro nás kód plovoucího průměru. Napsal jsem ho v podobě šablony, v níž nastavíte datový typ a počet záznamů v bufferu.

///
/// 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žití v kódu je následující. Měříme analogové hodnoty, zasíláme je do bufferu a na další výpočty už používáme hodnotu vypočítaného plovoucího průměru.

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ární implementace plovoucích průměrů, která ale splní všechny běžné požadavky. Můžete vyzkoušet i jiné implementace ze stránek Arduina.



Download

15.06.2018


Menu