私はグループの中で、A-Bまでのコースの役割を背負いました。 今回は始めに言いますが、結局成功しませんでした。だけれど、その分、大学生活のかなりの時間を割いて、ロボットの作成に当たりました。CurrentTick()を使ったタイム処理か、それとも、黒の回数を、カウントして交差点を乗り切るのか、結末を言えば、成功確率のうえで、タイム処理が勝ってしまいました。回数のカウントは、その時の明るさなど、変数域が大きく、失敗してばかりでした。 一方でタイム処理のほうはまだそれに比べればましだという程度のことでした。
アームを前面に取り付けて、固定に、本体との接続を利用した。 光センサーはTraceの感覚がちょうどよくなるように、アームと、車輪との間に取り付けた。 また、アームには、歯車を使用して、アームが前面に取り付けやすいように工夫した。 重心が、前半部分に偏り過ぎていたため、本体の後ろ側に固定には、かなり気を使った。 しかしながら、アームの部分が少し重すぎるためか、少し圧力がかかると、容易にぐらついてしまうのが難点ではある。 そのほか、始めは、前タイヤと、アームとがある境界を超えると、接触してしまうというバグが低確率だが一定の確率でおきていたのを、修正した。
#define Do 523 #define Re 587 #define Mi 659 #define Fa 698 #define So 784 #define Ra 880 #define Si 988 #define DoX 1047 #define SSS 50 #define AAA 500 #define DoI() PlayTone(Do,SSS);Wait(AAA); #define ReI() PlayTone(Re,SSS);Wait(AAA); #define MiI() PlayTone(Mi,SSS);Wait(AAA); #define FaI() PlayTone(Fa,SSS);Wait(AAA); #define SoI() PlayTone(So,SSS);Wait(AAA); #define RaI() PlayTone(Ra,SSS);Wait(AAA); #define SiI() PlayTone(Si,SSS);Wait(AAA); #define DoXI() PlayTone(DoX,SSS);Wait(AAA); #define MTT 300 #define STT 100 #define UTT 300 #define MT 150 #define QTT 600 #define Particular 500
上記のマクロ群は、Trace中にマシンが刻むリズム、または、ゴール時に鳴らす「カエルのうた」のためのものである。前半部分はド〜ド周波数をマクロに設定し、さらにそれをの再生可能な形でマクロに入れ込んだものである。後半の部分は、「カエルのうた」の音楽を、よりその曲に近づけるように、テンポをつくるために設定したものである。(カエルのうたの後半部分では、二倍速にテンポが変わるところがある)
#define GPT CurrentTick() //Get the Present Time の略 #define Check 1 //光センサーの検知時間 #define Catches RotateMotor(OUT_C,15,80);Off(OUT_C); //ボールのキャッチ #define Break Wait(1000); //小休止 #define Threshold 55 //黒レベル #define GoFor OnFwd(OUT_AB,40);Wait(500);Off(OUT_AB); //ボールへ接近 #define goal RotateMotor(OUT_C,15,-80);Off(OUT_C);OnFwd(OUT_AB,100);Wait(200);Off(OUT_AB);
sub RandomI(){ int RandomBox = Random(8); if(RandomBox == 1){ DoI()}; if(RandomBox == 2){ ReI()}; if(RandomBox == 3){ MiI()}; if(RandomBox == 4){ FaI()}; if(RandomBox == 5){ SoI()}; if(RandomBox == 6){ RaI()}; if(RandomBox == 7){ SiI()}; if(RandomBox == 8){ DoXI()}; }
八音階がTrace中の検知ごとに、ランダムになるように、サブルーチンを用いて設定したものである。
task FlagSong(){ while(true) { PlayTone(523,STT);Wait(MTT); PlayTone(587,STT);Wait(MTT); PlayTone(659,STT);Wait(MTT); PlayTone(698,STT);Wait(MTT); PlayTone(659,STT);Wait(MTT); PlayTone(587,STT);Wait(MTT); PlayTone(523,50);Wait(MTT);
Wait(300);
PlayTone(659,50);Wait(MTT); PlayTone(698,50);Wait(MTT); PlayTone(784,50);Wait(MTT); PlayTone(880,50);Wait(MTT); PlayTone(784,50);Wait(MTT); PlayTone(698,50);Wait(MTT); PlayTone(659,50);Wait(MTT);
Wait(300);
PlayTone(523,50);Wait(QTT); PlayTone(523,50);Wait(QTT); PlayTone(523,50);Wait(QTT); PlayTone(523,50);Wait(QTT);
PlayTone(523,50);Wait(MT); PlayTone(523,50);Wait(MT); PlayTone(587,50);Wait(MT); PlayTone(587,50);Wait(MT); PlayTone(659,50);Wait(MT); PlayTone(659,50);Wait(MT); PlayTone(698,50);Wait(MT); PlayTone(698,50);Wait(MT);
PlayTone(659,50);Wait(UTT); PlayTone(587,50);Wait(UTT); PlayTone(523,50);Wait(UTT); Wait(500); }
}
「かえるのうた」の楽曲である。
task main (){ SetSensorLight(S3); //センサーをセット
long t0 = GPT; //現在時刻を習得
while(GPT-t0 < 6000) //6秒よりも下は、以下の動き { OnFwdSync(OUT_AB,60,(SENSOR_3-Threshold)*23/10); //Line trace
この関数のみでTraceが可能なのは、(SENSOR_30-Threshold)の値が、LINEが黒ければ、マイナスの値をとり、白ければ、プラスの値を返すからである。後半の四則演算は、その値を調整するためである。(これを考えたのは、他の班員である)
RandomI(); //ランダムに音を鳴らす。 } OnFwd(OUT_B,40);Wait(500);Off(OUT_B); Break GoFor Catches
一つ目の交差点に差し掛かったところで、Traceを中断し、前進、そして、小休止を入れたうえで、ボールに接近し、ボールをアーム内に抱え込む。
while(GPT-t0 > 6000 && GPT-t0 < 24000) { OnFwdSync(OUT_AB,40,(SENSOR_3-Threshold)*23/10); RandomI(); }
23/10の値はTresholdとSENSOR_3の値が+-のどちらにもなるので、交互の車輪が動いて、ライントレースが可能だが、その+-の値が小さな値だという欠点があるので、始めに23の値を掛けてその値を増やしたのちに、その値を、RotateMotor関数の同期率になおすために/10の値をつけてある。なお、23という値は試行錯誤の上に当てはまった値である。 [#lb342a85] 再び円形内のLINEを音を鳴らしながら、Traceする。
OnFwd(OUT_B,40);Wait(500);Off(OUT_B);
24秒経過後に、第二交差点を乗り越える。
while(GPT-t0 > 24000 && GPT-t0 < 28000) { OnFwdSync(OUT_AB,40,(SENSOR_3-Threshold)*23/10); RandomI(); }
この間は、このプログラムのみで十分で、しっかりと、直角のカーブを曲がり、最終目的地B地点まで 到達する。
start FlagSong; goal; Float(OUT_AB); Wait(12000); //かえるのうた二曲分の長さ stop FlagSong; }
ゴールへの到着とともに、「かえるのうた」を演奏し、勢いのある、短い前進とともに、ボールをシュートする。
何度も、何度も、何度も、ロボットを作り替えた。 はじめのロボットは、タイヤがバギーのような形をしたものになった。 しかし、このロボットは曲がるときに、前タイヤが、変な角度に曲がってしまい、マシンとして機能しなかった。 次に、つくったロボットはとても、背が高く、光センサーをつけるのにとても、無駄な部品を使ってしまったため、廃案になった。マシンの作成に思いのほか時間がかかってしまったが、その分、部品の利用価値について多くのことを学べたと思う。これは次回に生きる脳力である。
今回からはBGMの領域まで踏み込んだので、どうせならと、生物らしい、ランダムでの音階で、行動を表現しようと考えた。楽曲を作成するうえで簡単な曲といえども、感覚やリズムを表現するのを考えるのは楽しいひと時であった。結果として、ボールがうまくアームに収まらないなど、ゴールにたどりつくことはなかったが、時間を費やした分、自分の身になったと、肌で感じることができるので、そこになにも、後悔の念はない。
ロボットコンテストのページがなかったのでこちらに書かせていただきます。
課題2の時点では、Thresholdの値から、黒が連続的に続いて、交差点判定をするということができなかったが、ロボコンの時にはかなりその技術は試行錯誤の上に、感性されていた。
一周目のロボコンのときは、準備が足らず、うまくいかなかったがその次週からは時間も多く費やすことができたので、いいロボコンをすることができたと思う。
上の写真は実際にコースを走っているマシンである。
少し懲りすぎてしまい、本来の目的の「どれだれ速くゴールできるか」ということから離れてしまって、もっと軽量化すべきだったと後から気づいた
後ろのタイヤの部分は、タイヤではなくボールで代用した。
なぜなら、タイヤだと大きな溝、急な方向転換にはついていけないからだ。
アームは歯車を使い直接ボールをつかめるようにした。基本的には、ボールを挟んで走っている。
#define Do 523 #define Re 587 #define Mi 659 #define Fa 698 #define So 784 #define Ra 880 #define Si 988 #define DoX 1047 #define SSS 50 #define AAA 500 #define DoI() PlayTone(Do,SSS);Wait(AAA); #define ReI() PlayTone(Re,SSS);Wait(AAA); #define MiI() PlayTone(Mi,SSS);Wait(AAA); #define FaI() PlayTone(Fa,SSS);Wait(AAA); #define SoI() PlayTone(So,SSS);Wait(AAA); #define RaI() PlayTone(Ra,SSS);Wait(AAA); #define SiI() PlayTone(Si,SSS);Wait(AAA); #define DoXI() PlayTone(DoX,SSS);Wait(AAA);
ドレミファソラシドの8音階を、PlayToneで鳴らすためのマクロ(スピードは一定)
#define CONN 1//Bluetoothのコネクション番号 #define Signal_One 1//受け渡しの番号 #define Use_Motor OUT_AB//瞬時に、モータのポート番号を変更できるように #define Use_Arm OUT_C//瞬時に、モータのポート番号を変更できるように int Trace_Power = 30;//intで設定しておくことで、いつでもパワー調整が可能なようにする #define Sync_Trace(int Sync_Ratio) OnFwdSync(Use_Motor,Trace_Power,Sync_Ratio); //OnFwdSyncを用いて、その同期率をThresholdの値によって変更することで、ライントレースを可能にした、つまり、同期と反転同期により、左右の回転数に違いを持たせることで、左右に曲がることができる #define DetectTime 1//光センサーの検知時間 int Standard_Sync_Ratio = 60;//基準となる、同期率の値、60にしてあるのは、大きめに設定しなければ、カーブに対しては、同期率が小さすぎたため
#define FIC_Maxmum 100//First Intersection Countの略 交差点判定に使う #define SIC_Maxmum 110//Second Intersection Count #define TIC_Maxmum 120//Third Intersection Count #define NIC_Maxmum 90//Nothing Intersection Count Nothingなのは、交差点ではなく、S字カーブであるため #define LIC_Maxmum 120//Last Intersection Count #define Boundary_Threshold 54//基準となる、Thresholdの値、境界の辺り #define Final_Curve_Time 600//最後のカーブでボールを離すまでの曲り具合、何度も調整が必要だったのでマクロで設定しておいた
#define First_Limitation 6 int Second_Limitation = 20; #define Third_Limitation 20 #define Forth_Limitation 4
Limiationの意味は、交差点を曲がってからは、しばらく交差点判定をする必要がないので、無駄なところで交差点判定しないように、時間で定めたものである。つまり、交差点判定に対する制限。
#define After_Second_Recover_Time 4//名の通り、二つ目に交差点と、三つ目の交差点の幅が極端に短いために、交差点判定しなかったときようの保険である。4秒後自動的に動き出す。 #define Finish_Time 30 //最後のカープで、交差点判定しなかったら、自動的に30秒後動き出す。 #define PlayTone_Time 1//応援に対して、1秒間音を返す。 int IC = 0;//Intersection Counter 交差点判定のためのカウンター bool Finish = false;//最後で、この値がtrueになると、whileが終了する。 int RF = 1;//Reverse Function//ライントレースの方向を反転させるための数値 long t0 = 0; long t1 = 0; long t2 = 0; long t3 = 0; long t4 = 0; long t5 = 0; long t6 = 0;
ここで、タイムマー設定しておかないと、whileの中で、宣言したときには、=0となり、うまく作動しなかったため設定しておいた。
sub CatchAndStart(){//start CatchAndStart OnRev(Use_Arm,30);Wait(800);Off(Use_Arm); Sync_Trace(0)Wait(1000); }//end CatchAndStart
ボールをキャッチするようの動作
sub Bluetooth_Start(){// start Bluetooth_Start int msg = 0; while(msg != 1) {//start Bluetooth while ReceiveRemoteNumber(MAILBOX1,true,msg); if(SENSOR_1 >= Sound_Start){ msg =1; } if(msg == 1) { t0 = CurrentTick(); DoI();ReI();MiI(); CatchAndStart(); while((CurrentTick()-t0)/1000<=3) { SendResponseNumber(MAILBOX1,2); } } }//end Bluetooth while }//end Blutooth_Start
相方のマシーンから、信号が送られてきたら、スタートし、こちらも信号をおくりかえしたら、
相方のマシーンがC地点へと向かう。ボールをつかみ、全身して、ライン上に乗る。なお、SoundSensor
によるスタートも可能となっている(本番では使用しなかった)。
sub Line_Trace() {//start sub if(SENSOR_3 < Boundary_Threshold-10){ Sync_Trace(RF*(Standard_Sync_Ratio+9))IC++; } else if(SENSOR_3 < Boundary_Threshold-9){ Sync_Trace(RF(Standard_Sync_Ratio+8))IC++; } else if(SENSOR_3 < Boundary_Threshold-8){ Sync_Trace(RF(Standard_Sync_Ratio+7))IC++; } else if(SENSOR_3 < Boundary_Threshold-7){ Sync_Trace(RF(Standard_Sync_Ratio+6))IC++; } else if(SENSOR_3 < Boundary_Threshold-6){ Sync_Trace(RF(Standard_Sync_Ratio+5))IC++; } else if(SENSOR_3 < Boundary_Threshold-5){ Sync_Trace(RF(Standard_Sync_Ratio+4))IC++; } else if(SENSOR_3 < Boundary_Threshold-4){ Sync_Trace(RF(Standard_Sync_Ratio+3))IC++; } else if(SENSOR_3 < Boundary_Threshold-3){ Sync_Trace(RF(Standard_Sync_Ratio+2))IC++; } else if(SENSOR_3 < Boundary_Threshold-2){ Sync_Trace(RF(Standard_Sync_Ratio+1))IC++; } else if(SENSOR_3 < Boundary_Threshold-1){ Sync_Trace(0)IC=0; } else if(SENSOR_3 < Boundary_Threshold){ Sync_Trace(0)IC=0; } else if(SENSOR_3 < Boundary_Threshold+1){ Sync_Trace(0)IC=0; } else if(SENSOR_3 < Boundary_Threshold+2){ Sync_Trace(RF(-Standard_Sync_Ratio-1))IC=0; } else if(SENSOR_3 < Boundary_Threshold+3){ Sync_Trace(RF(-Standard_Sync_Ratio-2))IC=0; } else if(SENSOR_3 < Boundary_Threshold+4){ Sync_Trace(RF*(-Standard_Sync_Rati3))IC=0; } else if(SENSOR_3 < Boundary_Threshold+5){ Sync_Trace(RF(-Standard_Sync_Ratio-4))IC=0; } else if(SENSOR_3 < Boundary_Threshold+6){ Sync_Trace(RF(-Standard_Sync_Ratio-5))IC=0; } else if(SENSOR_3 < Boundary_Threshold+7){ Sync_Trace(RF(-Standard_Sync_Ratio-6))IC=0; } else if(SENSOR_3 < Boundary_Threshold+8){ Sync_Trace(RF*(-Standard_Sync_Ratio-7))IC=0;} else if(SENSOR_3 < Boundary_Threshold+9){ Sync_Trace(RF(-Standard_Sync_Ratio-8))IC=0; } else if(SENSOR_3 < Boundary_Threshold+10){ Sync_Trace(RF(-Standard_Sync_Ratio-9))IC=0; } else { Sync_Trace(RF(-Standard_Sync_Ratio-10))IC=0; } Wait(DetectTime); TextOut(0,LCD_LINE1,"SESOR_3"); NumOut(80,LCD_LINE1,SENSOR_3); TextOut(0,LCD_LINE2,"Passed Time"); NumOut(80,LCD_LINE2,(CurrentTick()-t0)/1000); TextOut(0,LCD_LINE4,"Motor_A"); NumOut(80,LCD_LINE4,MotorTachoCount(OUT_A)); }//end sub
このプログラムが一番多く使用されるもので、このプログラム全体の8割を占める。
前半の部分は基準となるThresholdの値から、+-の方向に1ずつ離れていくことで、1違いでのライントレースを可能にしている。
また、パワー調整はその値に対応するように、同じように勾配をつけている。前述したが、その調整にはOnFwdSyncの関数を用いている。
また、RF(Reverse Function)をこの位置に挟むことでライントレースの切り替えを可能にしている。
後半は、ディスプレイにThresholdの検知値、スタートしてからの時間、MotorAの回転数をリアルタイムで把握するためのものである。
sub FIC_Movement(){//start FIC DoI(); Sync_Trace(-100);Wait(1000); Sync_Trace(0);Wait(700); IC = 0; }//end FIC
一番目の交差点での動き、ここから交差点判定での動きの定義が続くがその判定が行われたかを速やかに判断するために
それぞれ異なる音が鳴るように設定してある。
sub SIC_Movement(){//start SIC SoI(); Sync_Trace(-100);Wait(900); Sync_Trace(0);Wait(700); Sync_Trace(100);Wait(800); OnRevSync(Use_Motor,Trace_Power,0);Wait(0); IC = 0; }//end SIC
二番目の交差点での動き
sub TIC_Movement(){//start TIC RaI(); Sync_Trace(100);Wait(600); Sync_Trace(0);Wait(700); IC = 0; }//end TIC
三番目の交差点での動き
sub NIC_Movement(){//start NIC DoXI(); Sync_Trace(0);Wait(1300); IC = 0; }//end NIC
最後のS字カーブでの動き、トレースを反転させる
sub Finish_Movement(){//start Finish_Movement RaI(); if(SENSOR_1<100){ Sync_Trace(-100);Wait(Final_Curve_Time); }; Trace_Power =30; Finish = true; Off(Use_Motor);Wait(1000); OnFwd(Use_Arm,30);Wait(1000);Off(Use_Arm); OnRevSync(Use_Motor,Trace_Power,0);Wait(800); long t5 =CurrentTick(); while((CurrentTick()-t5)/1000 <= 2){ SendResponseNumber(MAILBOX2,2); }; OnRevSync(Use_Motor,Trace_Power,0);Wait(2200); Trace_Power = 50; OnRevSync(Use_Motor,Trace_Power,100);Wait(1800);Off(Use_Motor); OnRevSync(Use_Motor,Trace_Power,0);Wait(1200); }//end Finish_Movement
最後の動き。アームを開いた後にBluetooth通信によって相手側に信号を送っている。
後半はマップから離れるための動き。
sub Random_Reaction_PlaySound(){//start Random_Reaction_PlaySound int RandomX = Random(3); if(SENSOR_1 >=100){//start if if(RandomX == 1){ PlaySound(SOUND_UP); } if(RandomX == 2){ PlaySound(SOUND_FAST_UP); } if(RandomX == 3){ PlaySound(SOUND_DOUBLE_BEEP); } }//end if }//end Random_Reaction_PlaySound
この部分は余興の部分で、実際の声援に対して、ロボットが反応するようにSoundSensorを用いて
ランダムで3つのうちのどれかの音が反応に使用されるようになっている。
task main(){//start task main SetSensorSound(S1); SetSensorLight(S3);
t0 = CurrentTick();
while(Finish == false)//最後の動きの部分でFinishがtrueになるので、プログラムが終了する {//start ZeroWhile Bluetooth_Start(); t1 = CurrentTick();
while(IC<=FIC_Maxmum) {//start RaWhile Line_Trace(); if((CurrentTick()-t1)/1000<=First_Limitation){ IC = 0; }; TextOut(0,LCD_LINE3,"First_IC"); NumOut(80,LCD_LINE3,IC); Random_Reaction_PlaySound(); }//end RaWhile
FIC_Movement();
ここから、交差点判定ごとに部分分けしてあり把握しやすいように工夫してある。
ディスプレイに交差点判定に使われる値を表示して、実際調整しやすくなるようにした。
時間の差を1000で割っているのは、人間が秒数として把握しやすくするためである。
(そこまで細かい時間は実際には不要なので)
なお、カッコが終わるごとにstartとendを用いることでどこまでがどこまでかわかりやすくした。
t2 = CurrentTick();
while(IC<=SIC_Maxmum) {//start ShuWhile Line_Trace(); if((CurrentTick()-t2)/1000<=Second_Limitation){ IC=0; }; TextOut(0,LCD_LINE3,"Second_IC"); NumOut(80,LCD_LINE3,IC); Random_Reaction_PlaySound(); }//end ShuWhile
SIC_Movement();
二つ目の交差点までの動き、Second_Limitationの値は高めに設定されている。
カーブが円いので交差点判定をしてしまうことがよくあったためこのようになった。
t6 =CurrentTick(); while(IC<=TIC_Maxmum&&(CurrentTick()-t6)/1000 <= After_Second_Recover_Time) {//start GYAWhile RF = -1; Line_Trace(); TextOut(0,LCD_LINE3,"Third_IC"); NumOut(80,LCD_LINE3,IC); Random_Reaction_PlaySound(); }//end GYAWhile
TIC_Movement();
三つ目の交差点までの動きだが、少し短すぎるという理由でかなり不安定(交差点判定がうまくいかない)
だったので、時間制御でもしも判定しなくても、同じように交差点判定後の動きをするように設定した。
t3 = CurrentTick(); while(IC<=NIC_Maxmum) {//start SHOWhile RF = -1; Line_Trace(); if((CurrentTick()-t3)/1000 <= Third_Limitation){ IC=0; }; TextOut(0,LCD_LINE3,"Not_IC"); NumOut(80,LCD_LINE3,IC); Random_Reaction_PlaySound(); }//end SHOWhile
NIC_Movement();
S字カープまでの動き。ロボコン中に指摘があった通りここで、わざわざライントレースを切り替える必要はなかったのかもしれないが
アドバンテージがあるとすれば、道が狭くなっている部分を通らなくてよくなることによって、
別の道に進む危険を減らせるということがある。
t4 =CurrentTick(); while(IC<=LIC_Maxmum&&(CurrentTick()-t4)/1000<=Finish_Time) {//start Finishwhile RF = 1; Trace_Power = 25; Standard_Sync_Ratio = 70; Line_Trace(); if((CurrentTick()-t4)/1000<=Forth_Limitation){ IC = 0; }; Random_Reaction_PlaySound(); }//end Finishwhile Finish_Movement();
最後の動き。実は交差点で判定をせずにフリーズするという現象が起こったので
時間制御で保険をかけている。
}//end ZeroWhile }//end task main
プログラム面では交差点判定が何度も失敗してしまい、その対策として、時間で動かしたり
数値を見えるようにして、把握しやすいようになど、とにかく、対応がしやすいようにした。
かなり対策したのにも関わらず、実際、本番でも交差点判定を二度間違えてしまい、非常悔しい思いを味わった。
また、task main内では時系列にそうように順番にプログラムを実行することで、部分的にプログラム
を走らせたいときに、すぐにコメント文にできるようにした。
全体的には、とにかく今回は「どれだけ、修正する速度」を上げられるかということが主題であった。
以下は走行中のロボット
まず、始めに言いたいことそれはロボットを触り試行錯誤をして、問題を解決していくということが
よりいっそう好きになれたというような気がするということです。
初めからうまくいく人など存在しない。ただ、根気を持ってそのことに挑み続けること、
それさえできれば、ロボットにかぎらずどんなことでも、ゆっくりと上達していけるのだと思います。
ただ、プログラムの場合は間違っているということが非常にハッキリしている。これはとてもよいことだと思いました。
生活の中では完璧に間違っているということを主張することがなかなかできないで相対論に陥りがちですが、
プログラムの場合はその曖昧さがありません。そこに貫徹しているルールに従っているだけなのですから。
このように、明らかな間違いを提示されて、それを修正するということこの力は思考そのものであるようにも感じました。
最後にですが、信州大に入ってまさか、ロボットにここまで深く携われる機会があるとは思っていなかったのですが、のめりこんでしまい、本当に良かったなと思います。有難うございました。