プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

Arduinoで振り子の周期を測定する

こちらの記事に質問をいただきました。
s51517765.hatenadiary.jp
f:id:s51517765:20200215171113p:plain

コメントで方針はお答えしましたが、実際にやってみました。
f:id:s51517765:20200215171203j:plain
ただし、振り子のハードウェアがないので、フォトセンサ(CDS)を指で隠すという形でフィージビリティ的に実施しました。
実際の振り子の周期としては、真ん中で観測すれば2回遮られるごとに周期になります。
左右のどちらかであれば遮られるごとに周期になります。
今回は左右のどちらか、に相当する方法で実装しました。

実装の考え方

フォトセンサ(CDS)は今回使用したものは、光が遮られる(振り子が通過すると)と抵抗が高くなり出力がLOWになり、光が戻ると抵抗が小さくなり出力はHIGHになります。
ただし、回路の組み方によってはHIGH/LOWは反対にもなります。
f:id:s51517765:20200215172432p:plain

CDSは明るさによって抵抗が変わります。5Vを基準抵抗(ここでは10kΩ)と分圧することで明るさを検知します。基準抵抗はCDSによって、感度が一番よくなる抵抗を選びます。
明るいときと暗いときでのAnalog出力の差がなるべく大きくなる抵抗値を選びます。
下のようなコードを書いて、CDSの出力感度を確認しました。

void setup() {
  // put your setup code here, to run once:
  pinMode(A3, INPUT);
  Serial.begin(9600);
}

void loop() {
  int read;
  read = analogRead(A3);
  Serial.println(read);
  delay(300);
}
//結果  10kΩ
306
306
301
473
733
823
831
304
299
564
822
821
826 //遮られている
826
600
298 //遮られていない
297
297
291
304

f:id:s51517765:20200215175413j:plain

実装のポイント

では実際に時間を図るコードです。
光が遮られていない状態から、光が遮られている状態に変化した時刻を記録します。時刻を記録する配列をunsigned long time_[10];で10個分で用意します。
LOW→HIGHの状態を10回記録します。

  while (true) {
    read = digitalRead(A3);
    if (read == 1 && readPre == 0) { //1つ前で遮られていなく、今回遮られている
      time_[count] = micros();
      count++;
    }
    //  Serial.println(String(count) + " " + String(readPre) + " " + String(read));
    readPre = read; //今の状態を前の状態として保持します
    delay(1);
    if (count == 10) break; //10回で終了
  }

次に、結果出力部です。
隣り合った時刻の差分をとれば、一周期です。
さらに、この平均をとります。平均はave=0にに対してして、平均したいものをave += period;のように加算し、最後にave /= 9;のように回数で割ります。
ついでにばらつき(最大ー最小)/2も算出してみました。
最大・最小は、Arduinoの関数Max/Minで求めてもよいです。
測定はMicro Secondですが、結果出力は1000倍してMilli Secondに丸めています。

  Serial.println("Calculation!");
  Serial.println("Raw result.");
  unsigned long max = 0;
  unsigned long min = 32767;
  unsigned long period;
  unsigned long  ave = 0;

  for (int i = 1; i < 10; i++) { //2つ目 - 1つ目が最初
    //  Serial.println(time_[i]);
    period = (time_[i] - time_[i - 1]) / 1000;
    Serial.println(period) ;
    ave += period;

    if (period < min)min = period;
    if (period > max)max = period;
  }
  ave /= 9;
  //結果
  Serial.println("Ave= " + String(ave));
  Serial.println("delta= " + String((max - min) / 2));
//結果出力
Waiting Start!
Calculation!
Raw result.
219
315
226
239
241
241
225
210
209
Ave= 236
delta= 53

まとめ

久しぶりにArduinoで遊ぶ、いいお題になりました。github.com