M5Stackなどでセンサの値を取得する際、センサによってはノイズが多い場合があります。
センサから取得した値に基づいて何らかの処理を行おうとする場合、ノイズが多いとその特異点でも処理を行なってしまうことになり、その結果、所望の処理を行えない可能性もあります。
そんな訳で、ここではデータの平滑化をしてみたいとおもいます。
直感的で分かりやすい方法がよいので、直近の10個のデータの算術平均をとることにします(単純移動平均)。
スケッチはこちらです。
今回はセンサは使わず、0〜4095の間のランダムな値を取得し、それを 3.3/4096 倍した値を使うことにします(ランダムな値なので特異点だらけということになります)。
#include <M5Stack.h>
int interval = 1;
unsigned long prev = millis();
const int elementNumber = 10;
int elementCount = 0;
float subset[elementNumber];
float movingAverage(float value) {
subset[elementCount++] = value;
if(elementCount >= elementNumber) elementCount = 0;
float result = 0;
for(int i=0; i<elementNumber; i++) result += subset[i];
result = result / elementNumber;
return result;
}
void setup() {
M5.begin();
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setTextDatum(4);
}
int cnt = 0;
void loop() {
unsigned long now = millis();
if(now - prev >= interval*1000) {
float val0 = random(0,4096) * 3.3 / 4096.0;
float val1 = movingAverage(val0);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawFloat(val0, 2, 160, 60, 8);
M5.Lcd.drawFloat(val1, 2, 160, 180, 8);
Serial.printf("%d,%.5f,%.5f\n",cnt++, val0, val1);
prev = now;
}
}
1秒毎に値を取得し、生値と平滑化した値をLCD画面とシリアルモニタに表示します。
実行した結果をExcelでグラフ表示してみます。
生値(青の線)はガタガタでデタラメな波形ですが、平滑化した値(赤の線)は変動が滑らかになっています。
ただ、この場合、直近のデータを格納する配列の初期値が「0」となっているため、10個のデータが揃うまでは、平滑化した値が小さめになってしまいます。
これを避けるため、配列に初期値を設定することとします。
#include <M5Stack.h>
int interval = 1;
unsigned long prev = millis();
const int elementNumber = 10;
int elementCount = 0;
float subset[elementNumber];
float movingAverage(float value) {
subset[elementCount++] = value;
if(elementCount >= elementNumber) elementCount = 0;
float result = 0;
for(int i=0; i<elementNumber; i++) result += subset[i];
result = result / elementNumber;
return result;
}
void initSubset(float value) {
for(int i=0; i<elementNumber; i++) subset[i] = value;
}
void setup() {
M5.begin();
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setTextDatum(4);
initSubset(1.65);
}
int cnt = 0;
void loop() {
unsigned long now = millis();
if(now - prev >= interval*1000) {
float val0 = random(0,4096) * 3.3 / 4096.0;
float val1 = movingAverage(val0);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawFloat(val0, 2, 160, 60, 8);
M5.Lcd.drawFloat(val1, 2, 160, 180, 8);
Serial.printf("%d,%.5f,%.5f\n",cnt++, val0, val1);
prev = now;
}
}
配列の初期値に「1.65」を設定したため、平滑化した値(赤の線)は最初から中間値付近で変動しています。
もちろん、初期値を「3.3」に設定すると、10個のデータが揃うまで、平滑化した値は大きめになります。
値がしきい値を下回ったときに何らかの処理を行う場合などは、このようにすることで不要な処理をしないようにすることもできそうです。
GROVEダストセンサを使って室内のホコリの濃度を測定してみました。
先ほどまでと同じく、青い線が生値、赤い線が平滑化した値です。
このダストセンサはノイズが多いのですが、平滑化の処理を行うことで、波形が滑らかになっています。