回答編集履歴

1

平滑化の修正追記

2020/01/02 02:51

投稿

thkana
thkana

スコア7645

test CHANGED
@@ -15,3 +15,95 @@
15
15
  新しいデータの方が大きい値なら最大値を新しいデータで書き換えれば、
16
16
 
17
17
  いいんじゃない?
18
+
19
+
20
+
21
+ ---
22
+
23
+ 平滑化(移動平均)処理の誤りについて
24
+
25
+
26
+
27
+ 平均ってどうやるか、これは言うまでもなく、要素全部の和/要素の数を計算すればいいわけです。
28
+
29
+ ここで、1023が150個あるはずの平均の計算が1023*151/150の値になったとすれば、「要素全部の和」が正しく求められているかをチェックする、というのが流れでしょう。
30
+
31
+ その計算は、
32
+
33
+ ` for(int i=0;i<n;i++) sensorValue+=f[i];`
34
+
35
+ でやっているわけですがしかし、このループに入る前にsensorValueには
36
+
37
+ `sensorValue = analogRead(sensorPin);`
38
+
39
+ という値が与えられています。これによって1023が151個分足される、ということになるわけです。ここを修正する必要があります。
40
+
41
+
42
+
43
+ ---
44
+
45
+ analogRead()は「実行したまさにそのときの値」
46
+
47
+
48
+
49
+ 多分大問題にはならないでしょうけど...analogRead()は、そのときそのときのADコンバータの値を返してきます。電圧が変化していたなら2度連続で読んで、おなじ値になる保証はありません。
50
+
51
+ ```
52
+
53
+ sensorValue = analogRead(sensorPin);//ここと
54
+
55
+ for(int i=n-1;i>0;i--)f[i]=f[i-1];
56
+
57
+ f[0]=analogRead(1);//ここ
58
+
59
+ ```
60
+
61
+ 温度のデータならこの短時間で大きく変化はしないだろう、というのは言えますけれど、一般論としてはよろしい手法ではないです。
62
+
63
+ ```
64
+
65
+ int tmpValue= analogRead(sensorPin); //取得は一回だけ
66
+
67
+ sensorValue=tmpValue;
68
+
69
+ for(int i=n-1;i>0;i--)f[i]=f[i-1];
70
+
71
+ f[0]=tmpValue;
72
+
73
+ for(int i=1;i<n;i++) sensorValue+=f[i];
74
+
75
+
76
+
77
+ ```
78
+
79
+ などとして、取得は一度だけにしておいた方がよいでしょう。
80
+
81
+ millis()による時間取得で似たようなことをやって、微妙なバグを生んでしまう例をみたことがあります。random()を何度も呼んでしまって動きが目茶苦茶になって...という質問も(ここではありませんが)先日ありました。この辺はバグが顕著に出やすいですね。
82
+
83
+
84
+
85
+ ---
86
+
87
+ 計算の改善案
88
+
89
+
90
+
91
+ 150個分のデータを貯めておいて平均の計算をするわけですが、総和を求める際には別に計算の順序はどうでも良くて、直近150個分のデータをとにかく足せばいいわけです。150個の配列は、先頭から順に新しいデータが並んでいる必要はありません。なので、一個ずつデータにお詰め合わせを願って先頭に最新のデータを置く、なんてやらずに、
92
+
93
+ `int i=0;`
94
+
95
+ をグローバル変数に追加して
96
+
97
+ ```
98
+
99
+ sensorValue=0;
100
+
101
+ f[i]=analogRead(sensorPin);
102
+
103
+ i= (i+1) % n;//この計算によりiは0,1,..,149,0,1,...を繰り返すことになる
104
+
105
+ for(int j=0;j<n;j++) sensorValue+=f[j]; //変数はiのままでもいいんだけど、わざわざ紛らわしいことはしない
106
+
107
+ ```
108
+
109
+ で十分かと思います。