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