あらかじめ指定されたコースの線に沿って動くロボットを作る。
1.Aをスタート 2.Bを直進 3.Cを右折 4.Fを直進 5.Rを左折(一時停止) 6.Pを直進 7.X地点の紙コップを取得してコースに戻る 8.Qを左折 9.Sを直進(一時停止) 10.Y地点に紙コップを置いてコースに戻る 11.Sを直進(一時停止) 12.Fを左折(一時停止) 13.Cを右折(一時停止) 14.D地点へ(ゴール)
作成したロボットはロボットの組み立て方が載っている本のマニュアルを参考に、台車部分を組み立て、台車の上の部分は「センサーの取り付け」「紙コップをとるためのモーターの機構」の組み立てを工夫した。(「ロボットの詳細」で説明する)
明るさを読み取るセンサーをロボットに取り付ける工夫として、紙に書かれたラインを正確に読み取れるように地面に対して垂直にセンサーを取り付けた。
また、センサーの位置をできるだけ前輪の車軸の近くにすることで急カーブでもスムーズに動作するロボットを作ることができた
紙コップをとる機構を作るうえでの工夫として、必ず紙コップが取れるように腕の幅を広くした。
プログラミングを作るうえでは「線の左を認識」「線の左を認識かつ交差点認識」「線の右かつ交差点認識」の3個のプログラミングに大きく分け、さらに交差点ごとに3個に分けたプログラミングのうち一個を使ってブルーチンを作りロボットが誤作動を起こしたとき、どのプログラミングに問題があるかを見つけやすくした。
幅が2cmの黒い線に光センサーをそれぞれの場所に当てた時に示す数値は下の図のようになる。
この光センサーが示した値をもとに「if」を用いてプログラムを書けばよい。
下の(「線の左側を認識」して進むプログラミング)を参照
交差点を曲がる、もしくは通過するとき、センサーは必ず濃い黒の部分を連続して通る。 この濃い黒を連続で通過する時間がある一定時間以上あるとき、そこは交差点であるとロボットに認識させることができる。(下図参照)
まずdefineで次の内容を定義する。
#define llt OnFwd(OUT_A,30); OnRev(OUT_C,30);//大きく左に回る #define lrt OnRev(OUT_A,30); OnFwd(OUT_C,30);//大きく右に回る #define ternleft OnFwd(OUT_A,50); OnFwd(OUT_C,5);//左に回る #define ternright OnFwd(OUT_A,5); OnFwd(OUT_C,50);//右に回る #define go OnFwd(OUT_A,30); OnFwd(OUT_C,30);//直進する #define LD OnFwd(OUT_B,30); Wait(400); Off(OUT_B);//コップを取る腕を下げる #define LU OnRev(OUT_B,30); Wait(400); Off(OUT_B);//コップを取る腕を上げる #define STEP 1;//whileの中のプログラミングを1/1000秒ごとに実行する
*「PB」の値は線の中央の濃い黒を連続で認識できる時間である。
例えば「PB<180」の場合濃い黒を連続で180/1000秒間以上認識されると、while内のプログラムは実行されなくなる。
*「PB=0」はセンサーが濃い黒以外を認識した場合PBの値を0に戻すという意味である。
task main () { SetSensorLight(S1); while(true) { if(SENSOR_1<30)//線の中央の濃い黒を認識したときは大きく左に回る { llt; } else if(SENSOR_1<40)//線の中央でない薄い黒を認識したとき左に回る { ternleft; } else if(SENSOR_1<45)//線と白の部分のちょうど境界線を認識したとき直進する { go; } else if(SENSOR_1<50)//真っ白ではないが白の部分を認識したとき右に回る { ternright; } else //真っ白の部分を認識したとき大きく右に回る { lrt; } Wait(STEP); } }
task main () { SetSensorLight(S1); int PB = 0; while(PB<180) //センサーが30以下の値を連続180/1000秒以上読み取らなければwhileは実行される { if(SENSOR_1<30) //線の中央の濃い黒を認識したときは大きく右に回る { lrt; PB++; } else if(SENSOR_1<40)//線の中央でない薄い黒を認識したとき右に回る { ternright; PB = 0; } else if(SENSOR_1<45)//線と白の部分のちょうど境界線を認識したとき直進する { go; PB = 0; } else if(SENSOR_1<50)//真っ白ではないが白の部分を認識したとき左に回る { ternleft; PB = 0; } else //真っ白の部分を認識したとき大きく左に回る { llt; PB = 0; } Wait(STEP); } Off(OUT_AC); PB = 0; }
task main () { SetSensorLight(S1); int PB = 0; while(PB<180)//センサーが30以下の値を連続180/1000秒以上読み取らなければwhileは実行される { if(SENSOR_1<30)//線の中央の濃い黒を認識したときは大きく左に回る { llt; PB++; } else if(SENSOR_1<40)//線の中央でない薄い黒を認識したとき左に回る { ternleft; PB = 0; } else if(SENSOR_1<45)//線と白の部分のちょうど境界線を認識したとき直進する { go; PB = 0; } else if(SENSOR_1<50)//真っ白ではないが白の部分を認識したとき右に回る { ternright; PB = 0; } else //真っ白の部分を認識したとき大きく右に回る { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); PB = 0; }
全体のプログラミングは「主に使用するプログラミング」で説明したプログラミングに、 次のライントレースのための初期移動を加えたもので構成されている。プログラミングはそれぞれの交差点ごとにサブルーチンを使い,それぞれ分けて書いた。
*プログラミングの説明の図では赤い矢印はロボットが前進した時に通るセンサーの軌跡、緑の矢印はロボットが後退した時に通るセンサーの軌跡である。
1.AB 2.BC 3.CFE 4.ER 5.RP 6.catch(紙コップを取る) 7.PQ 8.QS 9.SSA 10.release(紙コップを放す) 11.SSB 12.SF 13. FC 14. CD
AB間は「線の右側を認識、交差点を認識」して進むプログラムを使用している。
BC間のプログラムに移動するために右に少し曲がったロボットの軌道を[ternleft]を使い、修正した。
sub AB() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { lrt; PB++; } else if(SENSOR_1<40) { ternright; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternleft; PB = 0; } else { llt; PB = 0; } Wait(STEP); } ternleft;//少し左に曲がる Wait(200); PB = 0; }
BC間は「線の右側を認識、交差点を認識」して進むプログラムを使用している。
CFE間のプログラムに移動するために大きく右に回り、そのあと少し進み、線の左側にセンサーを移動させた。(図参照)
sub BC() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { lrt; PB++; } else if(SENSOR_1<40) { ternright; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternleft; PB = 0; } else { llt; PB = 0; } Wait(STEP); } Off(OUT_AC); lrt; //大きく左に回る Wait(600); go; //少し進む Wait(200); PB = 0; }
CFE間は「線の左側を認識」して進むプログラムを使用している。
交差点認識も含めたプログラミングを使うと急カーブの地点でセンサーが黒の上を長く移動するので交差点と認識してしまう、そこで2つの急カーブを通過するのに十分な時間(18秒)を指定し、その時間だけ「線の左を認識」するプログラムを使うことでライントレースしながらスムーズに急カーブを通過することができた。(図参照)
sub CFE() { SetSensorLight(S1); long t0 = CurrentTick(); while(CurrentTick()-t0 <= 18000)//18秒間だけ「線の左を認識」するプログラムでコースを進む { if(SENSOR_1<30) { llt; } else if(SENSOR_1<40) { ternleft; } else if(SENSOR_1<45) { go; } else if(SENSOR_1<50) { ternright; } else { lrt; } Wait(STEP); } }
ER間は「線の左側を認識、交差点を認識」して進むプログラムを使用している。
テスト走行の段階では、RP間のプログラムに移動するためにR地点で交差点を認識し、一時停止した後、大きく左に回るプログラムであったが、旋回後、センサーが線の左側にでるまでに時間を要するため、交差点と誤認識してしまっていた、そこでR地点で一時停止した後、大きく左に回り、そのあと少し進み、センサーが黒を判別するまで右に回るプログラムにすることによって誤認識を防いだ(図参照)
sub ER() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); Wait(1000); llt; Wait(800); go; Wait(400); while(SENSOR_1>45)//センサーが黒に反応するまで右に回る { ternright; } PB = 0; }
RP間は「線の左側を認識、交差点を認識」して進むプログラミングを使用している。
紙コップを取るプログラミングに移る前に交差点を先に超えるようにした。
交差点を超えるために左に少し曲がったロボットの軌道を[ternright]を使い、修正した。
sub RP() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } ternright;//少し右に回る Wait(300); PB = 0; }
紙コップを取るプログラムは単純で、その場で回転してアームを下すだけである。
腕の幅を大きくしたことで紙コップはとても取りやすくなった。
sub catch() { lrt; //その場で約90度右に回転 Wait(1200); Off(OUT_AC); LD; llt; Wait(1200); //その場で約90度左に回転し、元の位置に戻る Off(OUT_AC); }
PQ間は「線の左側を認識、交差点を認識」して進むプログラムを使用している。 QS間のプログラムに移るために左の車輪を後ろに回転した後、黒の線を十分に超える時間だけ直進し、さらにその後センサーが黒に反応するまで直進させた。(図参照)
sub PQ() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } PB = 0; OnRev(OUT_C,40);//左車輪を後ろに回す Wait(600); go; //黒の線を超えるように直進する Wait(600); while(SENSOR_1>45)//黒を認識するまで直進する { go; } }
QS間は「線の左側を認識、交差点を認識」して進むプログラムを使用している。
SSAのプログラムに移動するために、交差点に入った後、白を認識するまで直進させることで交差点を超えた。
sub QS() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); Wait(2000); while(SENSOR_1<50)//白を認識するまで直進する { go; } }
SSAは「線の左側を認識、交差点を認識」して進むプログラムを使用している。
sub SSA() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); PB = 0; }
紙コップを放した後、元のコースに戻るときにロボットが紙コップに接触しないように交差点Sからできるだけ遠い位置に紙コップを置くようにした。
プログラムはS交差点から、大きく左に回り、少し直進した後、右の車輪を後ろに回すことで元のコースに戻れるように工夫した。(図参照)
sub release() { llt; Wait(1500); go; Wait(900); Off(OUT_AC); LU; while(SENSOR_1>45) { OnRev(OUT_A,40); } }
SSB間は「線の左側を認識、交差点を認識」して進むプログラムを使用している。
紙コップを放した後、再びS交差点に向かう必要があったので新たにこのプログラムを追加した。
SFのプログラムに移動するために、左に少し曲がったロボットの軌道を[ternright]を使い、修正した。
sub SSB() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); Wait(1500); ternright;//少し右に回る Wait(300); PB = 0; }
SF間は「線の左側を認識、交差点を認識」して進むプログラムを使用している。
FC間のプログラムに移動するためにF交差点認識後、[ternright]を使い線の右側にセンサーが来るようプログラムを書いた。(図参照)
sub SF() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { llt; PB++; } else if(SENSOR_1<40) { ternleft; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternright; PB = 0; } else { lrt; PB = 0; } Wait(STEP); } Off(OUT_AC); Wait(1500); ternleft;//左に回り線の右側へ Wait(200); PB = 0; }
FC間は「線の右側を認識、交差点を認識」して進むプログラムを使用している。
Cの交差点を認識した後、Dのゴール枠に入る角度になるように右に大きく回るようにした。
sub FC() { SetSensorLight(S1); int PB = 0; while(PB<180) { if(SENSOR_1<30) { lrt; PB++; } else if(SENSOR_1<40) { ternright; PB = 0; } else if(SENSOR_1<45) { go; PB = 0; } else if(SENSOR_1<50) { ternleft; PB = 0; } else { llt; PB = 0; } Wait(STEP); } Off(OUT_AC); Wait(2000); lrt; //右に大きく回る Wait(1000); PB = 0; }
CD間のプログラムは直進するだけである。
sub CD() { go; Wait(2000); Off(OUT_AC); }
task main() { AB(); BC(); CFE(); ER(); RP(); catch(); PQ(); QS(); SSA(); release(); SSB(); SF(); FC(); CD(); }
今回使ったプログラムではif,currenttickなど新しく出てくるワードが多く、理解し使いこなせるようになるまで時間を要した。
課題1より難易度は大きく上がったと思った。
自分のレポートを書く量がおおすぎて驚いた。
全体をとおしてロボットがうまく動いたときは何にも代えがたい嬉しさがあった。
線をセンサーが感知するとき、紙のたわみによってセンサーが示す値が違ってくることから、おさえを付けるべきだった。
今回の課題では腕の上げ下げは一回ずつだったので影響はなかったが、複数回の場合、腕が下りてくる位置が変わるためそのための工夫が必要であると感じた。
コップを放す位置がずれて指定された範囲から少しはみ出てしまった。もう少し指定された範囲に置けるように手前においてもよかった。