ルート1を担当する。
「A地点をスタートしてQ付近の3/4円周上でピンポン玉をつかみ、そのピンポン玉をD地点においたゴールへシュートする
Aスタート → P直進 → Q直進 → (ピンポン玉をキャッチ) → Q直進 → R右折 → P直進 → S左折 → Dへシュート
ただし、
なるべく速く正確に動くロボットになるように工夫して、交差点では1秒間停止すること。
ピンポン玉はQ近くの3/4円周上ならどこにおいてもよい
可能であればC,Dのゴールエリアライン上からシュートする」
RISに比べれば巨大になった。本体のボタンは押しやすい。
光センサは紙面と垂直にギリギリまで接近させた。精度よく、閾値の設定が楽になった。
前輪は左右に激しく揺れるので滑りにくいゴムタイヤはつけなかった。ロボットが動き続けていると左右のモーターが八の字に開いて負荷が掛かってしまうので、本体から離れないよう補強した。
本体、モーターA、光センサ、超音波センサだけを取り出した写真。モーターAと光センサを本体の下、中心線上に置くためにロボットが高くなった。
左右のモーターが八の字に開かないよう後ろから横棒のパーツを足した。
超音波センサの真下にボールが来た時、モーターAのアームで枠をボールが落ちない高さまでおろしてつかむ。
アームが上がっているとき、超音波センサのすぐ前を棒が遮るのだが、センサの値には影響しなかったのでそのまま。センサが反応してアームが下ろせればほぼつかむことができた。
カーブで機体が揺れると失敗するので点Qを通過したあとすぐにボールをおいた。
・定義
#define turn_right OnRev(OUT_B,40); #define turn_right_slow OnRev(OUT_B,40); OnFwd(OUT_C,30); #define turn_left OnRev(OUT_C,40); #define turn_left_slow OnRev(OUT_C,40); OnFwd(OUT_B,30); #define go OnRev(OUT_BC,40); #define MAX_TIME 500
40%のスピードが最もコースから外れにくく、交差点の誤認識が少ないとわかった。
交差点で進路を確かめるために旋回したり、機体の向きを整えるプログラムは作らなかったので、機体の向きが甘い変わらないよう交差点を認識する時間MAX_TIMEは最小(0.5秒)に抑えた。
sub FOLLOW_LINE() { long t0=CurrentTick(); while(CurrentTick()-t0<MAX_TIME){ if(SENSOR_1<=42){ //線上黒なら右折 turn_right; }else if(SENSOR_1<=48){ turn_right_slow; t0=CurrentTick(); }else if(SENSOR_1<=58){ //丁度黒線と白紙の境界線上なら直進 go; t0=CurrentTick(); }else if(SENSOR_1<=65){ turn_left_slow; t0=CurrentTick(); }else{ turn_left; //線外白なら左折 t0=CurrentTick(); } } Off(OUT_BC); PlaySound(SOUND_UP); //停止した時=交差点認識した時ピロリンと音がなる Wait(2000); }
交差点でない限り、ロボットが黒線の左側を追って走り続けるプログラム。
・QR間の急なカーブを曲がる
授業の発表時、FOLLOW_LINEではQR間の急なカーブを曲がりきれなかった。(R=4のカーブで機体の幅が広いが右タイヤが動かず、線は辿れるものの機体の中心がコースから大きく外れてしまった。またモーターに負担をかけていた)
なのでこの区間だけ小刻みにタイヤを動かしてしっかり線上を進むような定義とプログラムを追加した。
#define kokizami_right OnRev(OUT_B,50); OnFwd(OUT_C,40); #define kokizami_left OnRev(OUT_C,50); OnFwd(OUT_B,40);
sub FOLLOW_LINE_KOKIZAMI() { long t0=CurrentTick(); while(CurrentTick()-t0<MAX_TIME){ if(SENSOR_1<=42){ kokizami_right; //変更 }else if(SENSOR_1<=48){ turn_right_slow; t0=CurrentTick(); }else if(SENSOR_1<=58){ go; t0=CurrentTick(); }else if(SENSOR_1<=65){ turn_left_slow; t0=CurrentTick(); }else{ kokizami_left; //変更 t0=CurrentTick(); } } Off(OUT_BC); PlaySound(SOUND_UP); Wait(2000); }
FOLLOW_LINEとほとんど変更していないが、QR間を辿れるようになった。
おおよその動きは以下のGIFを参照
sub USETSU() { OnFwd(OUT_BC,50); Wait(500); Off(OUT_BC); OnRev(OUT_C,50); Wait(1500); Off(OUT_C); }
点Rで右折する。Rで交差点認識した時、機体は左寄りに進みすぎているので少しバックしてから曲がる。
sub SASETSU() { OnFwd(OUT_BC,50); Wait(500); Off(OUT_BC); OnRev(OUT_B,50); Wait(1500); Off(OUT_B); }
点Sで左折する。Sで交差点認識した時、機体は進みすぎているので(線の右側に出てしまう)少しバックしてから曲がる。
sub CROSS() { go; Wait(800); Off(OUT_BC); }
点P.Qで2回ずつ交差点を直進する。垂直の線の幅をまっすぐ通り抜けてしまえば、白紙の上でFOLLOW_LINEに従いロボットは右折しようとして進むべき線に戻ることができる。
sub CATCH_BALL() { go; until (abs(SensorUS(S2)-13)>=4); if(abs(SensorUS(S2)-13)>=4){ Off(OUT_BC); //ボールを認識したら止まる OnRev(OUT_A,30); Wait(500); Off(OUT_A); //アームを下ろす } }
点Qを過ぎてからはFOLLOW_LINEではガタガタしてボールを認識しにくいのでgoで進んでいる。
平常時のSensorUS(S2)の値は13cm、ボールが真下に来た時は8cm。だが球体との距離を図るのは難しいので、ボールに近づいた時は5cm~24cm程度に値が振れる。
なので絶対値を計算する関数abs()で平常時の13cmから4cm前後変化した時ボールがあると判断するようにさせた。
sub GOAL() { OnFwd(OUT_A,30); Wait(300); Off(OUT_A); //Aのアームを上げる OnFwd(OUT_BC,40); Wait(800); Off(OUT_BC); //少しロボットを後退させる OnRev(OUT_A,30); Wait(500); Off(OUT_A); //Aのアームを下げる OnRev(OUT_BC,60); Wait(500); Off(OUT_BC); //ロボットを勢い良く走らせアームのバーにぶつける。シュート。 }
発表時は距離が足りずボールに当たらなかった。 紙の上でボールの位置が安定しないのでこのアームの形だと難しい。
task main() { SetSensorLight(S1); SetSensorLowspeed(S2); //光センサ、超音波センサを設定 FOLLOW_LINE(); CROSS(); //直進P FOLLOW_LINE(); CROSS(); //直進Q CATCH_BALL(); //ボールをキャッチ FOLLOW_LINE(); CROSS(); //直進Q FOLLOW_LINE_KOKIZAMI(); USETSU(); //右折R FOLLOW_LINE(); CROSS(); //直進P FOLLOW_LINE(); SASETSU(); //左折S FOLLOW_LINE(); GOAL(); //ゴール }
全ての動作を部品化したのでとても見やすい。メンテナンスもしやすい。
・初めてスタートからゴールまで走りきってくれた。
・QR間が曲がりきれなかった。→解決した
・ボールをゴールにシュートできなかった。→不可能ではないが、成功率はかなり低い
・前回の反省(プログラムしやすいようにロボットを組み立てる)を実行できた。次回もこれに続きたい。
・一つの動作が終わった時に音や画面表示などサインを出して、プログラムのミスやロボットの不調を見つけやすくなった。
・閾値の設定やFOLLOW_LINEの定義の値はまだ改善の余地があるのでは?ロボットごとにある誤差をもう少し実験したほうがよいと思われる。
やっとNXTに慣れてきたと感じた。それでもわからないことはたくさんあったので相方と他のいろんな班の人に教えてもらった。ありがとう。