🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

1回答

1985閲覧

温度センサーをAruduinoを用いた時の波のピークを検出したい

mitatoshi

総合スコア12

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2019/12/31 09:25

行いたいこと

温度センサーを用いたデータをArduinoを用いてデータを処理するのですが、下記のコードで平滑化っぽいことを行いシリアルプロッタで確認したところ処理前のデータよりはきれいなグラフになりました。そこで波のピーク(温度が一番上昇した時)を検出して出力したいです。

#define n 150 int sensorPin = A1; //アナログ1番ピンを指定 float sensorValue = 0; int f[n]={0}; void setup() { Serial.begin(9600); //シリアルモニタに表示するための設定 Serial.println("start"); } void loop() { sensorValue = analogRead(sensorPin); for(int i=n-1;i>0;i--)f[i]=f[i-1]; f[0]=analogRead(1); for(int i=0;i<n;i++) sensorValue+=f[i]; sensorValue =(float)sensorValue/n; Serial.print(sensorValue); Serial.print("\t"); Serial.println(""); delay(50); }

どのようなコードを付け足せばピークを検出できるのか教えて頂きたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

平滑化っぽいことを行い

ちょっとまちがってるけどね。

最大値を保持する変数を設けて、
最初はありえないほど小さな値にしておくか、それとも一番最初に取得舌データを入れておいて、
新しいデータが得られる度にその最大値を保持する変数と新しいデータとを比べて、
新しいデータの方が大きい値なら最大値を新しいデータで書き換えれば、
いいんじゃない?


平滑化(移動平均)処理の誤りについて

平均ってどうやるか、これは言うまでもなく、要素全部の和/要素の数を計算すればいいわけです。
ここで、1023が150個あるはずの平均の計算が1023*151/150の値になったとすれば、「要素全部の和」が正しく求められているかをチェックする、というのが流れでしょう。
その計算は、
for(int i=0;i<n;i++) sensorValue+=f[i];
でやっているわけですがしかし、このループに入る前にsensorValueには
sensorValue = analogRead(sensorPin);
という値が与えられています。これによって1023が151個分足される、ということになるわけです。ここを修正する必要があります。


analogRead()は「実行したまさにそのときの値」

多分大問題にはならないでしょうけど...analogRead()は、そのときそのときのADコンバータの値を返してきます。電圧が変化していたなら2度連続で読んで、おなじ値になる保証はありません。

sensorValue = analogRead(sensorPin);//ここと for(int i=n-1;i>0;i--)f[i]=f[i-1]; f[0]=analogRead(1);//ここ

温度のデータならこの短時間で大きく変化はしないだろう、というのは言えますけれど、一般論としてはよろしい手法ではないです。

int tmpValue= analogRead(sensorPin); //取得は一回だけ sensorValue=tmpValue; for(int i=n-1;i>0;i--)f[i]=f[i-1]; f[0]=tmpValue; for(int i=1;i<n;i++) sensorValue+=f[i];

などとして、取得は一度だけにしておいた方がよいでしょう。
millis()による時間取得で似たようなことをやって、微妙なバグを生んでしまう例をみたことがあります。random()を何度も呼んでしまって動きが目茶苦茶になって...という質問も(ここではありませんが)先日ありました。この辺はバグが顕著に出やすいですね。


計算の改善案

150個分のデータを貯めておいて平均の計算をするわけですが、総和を求める際には別に計算の順序はどうでも良くて、直近150個分のデータをとにかく足せばいいわけです。150個の配列は、先頭から順に新しいデータが並んでいる必要はありません。なので、一個ずつデータにお詰め合わせを願って先頭に最新のデータを置く、なんてやらずに、
int i=0;
をグローバル変数に追加して

sensorValue=0; f[i]=analogRead(sensorPin); i= (i+1) % n;//この計算によりiは0,1,..,149,0,1,...を繰り返すことになる for(int j=0;j<n;j++) sensorValue+=f[j]; //変数はiのままでもいいんだけど、わざわざ紛らわしいことはしない

で十分かと思います。

投稿2019/12/31 09:48

編集2020/01/02 02:51
thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mitatoshi

2019/12/31 11:00

回答ありがとうございます。正しい平滑化のコードを教えて頂きたいです。
thkana

2019/12/31 13:15

まぁその前に。間違っていることを確認しましょう。 センサーを外して、A1を直接5Vに接続した状態でどういうデータが出てくるかしら。 1029.82とかその辺が出てきませんか。ADコンは0~1023の値を出すので、 平均がそれより大きいってのは明らかにおかしいですよね。 この値は、1023*151/150、だったりするわけですが。この辺からなにか気が付きませんか。 続きは明日、ね。
mitatoshi

2020/01/01 12:30

なるほど。私がかいた平滑化のコードが間違っているのはわかりました。どのようにしたら正しく平滑化をおこなえますか?
mitatoshi

2020/01/06 16:09

最終的なコード #define n 100 int sensorPin = A1; //アナログ1番ピンを指定 float sensorValue = 0; int f[n]={0}; int i = 0; void setup() { Serial.begin(9600); //シリアルモニタに表示するための設定 Serial.println("start"); } void loop() { int tmpValue = analogRead(sensorPin); sensorValue = tmpValue; for(int i=n-1;i>0;i--)f[i]=f[i-1]; f[0]=tmpValue; for(int j=0;j<n;j++) sensorValue+=f[j]; sensorValue =(float)sensorValue/n; Serial.print(sensorValue); Serial.print("\t"); Serial.println(""); delay(50); }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問