おびやまロボット研究所 ~マイコン電子工作と自作ドローン~

おびやまロボット研究所 ~マイコン電子工作と自作ドローン~

反応速度を測るシステム



仕切り屋の某Y先生から,イベントのネタのご注文.

ダイソーから派手な色のデコパネを買って来ました.
CIMG8747.JPG

適当な長さの導線4本にクリップをハンダ付け.
CIMG8749.JPG

アルミテープにクリップを挟んで,デコパネに斜めに貼り付けます.
CIMG8751.JPG

クリップの部分や,アルミテープの浮いている部分をガムテープでマスクします.
これを2枚,アルミテープがクロスするようにスポンジ両面テープで隙間を空けて貼り付けます.
CIMG8755.JPG

ケーブルは外れないように,ホットボンドで固定.
CIMG8761.JPG

ケーブルを撚って,ブレッドボードやArduinoに挿して使えるようにピンをハンダ付けして出来上がり.
CIMG8757.JPG

手で押してスイッチのON-OFFを確認している動画→ パネルスイッチ

実際は,人が乗ってON-OFFさせて使う予定.
その動画→ パネルスイッチに乗ってジャンプ

以上,製作時間は1時間でした.

参考リンク↓
お手製パネルスイッチ
Arduinoでピアノ階段 with GarageBand

-----------------------------------------
反応速度を測る装置(マイコン部分)

パネルスイッチを何に使うかというと・・・
よくあるランプが付いてジャンプするまでの反応速度を測る装置です.
マイコンにシリアル通信でコマンドを送ると計測を開始して,ジャンプするまでの時間をミリ秒単位で返してくるように作りました.
後はPC側のソフトを作るだけですが・・・・誰かソフト系の人が作ってくれないかなぁ.

その動画→ 反応速度を測る装置のマイコン部分


-----------------------------------------
反応速度を測る簡単なシステムのソフトも作った.

昨日作ったパネルスイッチとArduinoのスケッチを利用して,ProcessingでPCに反射速度を表示するプログラムを組みました.やってみると昨日より遅いです.どうやらコマンド送受信と表示までの誤差があるようで,昨日の記録と比較すると0.05秒くらい遅れています.
とりあえず,0.05秒引いて表示させて使うとして,正確に遅れを計測する方法を考えます.
それよりは,画面をもっと楽しいものにしないと...

jump.jpg

おじさんがのそっと飛んでる動画はこちら→ 反応速度を測る簡単なシステム

-----------------------------------------
Arduinoのスケッチ

// ---------------------------------------------------------------------------
// 反応時間を調べる装置ためのスケッチ
//      葉山清輝(熊本高専) 2013.6.27
//
// 右側のスイッチは4ピンとGND,左側の5ピンとGND間にそれぞれ接続.
// スイッチONの時1,offの時0を返す.
//
// コマンドの説明
// r: スイッチRを調べ,押されたら1,そうでなければ0を返す.
// l: スイッチLを調べ,押されたら1,そうでなければ0を返す.
// b: 両方のスイッチを調べ,共にONのとき1,右のみ2,左のみ3,共にOFFで0を返す.
// g: 両方のスイッチが離されるまでの時間を返す.最初から両方離れてたら0を返す.
// ---------------------------------------------------------------------------

int swR = 4; // 右スイッチの番号
int swL = 5; // 左スイッチの番号
int led =13; // LED出力用
int cmd; // コマンド入力用
long time; // 時間計測用
void setup() {
pinMode(swR, INPUT); digitalWrite(swR, HIGH); // スイッチRを入力モード,プルアップに設定
pinMode(swL, INPUT); digitalWrite(swL, HIGH); // スイッチLを入力モード,プルアップに設定
pinMode(led, OUTPUT); digitalWrite(led,LOW); // LEDを出力,消灯
Serial.begin(9600);
randomSeed(analogRead(0)); // 未接続のアナログピンのノイズで乱数の初期化

}

void loop() {

// シリアル通信によりコマンドを監視.コマンドを受けた時それぞれの処理をする.
if (Serial.available()>0){
cmd=Serial.read(); // コマンド受信

// スイッチRを調べ,押されたら1,そうでなければ0を返す.
if (cmd=='r') if (digitalRead(swR)==1) Serial.println(0); else Serial.println(1);

// スイッチLを調べ,押されたら1,そうでなければ0を返す.
if (cmd=='l') if (digitalRead(swL)==1) Serial.println(0); else Serial.println(1);

// 両方のスイッチを調べる.
if (cmd=='b'){
if (digitalRead(swR)==0 && digitalRead(swL)==0) Serial.println(1); // 共にONのとき1
else if (digitalRead(swR)==0 && digitalRead(swL)==1) Serial.println(2); // 右スイッチのみONのとき2(文字入力などに使う予定)
else if (digitalRead(swR)==1 && digitalRead(swL)==0) Serial.println(3); // 左スイッチのみONのとき3(文字入力などに使う予定)
else Serial.println(3); // 共にOFFのとき0
}
// 両方のスイッチが離されるまでの時間を返す.最初から両方離れてたら0を返す.
if (cmd=='g') {
if (digitalRead(swR)==0 || digitalRead(swL)==0){
time=millis();
while(digitalRead(swR)==0 || digitalRead(swL)==0);
Serial.println(millis()-time);
} else {
Serial.println(0);
}
}

// 両方のスイッチが離されるまでの時間を返す.最初から両方離れてたら0を返す.
if (cmd=='a') {
if (digitalRead(swR)==0 || digitalRead(swL)==0){
digitalWrite(led,LOW);
delay(3000+random(8000));
digitalWrite(led,HIGH);
time=millis();
while(digitalRead(swR)==0 || digitalRead(swL)==0);
Serial.println(millis()-time);
digitalWrite(led,LOW);
} else {
Serial.println(0);
}
}

}

}

-----------------------------------------
Processingのスケッチ


import processing.serial.*;

Serial myPort; // Create object from Serial class
float value;
String inBuffer;
int state=0;
float tim;
int lf = 10;
float tmp;

void setup()
{
size(800, 600);
background(0);

String portName = Serial.list()[2];
myPort = new Serial(this, portName, 9600);
println(Serial.list()); // シリアル・ポートのリストを表示
delay(2000);

inBuffer = myPort.readStringUntil(lf);
inBuffer = null;
}

void draw()
{
if ( myPort.available() > 0) inBuffer = myPort.readStringUntil(lf);

if (inBuffer != null) {

if (state==1){
tmp =float(inBuffer);
inBuffer = null;
if (tmp==1) state=2; else state=0;
}

if (state==4){
background(0);
value=float(inBuffer)/1000;
value-=0.05; // 誤差の補正(仮)
textSize(200);
text(value,0,200);
inBuffer = null;
tim=millis();
state=5;
}

}

if (state==0){
myPort.write('b');
background(0);
textSize(200);
text("Ready",0,200);
state=1;
}

if (state==2){
background(0);
textSize(200);
text("Set ",0,200);
tim=millis()+random(2000,8000);
state=3;
}

if (state==3){
if (millis()>tim){
background(0);
textSize(200);
text("Jump!",0,200);
myPort.write('g');
state=4;
}
}

if (state==5){
if ((millis()-tim)>5000){
state=0;
}
}

}

------------------------------------------
更に追加
"Jump"と"Stay"の2通り表示させるようにして,条件付きで反応速度を測るようにした.
ランキングの入力と表示ができるようにした.
ランキングはファイルに記録して,次回起動時も呼び出せるようにした.
これで必要最小限はできた.
でもやっぱり画面が地味でゲーム性に乏しい.
ゲーム目的じゃなくて,数値計測用途と考えれば,LEDマトリクスを数個使って,
PCなしでコンパクトに作った方が良かったかも.

ranking.jpg

------------------------------------------
Jump and Stay, ランキング, ファイル記録まで追加したスケッチ


// 反応時間計測
// コマンド
// g:PCからコマンドを送った時からジャンプするまでの時間を計測して返す
//

import processing.serial.*;

Serial myPort; // Create object from Serial class
float value;
String inBuffer;
int state=0;
float tim;
float demo=0;
int lf = 10;
float tmp;
float[] rank= new float[5];
String[] rankSt= new String[5];
String[] name= new String[5];
int ranking;
int nchar=95; // '_'
int poschar=0;
String inputName;

void setup()
{
size(800, 600);
background(0);

String portName = Serial.list()[2];
myPort = new Serial(this, portName, 9600);
println(Serial.list()); // シリアル・ポートのリストを表示
delay(2000);

inBuffer = myPort.readStringUntil(lf);
inBuffer = null;

for(int i=0;i<5;i++) rank[i]=100;
for(int i=0;i<5;i++) { name[i]="";}

name = loadStrings("rankName.txt");
rankSt = loadStrings("ranking.txt");
for(int i=0;i<5;i++) rank[i]=float(rankSt[i]);

inputName="";

}

void draw()
{
if ( myPort.available() > 0) inBuffer = myPort.readStringUntil(lf);

if (inBuffer != null) {

if (state==10){ // "Ready"状態から上に乗ったかどうかを確認する
tmp =float(inBuffer);
inBuffer = null;
if (tmp==1) state=1; else state=0;
}

if (state==11){ // "Jump"した時の時間測定
value=float(inBuffer)/1000;
value-=0.05; // 誤差の補正(仮)
if (value>0){ // 正しくジャンプした時
background(0);
textSize(200);
text(value,50,350);
inBuffer = null;
tim=millis()+5000;
state=5; // ランキングの処理へ
} else state=4; // フライングした時,"Miss"を表示
}

if (state==12){ // stay状態
tmp =float(inBuffer);
inBuffer = null;
if (tmp==0) state=4; // 間違って飛んだ時,"Miss"表示
else state=3;
}

if (state==13){ // ランキング入力
tmp =float(inBuffer);
inBuffer = null;
if (tmp==2) {
if (nchar==95) nchar=65;
else if (nchar<90) nchar++;
}
if (tmp==3) {
if (nchar==65) nchar=95;
else if (nchar>65 && nchar!=95) nchar--;
}
if (tmp==0){
if (poschar<8){
if (nchar!=95){
inputName=inputName+char(nchar);
poschar++;
} else poschar=8; // 入力終了
}
}
if (poschar==8) { // 名前入力の完了判断
name[ranking]=inputName;
inputName="";
nchar=95;
poschar=0;
state=7; // ランキング表示へ
} else state=6; // ランキング入力へ

}

}

if (state==0){ // 初期状態,Readyを表示して,乗るのを待つ
myPort.write('b');
delay(100);
background(0);
textSize(200);
text("Ready",100,350);
state=10;
if (millis()>(demo+10000)) {
demo=millis();
state=7;
}
}

if (state==1){ // SETを表示して,タイマーをセット
background(0);
textSize(200);
text("Set ",100,350);
tim=millis()+random(2000,5000);
state=2; // 次の状態へ↓
}

if (state==2){ // セットした時間経過後,"Jump"または"Stay"を乱数で選んで表示させる
if (millis()>tim){
myPort.clear();
background(0);
textSize(200);
if(random(1)<0.6){
text("Jump!",100,350);
myPort.write('g');
state=11; // "Jump"の時の処理へ
} else {
text("Stay",100,350);
tim=millis()+2000;
state=3; // "Stay"の時の処理へ()
}
}
}

if (state==3){ // stayの時の処理
if (millis()>tim){ // 2秒間は表示を続ける
demo=millis(); // デモに行かないようにデモの待ち時間をリセット
state=0;
} else {
myPort.write('b'); // stayでジャンプしないかチェック
delay(20);
state=12;
}
}

if (state==4){ // 間違って飛んだ時"Miss"表示
background(0);
textSize(200);
text("Miss",100,350);
tim=millis()+2000;
state=8; // 時間が建ったら最初に戻す
}

if (state==5){ // ランキング処理
if (millis()>tim){
ranking=checkRank();
if (ranking<5){
state=6; // ランキングに入った時,ランキング入力
} else {
state=7; // ランキングに入らなかった時は,ランキング表示のみ
}
}
}

if (state==6){ // ランキング入力
InputRank();
state=13;
myPort.write('b');
delay(250); // 250ms秒毎に入力街
}

if (state==7){ // ランキング表示
dispRank();
tim=millis()+5000;
state=8;
}

if (state==8){ // 時間が建ったら最初に戻す
if (millis()>tim){
demo=millis(); // デモに行かないようにデモの待ち時間をリセット
state=0;
}
}

}

int checkRank(){
int i,j;
for (i=0;i<5;i++){
if (rank[i]>value){
for(j=4; j>i; j--){
rank[j]=rank[j-1];
name[j]=name[j-1];
}
rank[i]=value;
break;
}
}
return(i);
}

void dispRank(){
background(0);
textSize(100);
text("Ranking",50,100);
textSize(50);
text("1:",100,200); if (rank[0]<100){ text(rank[0],150,200); text(name[0],400,200); }
text("2:",100,280); if (rank[1]<100){ text(rank[1],150,280); text(name[1],400,280); }
text("3:",100,360); if (rank[2]<100){ text(rank[2],150,360); text(name[2],400,360); }
text("4:",100,440); if (rank[3]<100){ text(rank[3],150,440); text(name[3],400,440); }
text("5:",100,520); if (rank[4]<100){ text(rank[4],150,520); text(name[4],400,520); }
textSize(100);
text("*",40, ranking*80+235);
saveStrings("rankName.txt",name);
for(int i=0;i<5;i++) rankSt[i]= Float.toString(rank[i]);
saveStrings("ranking.txt", rankSt);
}

void InputRank(){
background(0);
textSize(150);
text("Rank in!",50,200);
textSize(80);
text("Enter your name.",50,300);
textSize(50);
text("(max 8 character, _:end)",50,350);
textSize(100);
text(inputName,50,450);
text(char(nchar),poschar*69+50,450);
}


© Rakuten Group, Inc.
X
Create a Mobile Website
スマートフォン版を閲覧 | PC版を閲覧
Share by: