目次
A地点から出発 → B → C(直進) → D(一時停止の後、直進) → E → F → G(一時停止の後、右折) → H → I → J(右折) → K(左折) → L(ピンポン玉をつかむ) → K(直進) → M(一時停止) → シュート→ A地点に入る(ゴール)
今回のロボットは,マニュアルに載っている基本の型をベースに光センサーやボールを捉える機構を加え,全体的にバランスが良くなるように工夫した.
↑機体を前から撮影した写真
↑機体を後ろから撮影した写真
明るさを読み取るための光センサーをロボットに取り付ける際の工夫として,紙に書かれたラインを正確に読み取るために紙に対して垂直にセンサーを取り付けた.
また,センサーの取り付け位置をタイヤの軸にできるだけ近づける事によって旋回した際のセンサーの振り幅を小さくし,センサーによる判断をより繊細にできるようにした.
ボールを捉える機構の工夫として,多少ずれたとしても捉えられるよう大きめの囲むような形状のアームを上から下ろす方法をとった.(機体を前から撮影した写真を参照)
今回作ったプログラムではライントレースする際判定をより繊細にしなめらかに動作するように ,センサーの値を5段階で判定した.
まずNXTのモニター機能でセンサーが完全に黒の線上にある時と完全に白の紙の上にあるときの数値を計測し,それぞれをblack・whiteと定義する.これにより,black以下の数値のときは線上,white以上の数値のときは線外,間の数値のときは線と紙の境目の灰色のところにセンサーがあることになる.
次に,bkackから+5の値までを黒寄りの灰色,whiteから−5の値までを白よりの灰色と定義することによって,5段階の判定が可能となった.
まずdefineで次のように定義した
#define black 30 //完全に黒のときの値 #define white 50 //完全に白のときの値 #define speedH 30 //速いスピード #define speedL 10 //遅いスピード #define speedM 5 //逆回転のときのスピード #define catch OnFwd(OUT_A,10);Wait(1300); //アームを下げボールをキャッチする動作 #define release OnRev(OUT_A,10);Wait(1300); //アームを上げボールを離す動作 #define mae OnFwd(OUT_BC,speedH); //前進の動作 #define turnRRR OnFwd(OUT_C,speedH);OnRev(OUT_B,speedM);//一番きつい右曲がり #define turnLLL OnFwd(OUT_C,speedH);ONRev(OUT_C,speedM);//一番きつい左曲がり #define turnRR OnFwd(OUT_C,speedH);Off(OUT_B); //右曲がり #define turnLL OnFwd(OUT_B,speedH);Off(OUT_C); //左曲がり #define turnR OnFwd(OUT_C,speedH);OnFwd(OUT_B,speedL); //緩めの右曲がり #define turnL OnFwd(OUT_B,speedH);OnFwd(OUT_C,speedL); //緩めの左曲がり #define step 1 //ライントレース一つの動作時間 #define jikan 100 //完全に黒で居続けられる時間の最大値(しきい値) #define teishi Off(OUT_BC);Wait(300); //一時停止
このプログラムではラインの右側をトレースしている.
トレースをする際黒のライン上にいるときは右に曲がるように設定しているが,交差点に差し掛かったとき黒であるとロボットが判断している時間が普通のライントレースをしているときより長いことを利用し,黒である時間のしきい値を定め,右曲がりしている時間がその値より長ければ交差点,短ければ普通のライントレースの最中であると定めた.
CurrentTickを使うこととによって完全に黒い線上にいる時間を測り,その値がjikanで定義したしきい値を超えた場合それを交差点と判断しwhileループから抜け出し次のプログラムに行ける様になっている.
ここでは黒でいる時間の上限をt_maxとし,そこにjikanの値を代入してしきい値の役割を与えている.
void linetraceS() //完全に黒がjikan以上だったら外れる { long t0=0,t_black=0,t_max=jikan; t0=CurrentTick(); while(t_black<t_max){ //t_blackがt_maxの値より大きくなったらループから抜ける if(SENSOR_3>=white){ //完全に白のとき turnLL; //左曲がり } else if(SENSOR_3>white-5){ //白よりの灰色のとき turnL; //緩い左曲がり } else if(SENSOR_3>black+5){ //灰色のとき mae; // 前進 } else if(SENSOR_3>=black){ //黒寄りの灰色のとき turnR; //緩い右曲がり } else{ //完全に黒のとき turnRR; //右曲がり t0=CurrentTick(); //現在時間をt0とする(時間を測り始める) until((SENSOR_3<black+5)&&(SENSOR_3>black)); //黒寄りの灰色になるまで測り続ける t_black=CurrentTick()-t0; //現在時刻からt0の値を引いたもの(右曲がりしていた時間)をt_blackとする } Wait(step); } }
交差点の判断は直進のときと同様の方法で行っている.
基本的には直線のときと同じ条件での構成になるが,今回制作したロボットの旋回半径がコースのカーブの半径より大きくなってしまい,内側のタイヤを逆回転する必要が出てきたが,それを直線でも行ってしまうと動作がなめらかでなくなってしまうので,カーブのときのライントレースは直線のときとは別のサブルーチンを使用した.
void linetraceC() //完全に黒がjikan以上だったら外れる { long t0=0,t_black=0,t_max=jikan; t0=CurrentTick(); while(t_black<t_max){ //t_blackがt_maxの値より大きくなったらループから抜ける if(SENSOR_3>=white){ //完全に白のとき turnLLL; //キツい左曲がり } else if(SENSOR_3>white-5){ //白よりの灰色のとき turnLL; //左曲がり } else if(SENSOR_3>black+5){ //灰色のとき mae; //前進 } else if(SENSOR_3>=black){ //黒寄りの灰色のとき turnRR; //右曲がり } else{ //完全に黒のとき turnRRR; //キツい右曲がり t0=CurrentTick(); //現在時間をt0とする(時間を測り始める) until((SENSOR_3<black+5)&&(SENSOR_3>black)); //黒寄りの灰色になるまで測り続ける t_black=CurrentTick()-t0; //現在時刻からt0の値を引いたもの(右曲がりしていた時間)をt_blackとする } Wait(step); } }
前に示した2つのサブルーチンでは,完全に黒でいる時間が長いとき交差点と判断しループから抜けている.つまり交差点と判定された瞬間は線上で進行方向に対し斜め右を向いていることになる.
その状況を突破するために,まず白よりの灰色になるまで前進しその後黒寄りの灰色になるまで左曲がりを続けるというプログラムを組んだ.
void crossline() { mae; //前進 until(SENSOR_3>white-5); //白よりの灰色になるまで turnLL: //左曲がり until(SENSOR_3<black); //完全に黒になるまで }
・右の曲がるときのサブルーチン
このときもやはり斜め右を向いた状態から始まるので,そのまま完全に白になるまで回転して突破する.
void Rcurve() { OnFwd(OUT_C,20);OnRev(OUT_B,10); //回転 until(SENSOR_3>white); //完全に白になるまで }
・左に曲がるときのサブルーチン
このときも同様に右斜め前を向いて始まるので,そこから完全に白になるまでキツめに左に曲がり突破する.
void Lcurve() { turnLLL; //キツい左曲がり until(SENSOR_3>white); //完全に白になるまで }
課題のコース上Lにてボールを回収しゴールに向かう際回転するサブルーチン
ボール回収後,ラインから外れるために一度前進しその後ラインに近づくまで回転することによって,ゴールに向けての方向転換を実現した.
void kaiten() { mae; //前進 Wait(900); //0.9秒間 OnFwd(OUT_B,20);OnRev(OUT_C,10); //回転 until(SENSOR_3<black+5); //黒寄りの灰色になるまで }
今回自分はコースを時計回りする動きをしなくてはいけなかったので,途中線の多い右側をトレースし交差点判定させることで直角カーブなどを突破した.
各行の後ろのアルファベットは,コースに振り分けられたものと対応している.
BtoCはBからCの間のプログラムであることを表している.
task main() { SetSensorLight(S3); linetraceS(); //BtoC crossline(); //C linetraceC(); //CtoD teishi; //D crossline(); //D linetraceS(); //DtoE Rcurve(); //E linetraceS(); //EtoF Rcurve(); //F linetraceS(); //FtoG teishi; //G Rcurve(); //G linetraceC(); //GtoH crossline(); //H linetraceC(); //HtoI crossline(); //I linetraceC(); //ItoJ Rcurve(); //J linetraceS(); //JtoK Lcurve(); //K linetraceS(); //KtoL Wait(2900); //KtoL catch; //Lでボールを取る kaiten(); //L linetraceS(); //LtoK crossline(); //K linetraceC(); //KtoM teishi; //M mae; //M Wait(1000); //10秒間 release; //ゴールでボールを離す }
CurrentTickなどを使った定義に手こずったが,結果ちゃんと動くプログラムを完成させられてよかった.
コース上L地点でボールを回収するところで線の端で止まるプログラムを作る際,センサーの判定だけでとまるアイディアが思いつかずWaitに頼ってしまったので,次回からはうまくできるよう考えたい.
カーブの際,内側のタイヤを逆回転させなくてはいけないのですこしぎこちない動きになってしまったので,次はタイヤの旋回の半径を小さくする努力をしたい.