●Arduinoでエアソフトガンの弾速を計る

実際にBB弾でセンサが動作するかどうかを試しただけであり、測定値が正しいかどうかは不明です。
そもそも、計算方法が違っているかもしれないので信用しないでください。
エアソフトガンのBB弾の速度を計るためにはタマが通り過ぎると反応する二つのセンサを置いて、 その間をタマが通り過ぎる時間を測定すれば速度が計算できるはずです。

そこでこのような装置を作りタマの速度を計りたいと思います。


■速度を計る部分の作成

なにか良い材料が無いかと物色していたら、ちょうど水道管と思われる直径15ミリほどの塩ビのパイプがありましたので、それを使いタマの速度を計る部分を作成します。


まずは、適当な二か所に貫通する穴を開けます。


この穴の部分をタマが通過すると時間を計測するようにします。
あまり間隔が狭いと精度が落ちますし、斜めに穴を開けてしまうと誤差が大きくなると考えられます。


適当に開けた二つの穴の距離を計った所、134ミリでした。


次に、BB弾が通過した時に反応するセンサーを取り付けます。
KKHMF LM393 IR赤外線障害物回避センサモジュール Arduino用
BB弾センサーなんて物はあるわけでもないですから、赤外線障害物回避センサーを代用してBB弾を検出します。
このセンサーは片方が発光ダイオードでもう片方がフォトダイオードであり、 障害物がセンサの前に来ると発光ダイオードの光が反射して光センサであるフォトダイオードが受け取る事で反応する仕組みになっています。
このセンサーを常時反応しっぱなしにしておいて、BB弾が光をさえぎるとOFFになることにより検出したいと思います。


まずは、赤外線発光ダイオードを取り外します。


赤外線発光ダイオードの線をびょ〜んと伸ばしました。


フォトダイオードを下向きに向けて、外からの光が入らないように外周にビニールテープを巻き付けておきます。


先ほど作成したパイプの穴の上下に向かい合うように発光ダイオードとフォトダイオードを設置します。


ちなみに、発光ダイオードを取り付けている赤い部品は100均で売っている瞬間接着剤のキャップの先端を切断して使用しました。


二つのセンサーを取り付ける事によりBB弾のタマの速度を計る部分を作成しました。



■Arduinoとの接続

まずは、センサーのVCCにプラス極、GNDにマイナス極を接続します。
一度電気を流して動作させて、センサーの感度の調整を行います。
センサーを単独で動作させたら、センサについている可変抵抗を回して、反応するか反応しないかギリギリのONになる位置に調整しておきます。

センサーのOUT端子はArduinoの割り込みが出来るデジタルピン2番3番に接続します、 Arduinoで割り込みを使うで使用したピンを使用します。
とりあえず2番ピン側がBB弾が進入する方向で3番ピンが出ていく方向とします。
データを表示する液晶画面は、Arduinoで1602 LCDを使うと同じものを作成しました。

動作確認のためにBB弾をセンサー部分に自由落下させて速度を計った所



▼Arduinoにはこのようなプログラムを書き込みました。
センサーの距離は134mmとしていますが、適当な値に書き換えてください。
#include <LiquidCrystal.h>

LiquidCrystal lcd( 8, 9, 4, 5, 6, 7);

void setup() {
  lcd.begin( 16, 2 );

  lcd.clear();
  attachInterrupt(digitalPinToInterrupt(2), test, FALLING);
  attachInterrupt(digitalPinToInterrupt(3), test2, FALLING);
}

volatile unsigned long int time1, time2;
void loop() {

  if (sensor_checker(digitalRead(2), digitalRead(3))) {
    double pos = ((double)134 / (double)(time2 - time1)) * 1000;
    if (time1) {
      lcd.setCursor(0, 0);
      lcd.print(String(pos, 3) + " m/s           ");

      double j2, j25;
      j2 = (pos * pos * 0.2) / 2000;
      j25 = (pos * pos * 0.25) / 2000;

      lcd.setCursor(0, 1);
      lcd.print(String(j2, 3) + "J " + String(j25, 3) + "J         ");
    } else {
      lcd.setCursor(0, 0);
      lcd.print("Ready!!  m/s");
      lcd.setCursor(0, 1);
      lcd.print("0.2g/J   0.25g/J");
    }
  }
  delay(100);
}

//センサーの確認、D2・D3のどちらかがOFF状態の場合は画面に表示する
int sensor_checker(int i, int j) {
  static char str_off[17] = "OFF           ";
  static char str_on[17] = "ON            ";

  if (i || j) {
    char*row0 = str_off;
    char*row1 = str_off;
    if (i) row0 = str_on;
    if (j) row1 = str_on;

    lcd.setCursor(0, 0);
    lcd.print("D2 ");
    lcd.print(row0);

    lcd.setCursor(0, 1);
    lcd.print("D3 ");
    lcd.print(row1);
    return 0;
  }
  return 1;
}
void test() {
  time1 = micros();
}
void test2() {
  time2 = micros();
}


▼速度の計算方法

速さ = 距離 / 時間
時間 = 距離 / 速さ

速さと時間の公式で計算しています。
16MHz動作のArduinoではmicros()関数の値の分解能は4マイクロ秒であり、BB弾の程度の速度なら十分な精度でしょう。
詳しくはこちらへ、Arduinoで時間を測定する
念のため計算すると
134mmの距離を4μ秒で通過したとすると、134 / 4 * 1000 = 33500 m/s
つまり、秒速335メートルのタマを1%の誤差で計測できるという事です。


▼BB弾のジュールの求め方

(タマの重さ×速さ×速さ)/2000
で求めています、詳しくはよくわかりません。
0.2gと0.25gのBB弾が標準のようなので、その重さで計算しています。


■実際に測定してみた所

エアソフトガンからこのようにBB弾を発射して測定してみました。


正しいかどうかわかりませんがとりあえず測定できるようです。



■センサーを大きくする(LED側を増やすアプローチ)
この方法では調整が難しくて実用的ではありません。
この後で作成する(フォトダイオードを増やす)の方がよっぽどうまく動作します。

現在のセンサーではパイプが細く使いづらいという事で、パイプ径を太くしたいと思います。
基盤に30ミリの穴を開けて、3個のLEDと一つのフォトダイオードを取り付けました。
赤外線発光ダイオードが手元に無かったため、白色LEDで代用しました。
そのため、赤外線フォトダイオードを可視光フォトダイオードに入れ替えました。



しかし、このままではうまく動作しませんでした。
一つ目の問題として、LEDが明るすぎるとフォトダイオードの感度の上限に達してしまい暗いと反応しなくなり、うまく動作しません、 そのためLEDに1KΩほどの可変抵抗をかませて明るさを調整する必要がありました。
二つ目の問題として、3つのLEDのうち真ん中のLEDの光のみが強力にフォトダイオードに入るため、真ん中のLEDしかセンサーとして動作しません。
LEDを一つ一つ光をさえぎってみても、3つのLEDが同時に動作する事が無く、このままの状態で3個のLEDが同時にセンサーとして動作するには、 真ん中のLEDの明るさをシビアに調整する必要がありました。
そこで、色々試行錯誤した結果、可視光フォトダイオードの受光部分には光が透ける程度の紙(ティッシュペーパーなど)を張り付ける事により、
LEDの光が紙に当たって、紙が光り、その光をフォトダイオードで受け取る事により、3個のLEDが各々BB弾を捉えるセンサーとして動作するようになりました。


▼外部の光の影響

センサーが大きくなると外部の光の影響を受けます、そこで感度の自動調整を行うように改良しました。

センサーモジュールの簡略化した回路図

センサーモジュールの感度調整は可変抵抗を回す事により行います。
回路図を見ると、可変抵抗の抵抗分圧により任意の電圧を作り出し、それをオペアンプの基準電圧として感度を調整しているのがわかります。
そこで、オペアンプに流す基準電圧をArduinoから出力して調整できるようにします。
オペアンプについては、オペアンプを使ってみるに書いてあります。


可変抵抗を取り外して、コンデンサを取り付けました。


自動調整のテストために、1KΩの抵抗を取り付けてArduinoのデジタルピン3番に接続します。
センサーのOUT端子はとりあえずArduinoのデジタルピン5番に接続しました。

詳細は、PWMによる疑似アナログ出力 にて書いてあります。
今回作成したセンサーのテストを行うために作成したプログラム
void setup() {
  Serial.begin(9600);
  pinMode(5, INPUT);

  int first=0,middle,last=0xFF;
  check(last);//PWMの電圧を安定させるため空で呼び出す

  //バイナリサーチをイメージしてセンサーの最適な値を検出
  while(!((last-first)<=1)){
    middle=first+(last-first)/2;
    if(check(middle)){
          Serial.print("on ");
          Serial.println(middle,DEC);
          last=middle;
    }else{
          Serial.print("off ");
          Serial.println(middle,DEC);
          first=middle;
    }
  }
  
  //ONの値が2秒ほど安定するまで感度を上げる処理
  for(unsigned long t=millis()+2000;t > millis();){
    if(digitalRead(5)){
        check(++middle);
        Serial.println(middle,DEC);
        t=millis()+2000;
    }
  }

  Serial.print("completion ");
  Serial.println(middle,DEC);
}

int check(int i){
  analogWrite(3,i);
  delay(100);
  return !digitalRead(5);
}

void loop() {
}

周囲の明るさを拾ってもセンサーが自動調整されて動作することが確認できました。
しかし、外部の光、特に昼間の太陽光の影響は調整できるレベルを超えていてうまく動作しませんでした。
実際に作成するときにはパイプの中など、光が入りにくい状態で作成した方が良いです。


▼二つのセンサーを作成する

単独のセンサーでは動作する事がわかりましたので、センサーを二つ作り間隔を置いて配置します。
同一の物を二つ作成して針金とストローで止めました、センサーの間隔は適当です。
ちなみに真ん中の基盤は補強です。


センサーの配線をして動作確認

この時点で各々のセンサーが動く事を確認しておきます。


▼Arduinoとの配線

Arduinoには1602LCDを直接接続している以上、4番から9番のデジタルピンが埋まっています。
割り込みが出来るのは2番と3番のデジタルピンと決まっていますので、そこにOUT端子を接続します。
感度調整はピンが空いていてPWMが可能な10番、11番のデジタルピンで行うしかありません。

以上の事柄により、このように配線しました。


実際に配線した所、Arduinoには互換NANOを取り付けました。


裏側から見た所です、まあ試作品ですから。


後は、プログラムを書くと動作するようになると思います。
まだ、ハードウエアにバグがあるかもしれません。
新しいセンサーを取り付ければもっと完成度が上がると思います。


▼二つのセンサーの自動調整

前回作成したセンサーの自動調整を行うプログラムを二つのセンサーに適用します。
ただ単に、調整部分を関数にして二回回しているだけですがね。
高速に調整したい場合には並列で処理するようにした方が良いです。
void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  adjustment(2,10);
  adjustment(3,11);
}

void adjustment(int in_port,int pwm_port){
  int first=0,middle,last=0xFF;
  adjust_check(in_port,pwm_port,last);//PWMの電圧を安定させるため空で呼び出す

  //バイナリサーチをイメージしてセンサーの最適な値を検出
  while(!((last-first)<=1)){
    middle=first+(last-first)/2;
    if(adjust_check(in_port,pwm_port,middle)){
          Serial.print("on ");
          Serial.println(middle,DEC);
          last=middle;
    }else{
          Serial.print("off ");
          Serial.println(middle,DEC);
          first=middle;
    }
  }
  
  //ONの値が2秒ほど安定するまで感度を上げる処理
  for(unsigned long t=millis()+2000;t > millis();){
    if(digitalRead(in_port)){
        adjust_check(in_port,pwm_port,++middle);
        Serial.println(middle,DEC);
        t=millis()+2000;
    }
  }
  Serial.print(in_port,DEC);
  Serial.print(" completion ");
  Serial.println(middle,DEC);
}

int adjust_check(int in_port,int pwm_port,int pwm){
  analogWrite(pwm_port,pwm);
  delay(100);
  return !digitalRead(in_port);
}

void loop() {
}

Arduinoに書き込むと二つのセンサーの感度が自動調整されるのが確認できました。


▼弾速を測定できるようにする

前に作成したセンサーが小さいタイプのプログラムと自動調整プログラムを合体して、タマのスピードを測定できるようにしました。
今回作成したセンサー間の距離は170ミリでしたのでプログラムの定数(赤文字部分)を変更しました。

#include <LiquidCrystal.h>

LiquidCrystal lcd( 8, 9, 4, 5, 6, 7);

volatile unsigned long int time1,time2;

#define LENGTH_BETWEEN 170  //センサー間の距離(ミリ)

void setup() {
  lcd.begin( 16, 2 );
  lcd.clear();
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  attachInterrupt(digitalPinToInterrupt(2),test,FALLING);
  attachInterrupt(digitalPinToInterrupt(3),test2,FALLING);
  print("Sensor adjust 1",0);
  adjustment(2,10);
  print("Sensor adjust 2",0);
  adjustment(3,11);
  delay(500);
  time1=0;
  time2=0;
}

void adjustment(int in_port,int pwm_port){
  int first=0,middle,last=0xFF;
  adjust_check(in_port,pwm_port,last);//PWMの電圧を安定させるため空で呼び出す

  //バイナリサーチをイメージしてセンサーの最適な値を検出
  while(!((last-first)<=1)){
    middle=first+(last-first)/2;
    if(adjust_check(in_port,pwm_port,middle)){
          print(String(middle),1);
          last=middle;
    }else{
          print(String(middle),1);
          first=middle;
    }
  }
  
  //ONの値が2秒ほど安定するまで感度を上げる処理
  for(unsigned long t=millis()+2000;t > millis();){
    if(digitalRead(in_port)){
        adjust_check(in_port,pwm_port,++middle);
        print(String(middle),1);
        t=millis()+2000;
    }
  }
  adjust_check(in_port,pwm_port,middle+5);//センサーの閾値がシビアになりすぎるため少々感度を上げる
}

int adjust_check(int in_port,int pwm_port,int pwm){
  analogWrite(pwm_port,pwm);
  delay(100);
  return !digitalRead(in_port);
}

void loop() {
    double pos=((double)LENGTH_BETWEEN/(double)(time2-time1))*1000;
    if(time1 && time2){
      print(String(pos,3)+" m/s           ",0);

      double j2,j25;
      j2=(pos*pos*0.2)/2000;
      j25=(pos*pos*0.25)/2000;
      
      print(String(j2,3)+"J "+String(j25,3)+"J         ",1);
    }else{
      print("Ready!!  m/s      ",0);
      print("0.2g/J   0.25g/J",1);
    }
    delay(100);
}

void print(char*str,int c){
      lcd.setCursor(0, c);
      lcd.print(str);
}

void print(String str,int c){
      lcd.setCursor(0, c);
      lcd.print(str);
}

void test(){
  time1 = micros();
}
void test2(){
  time2 = micros();
}

実際に作成して起動したところ。

センサー1番から2番にかけてBB弾を落とすとそれらしい速度が表示されました。
たぶんもっと速い速度でも動作するでしょう。
しかし、外部の光の影響が大きく手で持って動かしたり、明かりの前の影が動くと勝手に測定開始して液晶に変な値が表示されます。
パイプの中に作るなど、外部の光の入らない工夫が必要です。
実際に作成した所、LED側を増やすアプローチでは個々のLEDの調整が難しくうまく動作させることが困難でした。
そこで、次は別の方法で挑戦してみます。


■センサーを大きくする(フォトダイオードを増やすアプローチ)

フォトダイオードを直列に接続し光を当てると普通に光センサーとして動作しますが、その場合には一つのダイオードが暗くなるだけで全体の抵抗値が変わり、タマの影を検出できると思います。
つまり、センサーの検出範囲を広く取る事ができるようになると思われます。
そこで、赤外線障害物回避センサーの回路をこのように改造しました。

赤い四角で囲んだ部分のフォトダイオードを3個直列に接続しています。 元々モジュールに付いていた赤外線フォトダイオードではなくて頭が平面になっている可視光フォトダイオードを取り付けました。

センサー部分を作成した所、木の板に可視光フォトダイオードが3個直列に埋め込まれています。


フォトダイオードの反対側には白色LEDを一つ取り付けました。
自然光でも良いのですが明るさが不安定過ぎて調整が面倒ですし、蛍光灯は目に見えない速さの点滅が起きているため誤動作します。


白色LEDの光のスポットをフォトダイオード3個に直接あたるようにセットします。
これで、LEDの光が少しでも遮られると検出できるようになります。
この後、赤外線障害物回避センサーの可変抵抗を調整してOFFとONのギリギリのONの所に調整するとBB弾をうまく拾えます。

実際に作成してみました。

横から見た写真


BB弾を手で落としてみると、それらしい値が表示されました。
感度や操作性は今までのより良好です。


■動作確認してみました

周囲に囲いが無いため、センサーの感度が最大になってしまう直射日光や目に見えないチラつきがある蛍光灯の下を避けて薄暗い状態にする必要があります。
弾速計の置き場所を決めたら周囲の明るさに合わせてセンサーのボリュームを回して感度を最大になるように調整しました。

エアーガンで試射した所、いままで作成した弾速計はセンサーの範囲が狭くBB弾を捉える事自体が大変でしたが、センサーを直列に接続しただけの事はあり、検出範囲が広くなり調子よく測定できます。


回路やプログラムは一番最初に作成した弾速計とほぼ一緒のため前回と近い値がでました。

2020年 3月 1日 更新
2020年 8月23日 更新

▲トップページ > マイコンなど