Messwerte glätten

Manchmal zappelt das Messsignal etwas, dann ist es sinnvoll eine Glättung einzusetzen. Im wesentlichen finde ich dazu zwei mögliche Verfahren: einfacher und gewichteter Mittelwert. Beide sind recht einfach umzusetzen.

Initialisierung
Die wichtigste Frage dazu ist die Initialisierung der verwendeten Variablen. Setze ich sie erst einmal auf Null und fülle dann die Messwerte auf, so erhalte ich ein sichtbares Einschwingverhalten. Das lässt sich aber verhindern, wenn man die Variablen im ersten Durchgang mit dem Messwert initialisiert.

Codierung
Im folgenden habe ich das codiert für Variablen mit und ohne Vorzeichen. Die Initialisierung der gewichteten Glättung mit zwei Variablen (Simple) kann im Setup() mit einem Messwert initialisiert werden. Beim einfachen Glätten (CircBuf) in einem Ringpuffer Verfahren werden im ersten Durchlauf alle folgenden ArrayMember mit demselben Messwert aufgefüllt. In allen weiteren Durchläufen wird dann Wert für Wert nacheinander eingetragen und der Durchschnittswert berechnet.

Header

/****************************************************************************
 * (c) 2019 softwareentwicklung-als-prozess.de
 * Funktionalitaet:
 * Messwerte glaetten
 ***************************************************************************/
#ifndef GLAETTENHEADER
#define GLAETTENHEADER

// unsigned messwerte
void       smooth_SimplePresetU(uint16_t prst);
uint16_t   smooth_SimpleU(uint16_t messwert);
uint16_t   smooth_CircBufU(uint16_t messwert);
// signed messwerte
void       smooth_SimplePresetS(signed int prst);
signed int smooth_SimpleS(signed int messwert);
signed int smooth_CircBufS(signed int messwert);

#endif

Source

/****************************************************************************
 * (c) 2019 softwareentwicklung-als-prozess.de
 * Funktionalitaet:
 * Messwerte glaetten
 ***************************************************************************/
// Includes 
#include <arduino.h>

#include "glaetten.h"

// Definitionen
#define ALPHA 90
#define NUMREADINGS 10

// interne Prototypen
// unsigned messwerte
static void      PresetU(uint8_t startinx, uint16_t mw);
// signed messwerte
static void      PresetS(uint8_t startinx, signed int mw);

// globale Variablen
// unsigned messwerte
static uint32_t smplsmoothu = (uint32_t)0;
static uint16_t readingsu[NUMREADINGS] = {(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0,(uint16_t)0};
static uint8_t  readIndexu   = (uint8_t)0;
static uint32_t totalu       = (uint32_t)0;
static bool     ersterundeu  = true;  // presets auf messwert setzen -> kein einschwingen abwarten

// signed messwerte
static signed long smplsmooths = (signed long)0;
static signed int  readingss[NUMREADINGS] = {0,0,0,0,0,0,0,0,0,0};
static uint8_t     readIndexs   = (uint8_t)0;
static signed long totals       = (signed long)0;
static bool        ersterundes  = true;  // presets auf messwert setzen -> kein einschwingen abwarten

// Funktionen
void smooth_SimplePresetU(uint16_t prst)
{
  smplsmoothu = prst;  
}

uint16_t smooth_SimpleU(uint16_t messwert)
{
  smplsmoothu = ((smplsmoothu * (uint32_t)ALPHA) + ((uint32_t)messwert * ((uint32_t)100 - (uint32_t)ALPHA)));
  smplsmoothu /= (uint32_t)100;
  return((uint16_t)smplsmoothu);
}

void smooth_SimplePresetS(signed int prst)
{
  smplsmooths = prst;  
}

signed int smooth_SimpleS(signed int messwert)
{
  smplsmooths = ((smplsmooths * (signed long)ALPHA) + ((signed long)messwert * ((signed long)100 - (signed long)ALPHA)));
  smplsmooths /= (signed long)100;
  return((signed int)smplsmooths);
}

uint16_t smooth_CircBufU(uint16_t messwert) 
{
  if ((bool)true == ersterundeu)
  {
    PresetU(readIndexu, messwert);
  }
  totalu = totalu - (uint32_t)readingsu[readIndexu];// aeltesten Messwert abziehen
  readingsu[readIndexu] = messwert;                 // aktuellen messwert einlesen
  totalu = totalu + (uint32_t)readingsu[readIndexu];// und aufaddieren
  readIndexu = readIndexu + (uint8_t)1;             // naechster wert
  if (readIndexu >= (uint8_t)NUMREADINGS)           // Pufferende erreicht
  { 
    readIndexu = (uint8_t)0;                        // naechste runde starten
    if ((bool)true == ersterundeu)                  // presetting abgeschlossen
    {
      ersterundeu = (bool)false;
    }
  }
  return((uint16_t)(totalu / (uint32_t)NUMREADINGS)); // voila, der Durchschnitt
}

static void PresetU(uint8_t startinx, uint16_t mw)
{
  uint8_t i = (uint8_t)0;
  uint8_t j = (uint8_t)0;

  for(i = startinx; i < (uint8_t)NUMREADINGS; i++)
  {
    readingsu[i] = mw;
  }
  totalu = (uint32_t)0;
  for(j = (uint8_t)0; j < startinx; j++)
  {
    totalu += (uint32_t)readingsu[j];
  }
  totalu += ((uint32_t)((uint8_t)NUMREADINGS - startinx) * (uint32_t)mw);
}

signed int smooth_CircBufS(signed int messwert) 
{
  if ((bool)true == ersterundes)
  {
    PresetS(readIndexs, messwert);
  }
  totals = totals - (signed long)readingss[readIndexs];// aeltesten Messwert abziehen
  readingss[readIndexs] = messwert;                 // aktuellen messwert einlesen
  totals = totals + (signed long)readingss[readIndexs];// und aufaddieren
  readIndexs = readIndexs + (uint8_t)1;             // naechster wert
  if (readIndexs >= (uint8_t)NUMREADINGS)           // Pufferende erreicht
  { 
    readIndexs = (uint8_t)0;                        // naechste runde starten
    if ((bool)true == ersterundes)                  // presetting abgeschlossen
    {
      ersterundes = (bool)false;
    }
  }
  return((uint16_t)(totals / (signed long)NUMREADINGS)); // voila, der Durchschnitt
}

static void PresetS(uint8_t startinx, signed int mw)
{
  uint8_t i = (uint8_t)0;
  uint8_t j = (uint8_t)0;

  for(i = startinx; i < (uint8_t)NUMREADINGS; i++) // Messwerte eintragen
  {
    readingss[i] = mw;
  }
  totals = 0;
  for(j = (uint8_t)0; j < startinx; j++)          // Gesamtwert berechnen
  {
    totals += (signed long)readingss[j];
  }
  totals += ((signed long)((uint8_t)NUMREADINGS - startinx) * (signed long)mw);
}

Ergebnis
Das ständige „zappeln“ der letzten Stelle(n) in der Ausgabe verschwindet oder wird etwas ruhiger.

Links

Glättungsverfahren
https://de.wikipedia.org/wiki/Glätten_(Mathematik)

Gleitender Mittelwert
https://de.wikipedia.org/wiki/Gleitender_Mittelwert

Veröffentlicht von

Jürgen

Ich bin Software Ingenieur und habe meine Schwerpunkte in allen Aktivitäten, die zur Software Entwicklung gehören. Am längsten bin ich als Software Entwickler von Embedded Software in C tätig.