teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

平滑化の修正追記

2020/01/02 02:51

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -6,4 +6,50 @@
6
6
  最初はありえないほど小さな値にしておくか、それとも一番最初に取得舌データを入れておいて、
7
7
  新しいデータが得られる度にその最大値を保持する変数と新しいデータとを比べて、
8
8
  新しいデータの方が大きい値なら最大値を新しいデータで書き換えれば、
9
- いいんじゃない?
9
+ いいんじゃない?
10
+
11
+ ---
12
+ 平滑化(移動平均)処理の誤りについて
13
+
14
+ 平均ってどうやるか、これは言うまでもなく、要素全部の和/要素の数を計算すればいいわけです。
15
+ ここで、1023が150個あるはずの平均の計算が1023*151/150の値になったとすれば、「要素全部の和」が正しく求められているかをチェックする、というのが流れでしょう。
16
+ その計算は、
17
+ ` for(int i=0;i<n;i++) sensorValue+=f[i];`
18
+ でやっているわけですがしかし、このループに入る前にsensorValueには
19
+ `sensorValue = analogRead(sensorPin);`
20
+ という値が与えられています。これによって1023が151個分足される、ということになるわけです。ここを修正する必要があります。
21
+
22
+ ---
23
+ analogRead()は「実行したまさにそのときの値」
24
+
25
+ 多分大問題にはならないでしょうけど...analogRead()は、そのときそのときのADコンバータの値を返してきます。電圧が変化していたなら2度連続で読んで、おなじ値になる保証はありません。
26
+ ```
27
+ sensorValue = analogRead(sensorPin);//ここと
28
+ for(int i=n-1;i>0;i--)f[i]=f[i-1];
29
+ f[0]=analogRead(1);//ここ
30
+ ```
31
+ 温度のデータならこの短時間で大きく変化はしないだろう、というのは言えますけれど、一般論としてはよろしい手法ではないです。
32
+ ```
33
+ int tmpValue= analogRead(sensorPin); //取得は一回だけ
34
+ sensorValue=tmpValue;
35
+ for(int i=n-1;i>0;i--)f[i]=f[i-1];
36
+ f[0]=tmpValue;
37
+ for(int i=1;i<n;i++) sensorValue+=f[i];
38
+
39
+ ```
40
+ などとして、取得は一度だけにしておいた方がよいでしょう。
41
+ millis()による時間取得で似たようなことをやって、微妙なバグを生んでしまう例をみたことがあります。random()を何度も呼んでしまって動きが目茶苦茶になって...という質問も(ここではありませんが)先日ありました。この辺はバグが顕著に出やすいですね。
42
+
43
+ ---
44
+ 計算の改善案
45
+
46
+ 150個分のデータを貯めておいて平均の計算をするわけですが、総和を求める際には別に計算の順序はどうでも良くて、直近150個分のデータをとにかく足せばいいわけです。150個の配列は、先頭から順に新しいデータが並んでいる必要はありません。なので、一個ずつデータにお詰め合わせを願って先頭に最新のデータを置く、なんてやらずに、
47
+ `int i=0;`
48
+ をグローバル変数に追加して
49
+ ```
50
+ sensorValue=0;
51
+ f[i]=analogRead(sensorPin);
52
+ i= (i+1) % n;//この計算によりiは0,1,..,149,0,1,...を繰り返すことになる
53
+ for(int j=0;j<n;j++) sensorValue+=f[j]; //変数はiのままでもいいんだけど、わざわざ紛らわしいことはしない
54
+ ```
55
+ で十分かと思います。