今回の課題はライントレースである. "AからD"のコースと"BからC"のコースが選べるので, 私は"AからD"のコースを選択した.
"AからDのコースでは, A(スタート)->P->Q->(ボール捕獲)->Q->R->P->S->D(ゴール)の順に通過する.
今回は光センサーを"ひとつだけ"使用してライントレースを行う必要がある. そのため, 黒線の左側の境界を利用する.
もしセンサーが"黒線上"と判断した時は, 進行方向を左に修正する. 一方センサーが"黒線上でない"と判断した時は, 進行方向を右に修正する. センサーが"黒線との境界"と判断した時には直進する.
ただしこの方法では交差点を検出できない(左屈折と判断して左折してしまう).
しかし, 光センサーが交差点を通過するとき, 光センサーは一定時間以上"黒線上"と判断し続けるはずである. そこで, 光センサーがX秒以上"黒線上"と判断し続けた場合に限って, カーブではなく交差点であると判断するようにする.
今回のローバーは, mindstorm付属のテキストに紹介されている基本的なローバーロボットを改良(小型化及び部品点数の削減)し,
をフロントにセットした.
NXT本体, ボール捕獲用アームがそれぞれタイヤ駆動用モーター, 前輪上にあるので安定している.
光センサーは紙面すれすれに設置することもできたが, 紙面との間に隙間を設けたほうがより安定した動作をすることが判明したため, あえて紙面から離している.
黒線の, 左側の境界をなぞっていくので, 光センサーはフロント左側に設置した.
フロント部分にモーターユニットを設置する余裕がなかったので, 最初はモーターをローバーの上部分に設置するつもりだったが, そうするとNXT本体を設置する場所がなくなってしまう. そこで, モーターを縦方向に設置した. 重心が上がってしまうが, 上記の通り前輪上に重心があるため特に問題はなかった.
むしろ, 前輪のグリップ力が上がることで, 他のチームにあった「駆動輪が浮いてしまう」現象をある程度防ぐことができたようである.
以下がコードである.
#define SPEED 30 #define go_forward OnFwd(OUT_BC, SPEED); #define l_turn OnFwd(OUT_B, SPEED); Off(OUT_C); #define r_turn OnFwd(OUT_C, SPEED); Off(OUT_B); #define timer_r t0 = CurrentTick(); /* OUT_A -> ハンドル制御 OUT_B -> タイヤ(右) OUT_C -> タイヤ(左) SNS_4 -> 光センサー(ライントレース) */ //交差点直進 sub CROSS_GO() { RotateMotor(OUT_B, SPEED, -150); go_forward; Wait(500); } //交差点右折 sub CROSS_R() { RotateMotor(OUT_B, SPEED, -300); go_forward; Wait(900); } //交差点左折 sub CROSS_L() { RotateMotor(OUT_B, SPEED, 90); } //ボールを捕獲 sub BALLGET() { go_forward; Wait(700); Off(OUT_BC); Wait(500); RotateMotor(OUT_A, SPEED, -50); Off(OUT_ABC); Wait(500); } //ボールを射出 sub BALLTHROW() { RotateMotor(OUT_B, SPEED, -170); RotateMotor(OUT_A, SPEED, 50); Off(OUT_ABC); Wait(500); OnFwd(OUT_BC, SPEED + 30); Wait(200); Off(OUT_BC); } task main() { SetSensorLight(S4); long t0 = CurrentTick(); //交差点を数える int cross = 0; while(cross <= 7) { if (CurrentTick() - t0 < 2000) { if(SENSOR_4 > 48) { r_turn; timer_r; } else if(SENSOR_4 > 42) { go_forward; timer_r; } else { l_turn; } } //交差点とゴール else { Off(OUT_BC); Wait(1000); if ((cross == 0) || (cross == 2) || (cross == 5)) { /* 交差点を直進 */ CROSS_GO(); } else if (cross == 1) { /* 交差点を直進後,ボールを捕獲 */ CROSS_GO(); BALLGET(); } else if (cross == 3) { /* ヘアピンカーブ */ RotateMotor(OUT_B, SPEED, 100); } else if (cross == 4) { /* 交差点右折 */ CROSS_R(); } else if (cross == 6) { /* 交差点左折 */ CROSS_L(); } else if (cross == 7) { /* ボール射出 */ BALLTHROW(); } cross ++; timer_r; } } }
以下に詳細を説明する.
/* OUT_A -> ハンドル制御 OUT_B -> タイヤ(右) OUT_C -> タイヤ(左) SNS_4 -> 光センサー(ライントレース) */
これは, それぞれのモーターの出力とセンサーの対応を示している(コメント).
#define SPEED 30 #define go_forward OnFwd(OUT_BC, SPEED); #define l_turn OnFwd(OUT_B, SPEED); Off(OUT_C); #define r_turn OnFwd(OUT_C, SPEED); Off(OUT_B); #define timer_r t0 = CurrentTick();
これ以外の複雑な動作(交差点の進行, ボールの捕獲・射出など)はsubroutineを利用した.
//交差点直進 sub CROSS_GO() { RotateMotor(OUT_B, SPEED, -150); go_forward; Wait(500); } //交差点右折 sub CROSS_R() { RotateMotor(OUT_B, SPEED, -300); go_forward; Wait(900); } //交差点左折 sub CROSS_L() { RotateMotor(OUT_B, SPEED, 90); } //ボールを捕獲 sub BALLGET() { go_forward; Wait(700); Off(OUT_BC); Wait(500); RotateMotor(OUT_A, SPEED, -50); Off(OUT_ABC); Wait(500); } //ボールを射出 sub BALLTHROW() { RotateMotor(OUT_B, SPEED, -170); RotateMotor(OUT_A, SPEED, 50); Off(OUT_ABC); Wait(500); OnFwd(OUT_BC, SPEED + 30); Wait(200); Off(OUT_BC); }
ライントレースを一旦中止し, 進行方向を修正し, ある程度直進する. その後またライントレースを再開する.
ボールは交差点Qの10cm先にあるので, 交差点Qを通過後10cm進んでから, アームをおろす.
射出時は進行方向を修正し, アームをあげて, ローバーを急発進させ, その勢いでボールを射出する.
task main() {
光センサーとそのポートを定義.
SetSensorLight(S4);
交差点の判断にタイマーを利用する. ここでlong型のt0に現在のタイマーの値を代入しておく.
long t0 = CurrentTick();
通過する交差点が何番目の交差点なのかを判断する. 交差点(ヘアピンカーブとゴールを含む)を通過するたびにcrossの値を増やしていく.
//交差点を数える int cross = 0;
crossが8になった時(=すべての交差点, ヘアピンカーブ, ゴールを通過した時), プログラムは終了する.
while(cross <= 7) {
光センサーの値が
if (CurrentTick() - t0 < 2000) { if(SENSOR_4 > 48) { r_turn; timer_r; } else if(SENSOR_4 > 42) { go_forward; timer_r; } else { l_turn; } }
タイマーを利用し, "左へ進路変更"が2秒間続くと交差点(ヘアピンカーブ, ゴール)と判断し, ライントレースを中断する. これらについて,
である. 各動作のあとにcrossの値を増やし, タイマーをリセットする. また, 課題では,交差点で1秒間停止することになっているので, 交差点と判断した時点で1秒間停止する.
//交差点とゴール else { Off(OUT_BC); Wait(1000); if ((cross == 0) || (cross == 2) || (cross == 5)) { /* 交差点を直進 */ CROSS_GO(); } else if (cross == 1) { /* 交差点を直進後,ボールを捕獲 */ CROSS_GO(); BALLGET(); } else if (cross == 3) { /* ヘアピンカーブ */ RotateMotor(OUT_B, SPEED, 100); } else if (cross == 4) { /* 交差点右折 */ CROSS_R(); } else if (cross == 6) { /* 交差点左折 */ CROSS_L(); } else if (cross == 7) { /* ボール射出 */ BALLTHROW(); } cross ++; timer_r; } } }
ローバー自体の設計が遅れてしまい, それが原因で16日の発表に間に合わせることができなかった. 次の, 課題3ではそのようなことが起こらないようにしたい. また, 設置する場所が作れなかったため, 超音波センサーをのせることができなかった. 結果としてはタイマーを使うことでボールの位置を特定したが, 次回はそのような妥協をしないようにしたい.
一方で, ロボットやプログラム自体は当初思っていたよりもしっかりとしたものを作ることができたので, その点は嬉しかった.