皆さんご存じのように、速度とはある物体の単位時間当たりの変位を表します。自動車のスピードメーターを読むことにより運転者は自分の車の現在の速度を知ることが出来たり、TV放送では陸上選手の走行のタイムや、ピッチャーの投げたボールの速度を知ることが出来たりします。ここでは最も簡単に入手できるワンボードマイコンの1つであるArduinoを使ってこれらの測定方法に触れてもらいたいと思います。
始めに速度を測定するセンサーやLEDを用いてArduinoの最も簡単な動作確認を行います。
準備するスマホやガラゲーについては、新しいものはフィルターによりIR(赤外線)が見えないものが多いため、古めのものを準備してください。IRが見えるものならば図1-1の様にインカメラ(液晶画面側についているカメラ)を使ってやや紫がかった光が見えます。またリモコンは人のいない向こう側に向けて光が出るように注意して、インカメラを使って確認してください。この機材確認をして赤外線の見えるスマホを確保しておくと今後の実験が楽になります。今後の実験についてもIR (赤外線)LEDについては人に向けない様にしてください。
まず最初に準備した部品のIR LEDとIR PT(フォトトランジスタ)を固定するホルダーを作成します。IR LEDとIR PTの極性について図1-2を見て円筒ケース縁の切り欠き方向に注意してGNDになる方(黒い結線となる方)を後から分かるように黒マジックなどで印をつけてください。さらに図1-3のようにプラダンボールでU字形に作成して穴パンチで穴を空けたホルダーに、透明なIR LEDの円筒方向と黒色のIR PT(フォトトランジスタ)の円筒方向が向い合せになるようにホットボンドで固定してください。ここでは向い合せに取り付けるIR LEDとIR TPは50㎜程度離して固定してください。完全に2つの部品が向い合せに固定出来るまで配線してはいけません。(代わりに黄色の部品の様に3Dプリンターで作成しても構いません)
図1-3 IR LED & IR PTの固定
この実験1-1は、IR LEDから発した赤外線をIR PTで常時受光する状態に対してその光を遮断検知すると基板上にあるLEDが点灯するというIR(赤外線)部分の配線と部品の基本動作を確認します。図1-1の配線図に基づいてブレッドボードを用いて配線してみてください。必ず先のU字ホルダーにIR LEDとIR PTをきちんと固定した後でブレッドボードへの配線をしてください。
また実物での配線状態を図1-5に示します。
次にArduinoの設定です。Arduinoの開発環境Arduin IDEは以下のサイトで無償入手できます。
今回はArduino IDE(バージョンは2.2.1)をインストール後にArduino UnoとUSBケーブルでパソコンを接続したら、Arduinoアプリの画面でシリアルポートを設定します。「Tools」バーから「Serial Ports」をクリックして、自分の利用しているArduino(今回は「Arduino Uno R4 Minima」)となっているものを選びます。
void setup() {
pinMode( 12, OUTPUT );
pinMode( 13, OUTPUT );
}
void loop() {
digitalWrite( 12, HIGH );
digitalWrite( 13, HIGH );
delay(100);
digitalWrite( 12, LOW );
digitalWrite( 13, LOW );
delay(300);
}
リスト1-1では、最初にIR(赤外線)LEDから光が出ているかを確認します。Serup{}内で12番ピンにIR(赤外線)LEDと13番ピンのArduino基板上に設置されている小さなLEDの接続を定義します。次に動作を繰り返すloop{}内で100マイクロ秒[ms]点灯した後300マイクロ秒消灯して点滅する定義をしています。
正常に動作している場合は基盤上のArduino本体の12番ピン近くにあるLEDが点滅して、また接続しているIR LEDが同じように点滅しているはずですが、IR LEDは肉眼で見えないので先の予備実験で確保した赤外線の見えるスマホのインカメラで間接的に見てください。以下の動画の様に点滅して見えると思います。やや紫がかった色で点滅していることがわかります。
実験1-1でIR LEDの結線は出来て赤外線で発光していることがわかったので、次は受光部のIR PTの動作確認をします。
// IR LEDとIRセンサーのセットの割込での動作確認
volatile int TOGGLE = LOW; //割込処理時に利用するLED点灯変数TOGGLE(ON/OFF値)を設定
void setup() { //一回だけ実行
pinMode(12, OUTPUT); //IR LEDが接続された12番ピンを出力に設定
pinMode(13, OUTPUT); // LEDが接続された13番ピンを出力に設定(基盤LED上)
pinMode(2, INPUT_PULLUP); //外部割込みを監視する2番ピンをプルアップし入力に設定
attachInterrupt(digitalPinToInterrupt(2), blink, CHANGE);
//2番ピン(割込番号0)の電圧に変化があったらblink関数を実行
}
void loop() { //{}内を無限ループで実行
digitalWrite(12, HIGH); //IR LEDは点灯し続ける
digitalWrite(13, TOGGLE); //LEDが接続された13番ピンとLED点灯変数を関連付ける
}
void blink(void) { //割り込み番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(2) == LOW) { //もし2番ピンがLOWなら{}内を実行する
TOGGLE = LOW; //LED点灯変数をLOW(消灯)にする
}
else { //もし2番ピンがLOWでなかったら(HIGHなら)以下の{}内を実行する
TOGGLE = HIGH; //LED点灯変数をHIGH(点灯)にする
}
}
リスト1-2プログラムを説明します。初めに「volatile int」 で割込処理中でも利用できる関数(今回はTOGGLE)を定義します。次にSerup内で12番ピンにIR(赤外線)LEDと2番ピンにIRの受光センサーとしてPT(フォトトランジスタ)を設定、またこの2つの部品の間の赤外線の発光と受光を遮断した場合に13番ピンのArduino基板上に設置されている小さなLEDが点灯するようにプログラムしています。
IR LEDとIR PTの間に見えない光が通っているので、その間を遮断するものを置きます。置いたと同時に基盤上のLEDが点灯すれば、配線は問題なく出来ています。動作した場合は図1-7の様になります。
これから先の実験もこの光路の遮断検知が基本動作となります。次の実験でプロペラの回転数を計測する場合もプロペラが光路を遮断している時にランプが点灯しているかこのリスト1-2を実行して確認してからにしましょう。もしうまく点灯しない場合はブレッドボード図と比較して間違いがないか、Arduinoプログラムをコンパイル時にエラーが出ていないかなどを再度確認してください。
※全てのトラブルに対してここで記すことは出来ないのでネットで調べる、学校の理科の先生に相談する等工夫して自分で解決する方法を模索してみてください。
実験1-1と実験1-2で問題無ければ、IR LEDとIR PTのブレッドボード図と同じ配線が出来ているので、次はArduinoの内臓のタイマー(簡易時計)と合わせて利用してみましょう。物体がLEDとPTの赤外線の通路を遮断した回数を記憶して、約0.5秒間に遮断された回数から計算して1分当たりに回転する数を回転数(rpm: revolutions per minute)として表示します。ここではドローンで利用されているブラシレスモーターのプロペラの回転数を計測してみます。
/* <Simple tachometer> IR LED pin12 => IR PT(Sensor) pin2
Programmed for basic studies in 2015 For TV broadcast
Waveform confirmed with simple oscilloscope
*/
int ledPin = 12; //IR LED digital pin 12
volatile byte rpmcount;
unsigned int now,dt; //from 0 to 65535
unsigned long timestr,timeold; ////from 0 to 4,294,967,295
unsigned int wingin =2; //Number of spokes or propeller blades : 6
float rpm;
void rpm_fun() //Counting the number of passes
{ //with interrupt processing
rpmcount++;
}
void setup()
{
Serial.begin(9600) ;
pinMode(2, INPUT_PULLUP); //Pulled up Pin2
attachInterrupt(digitalPinToInterrupt(2), rpm_fun, FALLING); //Interrupt
pinMode(ledPin, OUTPUT); //IR LED Mode
digitalWrite(ledPin, HIGH); //IR LED Lighting
rpmcount = 0; //Counter reset
rpm = 0; //rpm reset
timestr = millis(); //Arduino startup time[ms]
timeold = 0; //time record reset
}
void loop()
{
delay(500); //Update RPM every 500mmsec
detachInterrupt(0); //Stop Interrupt
now = millis()-timestr; //Time from Arduino startup
dt = millis()-timeold; //From the previous record time
rpm = 60*1000*rpmcount/dt ; //Multiply by the number of rotations
rpm = rpm/wingin; //Divide by the number of shields
Serial.print("time=");
Serial.print(now); //Serial display of time[ms]
Serial.print("[ms] rpm=");
Serial.println(rpm); //Serial display of RPM
timeold = millis(); //Reset time records
rpmcount = 0; //Initialize rotation record
attachInterrupt(0, rpm_fun, FALLING); // restart Interrupt
}
図1-8の画像を見てもらうと画面の右上に回転しているプロペラが見えます。また画面左側の下部にシリアルプリントとして計測した回転数[rpm]の結果が出ています。物体がLEDとPTの赤外線の通路を遮断した際にセンサーからの電圧が切り替わって光路遮断を認識しているかどうかミニオシロスコープで確認しています。プロペラ2枚羽のため1回転で2回遮断して比較的細いので電圧が上昇して反応している時間の割合は短いです。厳密な矩形波ではないですが規則性がありFFT(フーリエ変換)により繰り返しの周波数(Hz)を得る事が出来ます。オシロスコープの周波数(Fre~Hz)が1分間に2回通過よりx60÷2としてArduinoの表示結果とほぼ一致しているのがわかると思います。
この実験と同じような仕組みで回転数を測れる部品として例えばフォトインタラプターがあります。興味がある人は是非調べてみてください。
実験1-1から実験1-3まで問題無ければ、IR LEDとIR PTのブレッドボード図と同じ配線が出来ているので、次はArduinoの内臓のタイマー(簡易時計)と合わせて利用してみましょう。物体がLEDとPTの赤外線の通路を遮断した時間を記憶して、前の遮断された時間との差を表示します。例えば巡回コースを回る玩具の自動車の周回ラップタイムの計測に利用出来ます。また周回の距離をラップタイムで割れば平均速度となります。
// ミリ秒単位で記録して算出(光源1を遮断する物体の前回遮断との経過時間を記録)
// IR LED&IR PT 1セットの割込処理での動作
unsigned long timeold,timenow1,timerec1,dt;//整数64bit -9223372036854775808
void setup() {
Serial.begin( 9600 ); // シリアル通信を初期化。通信速度は9600bps
timeold = millis(); // Arduino起動した時間を記録(ミリ秒単位)
timenow1 = 0; //タイマー1の初期化
pinMode(12, OUTPUT); //IR LED1が接続された12番ピンを出力に設定
pinMode(2, INPUT_PULLUP); //外部割込みを監視する2番ピンをプルアップし入力0に設定
attachInterrupt(digitalPinToInterrupt(2), blink1, CHANGE);
//2番ピンの電圧変化からblink1を実行
digitalWrite(12, HIGH); //IR LED1は点灯し続ける
}
void loop() { //{}内を無限ループで実行
delay(1000); //一秒おきにloop内実行 その間は割込待ち
dt=timenow1-timerec1;
if(dt > 0) {
Serial.print(" t1=");
Serial.print(timenow1); //タイマー1の記録時間(ミリ秒単位)をシリアルに書き出し
Serial.print("Time Record=");
Serial.println(dt); //前回のタイマー1の記録時間との差をシリアルに書き出し
}
timerec1=timenow1;
}
void blink1(void) { //割込番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(2) == LOW) { //もし2番ピンがLOWなら{}内を実行する
timenow1 = millis()-timeold; //タイマーに今の時間を記録(Arduino起動時からの時間)
}
}
Arduinoの内臓のタイマー(簡易時計)とIR LEDとIR PTの応用の実験1-3まで体験できた人は更に応用を考えてみてください。物体がLEDとPTの赤外線の通路を遮断した時間を記憶して、前の遮断された時間との差を表示します。例えば図1-9の動画で確認できるように巡回コースを回る玩具の自動車の周回ラップタイムの計測に利用出来ます。また周回の距離をラップタイムで割れば平均速度となります。自分で改造してみてください。
実験1ではIR LEDとIR PTの間に見えない光が通っており、その光の遮断の判別から色々な応用が出来ました、実験2ではこのIR LEDとIR PTを2セットに増やして通過時間を測定出来るようにします。
// ボールの転がりの速度 ミリ秒単位で記録して算出(光源1と2を遮断する物体の通過時間を記録)
// IR PT2セットの割込処理での動作
unsigned long timeold,timenow1,timenow2;//整数64bit -9223372036854775808
double velo;
void setup() { //一回だけ実行
Serial.begin( 9600 ); // シリアル通信を初期化。通信速度は9600bps
Serial.println( "Hello Arduino!" ); // 最初に1回だけメッセージを表示する
timeold = millis(); // Arduino起動した時間を記録(ミリ秒単位)
timenow1 = 0; //タイマー1の初期化
timenow2 = 0; //タイマー2の初期化
pinMode(12, OUTPUT); //IR LED1が接続された12番ピンを出力に設定
pinMode(11, OUTPUT); //IR LED2が接続された11番ピンを出力に設定
pinMode(2, INPUT_PULLUP); //外部割込みを監視する2番ピンをプルアップし入力0に設定
pinMode(3, INPUT_PULLUP); //外部割込みを監視する3番ピンをプルアップし入力1に設定
attachInterrupt(digitalPinToInterrupt(2), blink1, CHANGE); //2番ピンの電圧変化からblink1を実行
attachInterrupt(digitalPinToInterrupt(3), blink2, CHANGE); //3番ピンの電圧変化からblink1を実行
digitalWrite(12, HIGH); //IR LED1は点灯し続ける
digitalWrite(11, HIGH); //IR LED2は点灯し続ける
}
void loop() { //{}内を無限ループで実行
delay(1000); //一秒おきにloop内実行 その間は割込待ち
Serial.print(" t1=");
Serial.print(timenow1); //タイマー1の記録時間(ミリ秒単位)をシリアルに書き出し
Serial.print(" t2=");
Serial.print(timenow2); //タイマー2の記録時間(ミリ秒単位)をシリアルに書き出し
Serial.print(" v=");
// velo = 0.1/((float(timenow2-timenow1))/1000.) // 0.1[m]を経過[秒s]で速度算出(下記は同じ内容)
velo = 100./float(timenow2-timenow1); //タイマー1とタイマー2の時間と0.1[m]の距離から速度を算出
Serial.println(String(velo,3)); //速度をシリアルに書き出し 小数点3桁の精度
}
void blink1(void) { //割り込み番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(2) == LOW) { //もし2番ピンがLOWなら{}内を実行する
timenow1 = millis()-timeold ; //タイマーに今の時間を記録(Arduino起動時からの時間)
}
}
void blink2(void) { //割り込み番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(3) == LOW) { //もし2番ピンがLOWなら{}内を実行する
timenow2 = millis()-timeold ;
}
}
Arduinoの内臓のタイマー(簡易時計)とIR LEDとIR PTの応用の実験1-3まで体験できた人は更に応用を考えてみてください。
実験2のIRLEDとIRPTを2セットに加えて、結果をLCD液晶に表示出来るようにします。図3-1がLCD 1602Aをブレッドボードで接続した状態です。このLCD液晶を既に組付けて端子のはめ込みだけで利用出来るシールド(LCD Keypad シールド等)という製品が市販されているのでそちらを利用してもよいです。プログラムのリスト3はピン位置を同じにしてあるのでそのまま利用出来ます。
Arduino Uno 1602A
増設した液晶(LCD 1602A)のピン接続を以下に示します。
// ボールの転がりの速度 ミリ秒単位で記録して算出(光源1と2を遮断する物体の通過時間を記録)
// IR PT2セットの割込処理での動作 LCD 1602A液晶表示追加
unsigned long timeold,timenow1,timenow2;//整数64bit -9223372036854775808
double velo;
#include
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() { //一回だけ実行
Serial.begin( 9600 ); // シリアル通信を初期化。通信速度は9600bps
lcd.begin( 16, 2 );
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Hello, world!");
Serial.println( "Hello Arduino!" ); // 最初に1回だけメッセージを表示する
timeold = millis(); // Arduino起動した時間を記録(ミリ秒単位)
timenow1 = 0; //タイマー1の初期化
timenow2 = 0; //タイマー2の初期化
pinMode(12, OUTPUT); //IR LED1が接続された12番ピンを出力に設定
pinMode(11, OUTPUT); //IR LED2が接続された11番ピンを出力に設定
pinMode(2, INPUT_PULLUP); //外部割込みを監視する2番ピンをプルアップし入力0に設定
pinMode(3, INPUT_PULLUP); //外部割込みを監視する3番ピンをプルアップし入力1に設定
attachInterrupt(digitalPinToInterrupt(2), blink1, CHANGE); //2番ピンの電圧変化からblink1を実行
attachInterrupt(digitalPinToInterrupt(3), blink2, CHANGE); //3番ピンの電圧変化からblink1を実行
digitalWrite(12, HIGH); //IR LED1は点灯し続ける
digitalWrite(11, HIGH); //IR LED2は点灯し続ける
}
void loop() { //{}内を無限ループで実行
delay(1000); //一秒おきにloop内実行 その間は割込待ち
Serial.print(" t1=");
Serial.print(timenow1); //タイマー1の記録時間(ミリ秒単位)をシリアルに書き出し
Serial.print(" t2=");
Serial.print(timenow2); //タイマー2の記録時間(ミリ秒単位)をシリアルに書き出し
Serial.print(" v=");
//Print out result to lcd
lcd.clear();
lcd.setCursor(0,0);
lcd.print(timenow2);
lcd.print("-");
lcd.print(timenow1);
lcd.setCursor(0,1);
lcd.print("v=");
// velo = 0.1/((float(timenow2-timenow1))/1000.) // 0.1[m]を経過[秒s]で速度算出(下記は同じ内容)
velo = 100./float(timenow2-timenow1); //タイマー1とタイマー2の時間と0.1[m]の距離から速度を算出
Serial.print(String(velo,3)); //速度をシリアルに書き出し 小数点3桁の精度
Serial.println("[m/s]");
lcd.print(String(velo,3));
lcd.print("[m/s]");
}
void blink1(void) { //割り込み番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(2) == LOW) { //もし2番ピンがLOWなら{}内を実行する
timenow1 = millis()-timeold ; //タイマーに今の時間を記録(Arduino起動時からの時間)
}
}
void blink2(void) { //割り込み番号0(2番ピン)の電圧に変化があった時のみ実行される
if (digitalRead(3) == LOW) { //もし2番ピンがLOWなら{}内を実行する
timenow2 = millis()-timeold ;
}
}
うまくいったでしょうか。ここまで紹介した測定方法は様々な方法の中の一つの例であり、説明のために構造を単純化したり、部品をなるべく安全で安価なものに置換しています。そのため測定精度が必ずしも高いものではないことをご承知おきください。高精度で厳密な手法は皆さんが将来大学の工学部にて触れて頂けると幸いです。
掲載大学 学部 |
横浜国立大学 理工学部 | 横浜国立大学 理工学部のページへ>> |
私たちが考える未来/地球を救う科学技術の定義 | 現在、環境問題や枯渇資源問題など、さまざまな問題に直面しています。 これまでもわたしたちの生活を身近に支えてきた”工学” が、これから直面する問題を解決するために重要な役割を担っていると考えます。 |