下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成せよ。
今回私は A地点から出発 → M → K(直進) → L(ピンポン玉をつかむ) → K(右折) → J(一時停止の後、左折) → I(直進) → H(直進) → G(左折) → F → E → D(一時停止の後、直進) → C(直進) → B(一時停止) → シュート→ A地点に入る(ゴール) というルートを進むロボット、プログラムを作成した。
今回のロボットでは左タイヤをA、右タイヤをC、ボールをつかむところをBとした。
最初は両側から挟み込むタイプにしていたが、ピンポイントでロボットを移動させないといけなくなり、難易度が上がるため変更しました。 大きくすくうようなアームとボールの受け皿としてタイヤを使うという工夫をしました。
まず今回ロボットを光センサーでレール上を動かすために、
この図のように線の左側に沿ってロボットが動くようにした。
簡単に説明をすると白くなると右へ行き、黒くなると左に、ちょうど中間の際はまっすぐ、そして白と中間の間は若干右に、右と中間の間は若干左にというように、五段階に分けてレール上をトレースするようにした。 センサーでの値は周りの明るさにも大きく左右されるので、その時々で微調整をするため今回のプログラムでの値は最終的に完成した際のものとなっている。
交差点を判断するために、まず図のようにレール上にセンサーがいる時間を測り続け、連続して黒にいる時間が0.20秒間、曲がりきれずにレール上にいた場合を交差点と判断させ、一時停止するようにしている。 その場合急なカーブに差し掛かった際に交差点と判断してしまう可能性が高いため、M→Kのルートを動いている時間を測りその間は交差点を判断させずにライントレースのみ行うようにすることで対応している。 交差点を判断し一時停止した後は別のサブルーチンでルートに対応できるようなプログラムを作りました。
まずはレール上を動く際のサブルーチン定義した
sub follow_line() { int t; t=FastTimer(0); int n; n=FastTimer(1); SetSensor(SENSOR_1,SENSOR_LIGHT); ClearTimer(0); while(FastTimer(1)<1200) { if(SENSOR_1>=50){OnFwd(OUT_A);OnRev(OUT_C);ClearTimer(0); }else if(SENSOR_1>48){OnFwd(OUT_A);ClearTimer(0); }else if(SENSOR_1>42){OnFwd(OUT_AC);ClearTimer(0); }else if(SENSOR_1>39){OnFwd(OUT_C);ClearTimer(0); }else{OnFwd(OUT_C);OnRev(OUT_A);ClearTimer(0); } }
ここではタイマーとライトの設定、そしてレール上を五段階で判断してトレースするプログラムとなっている。
タイマーの1ではこのM→K→Lに移動する際に交差点と間違えないように、Kの直前までの時間を測って、そこまでは交差点に関係なくレールに必ずついていくようにしました。 ですのでサブルーチン内ではタイマー1をリセットしないようにしています。それによってプログラムを開始して12秒後は必ず[while]の中の[while]のプログラムで動くようになっています。
while(FastTimer(0)<20) { if(SENSOR_1>=50){OnFwd(OUT_A);OnRev(OUT_C);ClearTimer(0); }else if(SENSOR_1>48){OnFwd(OUT_A);ClearTimer(0); }else if(SENSOR_1>42){OnFwd(OUT_AC);ClearTimer(0); }else if(SENSOR_1>39){OnFwd(OUT_C);ClearTimer(0); }else{OnFwd(OUT_C);OnRev(OUT_A); } } Off(OUT_AC); Wait(100); }
ボールを取リ終わった後は交差点を判断するために黒い線に一定時間乗っている場合に停止するというようにしています。ここで黒の場合の時以外には時間をリセットする。それによって黒が連続する時間を測り0.2秒以下の場合は(while)内のプログラムを繰り返すようにしている。
今度は交差点を渡る際のサブルーチンです。
sub cross_line() { SetSensor(SENSOR_1,SENSOR_LIGHT); OnFwd(OUT_AC); Wait(30); Off(OUT_AC); while(SENSOR_1<40) { OnFwd(OUT_A); } Off(OUT_A); Wait(100); }
交差点で止まった際に渡れるようにほんの少し前に出たのち、黒い線に合うまで回転を続けるというプログラムとなっています。
#define turn_left OnFwd(OUT_C);Wait(20);Off(OUT_C);
今度のプログラムはF→Eを渡る際に交差点と判断してしまうため、左回転することでコースからそれないように定義したものとなります。
task main() { ClearTimer(1); follow_line(); OnFwd(OUT_A); Wait(80); Off(OUT_A); OnFwd(OUT_AC); Wait(50); Off(OUT_AC); OnRev(OUT_B); Wait(30); Off(OUT_B); OnRev(OUT_AC); Wait(80); Off(OUT_AC); OnFwd(OUT_C); OnRev(OUT_A); Wait(50); Off(OUT_AC);
ここまでのプログラムですが、上記した通りにKのルートまで移動します。
その後はこの図のように、まずロボットが交差点を判断したときに位置を調整してまっすぐ線に沿って動きます。その後事前に測ってあったボールまでの時間動きボールをつかみます。 そのまま線に沿わせると来た道をたどって戻ってしまうので、バックしたのちに旋回して位置を調整しています。
follow_line(); follow_line();
ここで[follow_line]を二度続けている訳を説明すると、[cross_line]では一度前に出るように設定しているため、うまくレールに沿って動けないので、二度繰り返すことで移動させています。
cross_line(); follow_line(); cross_line(); follow_line();
ここではI→Hまでの移動です
turn_left; follow line(); turn_left; follow_line();
ここでは上記のように[cross_line]では線の上に沿えないので左に曲がることで、半ば無理やりですが移動させています。
cross_line(); follow_line(); cross_line(); follow_line();
ここではD→C間の移動のプログラムです。
OnFwd(OUT_B); OnRev(OUT_AC); Wait(40); Off(OUT_ABC); OnFwd(OUT_AC); Wait(60); Off(OUT_AC); Wait(80); OnFwd(OUT_AC); Wait(90); Off(OUT_AC); }
ここはシュートのプログラムとなっています。Bを交差点と判断したロボットがボールを離して後ろに下がり前に移動してボールを転がして、その後に自分もAまで移動するというプログラムになっています。
まずボールを掴むというところまでは何度試してもほぼ必ず成功するようにはなった。しかしその後のI→H→F→E→Dまでの間に交差点と判断できるかできないかで全てずれてしまうため、あまり汎用性のあるプログラムにはならなかった。
コースに沿って動かす際にFとEの辺りで左に曲がるプログラムを作るなど、少し無理やり感があるものとなってしまった。
コースが予想以上に小さくロボットをもう少し小型化することで小回りが利いて交差点の判断などももう少しうまくできる余地があった。