下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成せよ。
1,Aをスタート
2,Bを直進
3,Cで一時停止の後、直進
4,Dで一時停止の後、Xの空き缶をキャッチしてD地点に戻る
5,DからEに向かい、Eを直進
6,Fを左折
7,Gで一時停止の後、左折
8,Hで一時停止の後、右折
9,Iで一時停止の後、右折
10,Lを直進
11,Kを直進
12,Jで一時停止の後、空き缶をYに置きてJに戻りBに向かう
13,Bで一時停止の後、左折
14,Aで停止
ロボットはなるべく前回の形をヒントにあまり変えないようにして作るように心掛けた。
最初はアームを上げ下げして缶を動かす仕組みではなく左右からアームで挟んで持ち上げて運ぼうとしたが、缶が滑り落ちてしまったりジャストで缶の場所へたどり着かなければ缶をつかめないと思ったのでアームを下す式にした。
アームが缶の大きさに対して非常に大きなっており、これは多少プログラムの誤差が出てしまった時でもちゃんと缶を捉えられるようにするためである。また、アームは腕の肩と肘のように二段階で折れ曲がるようになっているので缶が機体とともに動いているときにアームの外へ出てしまうのを防いでいる。アームの上げ下げは課題1の時と同じように車輪のモーターとは別のモーターを搭載し、モーターの動きによって行われている。一つのモーターからどのようにうでを2本取り付けたかというと、モーターを2本の腕で挟み込むようにしたのでとりつけられた。2本であるのは1本よりもブレがなく安定し、支えやすいからである。
そして、機体に必要な部品を前面に出さないことで部品に空き缶があたって場所がずれるといったことを防止した。
写真を見てみると課題1のときと同じような背面となっている。2つの車輪を自動車のように棒状の部品でつなぎ合わせることで車輪がぐらつくことを防ぎ、重い上部を支えられるようにした。棒状の部品を使うにあたって2つの車輪の距離感をうまく作り出した。
前面の説明で述べたように腕の可動部が2つあることがわかる。
写真から見て取れるが、機体が若干前へ傾いているのは重心が後ろに寄ってしまったのでそれを前へなるべく移動させるのと上部のおもさを後ろの小さい2つの車輪にかけるのは部品故障や後ろへ転倒することを招きかねないのでそれを防止するための策として、光センサーをゆかに限りなく近づけるためにわざとしてある。前へ傾ける小さな一工夫でとてもたくさんの恩恵を受けられた。
センサーと床が限りなく近くなっている。これは、遠くなればなるほど外界からの光によって黒を正確に判定できなくなってしまうのを防ぐためにしてある。
光センサーを指示をだす部品から近くしてあるのは機体がプログラム上左右に動いた際にどうしても発生してしまう揺れによってセンサーも揺れてしまうのをなるべく少なくするために重心となる部分に近くする必要があったからだ。
センサーはレゴの突起にはめることができる棒状の部品を使い重心から地面へと伸ばしている。
#define turnL OnFwd(OUT_A,30); OnFwd(OUT_B,-20); //右に曲がる #define turnR OnFwd(OUT_A,-20); OnFwd(OUT_B,30); //左に曲がる #define armUP OnFwd(OUT_C,-20); Wait(1500); Off(OUT_C); //アームを上げる #define armDW OnFwd(OUT_C,20); Wait(1500); Off(OUT_C); //アームを下げる
左、右に曲がるプログラムはトレースするときに頻繁に使用するので定義して簡略化した。
アームの上げ下げはプログラムをわかりやすくするために導入した。
task main() { SetSensorLight(S2); long t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Bを直進 } } Off(OUT_AB); Wait(1000); OnFwd(OUT_AB,20); Wait(500); Off(OUT_AB); //Cで一時停止後直進 t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Dの直前の角までラインをトレース } } RotateMotorEx(OUT_AB,-50,150,100,true,false); Off(OUT_AB); //Dの直前の角をターン t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Dまでラインをトレース } } Off(OUT_AB); Wait(1000); //Dで一時停止 armDW; //Xの空き缶をキャッチ OnFwd(OUT_AB,-20); Wait(300); Off(OUT_AB); //バックしてライン上へ戻る t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Eまでラインをトレース } } OnFwd(OUT_AB,20); Wait(500); Off(OUT_AB); //Eを直進 t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Fまでラインをトレース } } OnFwd(OUT_A,20); Wait(500); Off(OUT_A); //Fを左折 t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Gまでラインをトレース } } Off(OUT_AB); Wait(1000); //Gで一時停止 OnFwd(OUT_A,20); Wait(500); Off(OUT_A); //Gを左折 t0 = CurrentTick(); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Hまでラインをトレース } } Off(OUT_AB); Wait(1000); //Hで一時停止 OnFwd(OUT_B,20); Wait(750); Off(OUT_B); //Hで右折 while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Iまでラインをトレース } } OnFwd(OUT_B,20); Wait(750); Off(OUT_B); //Iで右折 while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnR; } else { turnL; t0 = CurrentTick(); //Lまでラインをトレース } } OnFwd(OUT_AB,20); Wait(500); //Lを直進 while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnR; } else { turnL; t0 = CurrentTick(); //Kまでラインをトレース } } OnFwd(OUT_AB,20); Wait(500); //Kを直進 while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnR; } else { turnL; t0 = CurrentTick(); //Jまでラインをトレース } } Off(OUT_AB); Wait(1000); //Jで一時停止 OnFwd(OUT_A,20); Wait(1000); Off(OUT_A); armUP //Yに空き缶を置く OnFwd(OUT_A,20); Wait(1000); Off(OUT_A); while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Bまでラインをトレース } } Off(OUT_AB); Wait(1000); //Bで一時停止 RotateMotorEx(OUT_AB,-50,150,100,true,false); Off(OUT_AB); //Bを左折 while (CurrentTick()-t0 <= 300) { if (SENSOR_2 < 40){ turnL; } else { turnR; t0 = CurrentTick(); //Aまでラインをトレース } } Off(OUT_AB); //Aで停止 }
トレースの仕方だが、他の班の多くが白、黒、グレー、・・・といったように3色以上で判定するようにしたプログラムにしてあるがこのプログラムにおいては白、黒の2色のみで判定するようにしてある。この理由だが、2色でもトレースできたのもそうだが、プログラムの微調整にかけられる時間をなるべく長くするために早くプログラムを書き終えたかったからである。3色以上の判定のほうが機体は素早くうごき、正確な判定をすることが可能のようだが課題1の教訓でプログラムの調整のほうがこのときは大事であったためこうした。
場合によって違うが白なら右、黒なら左に曲がるようにプログラムして動くスピードは遅いがそのおかげで急カーブも難なくトレースできたため結果的によかった。
交差点の判定の仕方は黒の判定所間をはかり0.3秒よりも長いときは交差点と判定している。逆にその時間未満であれば交差点ではないと判断し黒い部分をそのままトレースする。また、交差点と曲がり角の判定は不可能であったため、何回目に曲がり角になるかを数えてそこで交差点判定をしたら機体にターンをするように指示をだすようにした。また、詳しくは以下のようになる。
while (CurrentTick()-t0 <= 300) { //while内において0.3秒以下の時に実行される。 if (SENSOR_2 < 40){ //線の明るさが40以下の時左に曲がる turnL; } else { //そうでなければ turnR; //右にまがる t0 = CurrentTick(); //はかっている時間をリセット } }
あとから思ったのが、トレースにつかうwhileから始まるプログラムも定義してしまえばより簡潔でわかりやすいプログラムになった。
良くも悪くも左右の動きを細かくしたため急な曲がり角も難なくトレースできたが動きが遅いので一回あたりの試行に多くの時間が費やされてしまった。
また、アームを大きくしたにも関わらず機体がぶれてしまって缶をうまくつかめないときが多くあった。
トレース自体は同じプログラムの繰り返しだったためにそこまで大変ではなかったが缶を掴んだり曲がるときの微調整がとても大変だったためそこで苦戦してしまった。時間をとったと思っていたがまだ全然足りなかったようで缶をとるような細かい1つ1つの動作の詰めの甘さが出てしまったので課題3ではより多くの時間をさけるように授業内での作業スピードをあげ、課外でなるべく集まる時間を多くしたい。
トレースの判定を2色でしてみたが、それによってプログラムが楽になるなどの恩恵はあったものの1回3色以上の判定も試してみてどっちがいいか吟味するのも必要だったかもしれないと後悔したので微調整の試行回数を増やすだけでなく、元の主となるプログラムの試行回数も増やすよう。