#contents *ハード設計 [#u0e00523] **形状 [#k00cbc2b] 今回は二輪車型を採用した。ラインという道路を走るので、この形状が一番適しているのはいうまでもない。 **アーム [#l05776ac] ボールをうまく掴むために、今回は以下のような機構を用意した。~ &ref(A.jpeg);~ 左右両側から覆うような構造にしたため、ボールに到達した時完全にボール正面に位置しなくても、ボールを捉えやすい。 この機構は、ボールを離した状態ではボールを弾いてキックすることもできる。~ &ref(B.jpeg); **工夫点 [#td425d48] +本体とラインセンサーの距離を「近くに置いた」。~ 実はこの距離には加減が重要で、前回私が作成した文字を書くロボットのペン先の位置と同様に回転軸の中心にラインセンサーを置いたのでは、プログラム設計上の問題が生じる。プログラムを計画通りに(プログラム-方針の項目参照)動かすには、回転軸中心の少し前方にラインセンサーを置かなくてはならない。 +比重の高いギアを採用した。~ プログラムを作成していくうちに、一連の動きを「早く」終了するためには、「速い動き」ではなく、「正確な動き」である必要があるとわかった。言い換えれば、安定性のために低速での動作を選んだということである。~ 因みに、このおかげで一層タイヤとラインセンサーの位置が近くなった。~ &ref(C.jpeg); *プログラム [#w34c4c3b] **方針[#ob326ed2] ラインセンサーを用いれば、センサー先の色の濃さが判別できる。これを用いて、黒色だったら線の外側へ斜め前方向に、白色だったら線の内側に斜め前方向に移動することができれば、線の「縁」をたどることができる。~ **定義 [#g5dc48a2] +方針を達成するために、黒、黒よりのグレー、グレーよりの白、白、の4つの境界を用意した。 #define BLACK 37 #define BLACKandGLAY 45 #define GLAYandWHITE 55 #define WHITE 60 (数値は調整済み) + 次に、効率よくプログラムを作るため、順に、直進、右/左斜め前へ直進、右/左方向へ回転する動作。その他にも小休止、(使うことはなかったが)後退を定義した。 #define go OnRev(OUT_BC,35); #define turn_leftFwd OnRev(OUT_B,35);Off(OUT_C); #define turn_rightFwd Off(OUT_B);OnRev(OUT_C,35); #define turn_left OnRev(OUT_B,35);OnFwd(OUT_C,35); #define turn_right OnFwd(OUT_B,35);OnRev(OUT_C,35); #define short_break Off(OUT_BC);Wait(1000); #define back OnFwd(OUT_BC,35); ※35というパワーは、正確さを失わずに全行程を終了させることのできる最高の速さとして調整した。 +次に方針に従ってラインをトレースするサブルーチンをを左端、右端それぞれについて作った。~ 継続して黒にいる時間が150×10^-3秒経過したとき、サブルーチンを終了するようにした。~ (実際にはサブルーチン開始後即その時の時刻をt0として定義し、while文を用いて「以降の時々の時刻-t0」が150×10^-3より小さい時はトレースを実行としている。黒の時だけタイマーが効くのは、黒以外の時はそれぞれの動作の直前にt0をその時の時刻として定義し直しているからである。)~ sub Rside_trace() { long t0 = CurrentTick(); while(CurrentTick()-t0<150) { if (SENSOR_3 < BLACK) { turn_right; } else if (SENSOR_3 < BLACKandGLAY) { t0=CurrentTick();turn_rightFwd; } else if (SENSOR_3 < GLAYandWHITE) { t0=CurrentTick();go; } else if (SENSOR_3 < WHITE) { t0=CurrentTick();turn_leftFwd; } else { turn_left;t0=CurrentTick(); }} Off(OUT_BC); } sub Lside_trace() { long t0 = CurrentTick(); while(CurrentTick()-t0<150) { if (SENSOR_3 < BLACK) { turn_left; } else if (SENSOR_3 < BLACKandGLAY) { t0=CurrentTick();turn_leftFwd; } else if (SENSOR_3 < GLAYandWHITE) { t0=CurrentTick();go; } else if (SENSOR_3 < WHITE) { t0=CurrentTick();turn_rightFwd; } else { turn_right;t0=CurrentTick(); }} Off(OUT_BC); } ※このサブルーチンの説明~ &ref(D.jpeg);~ ※このサブルーチンでは、設計で述べたラインセンサーと動力モータの絶妙な位置関係が生きる。というのも、極端な色(白や黒)での軌道修正は純粋な回転(モーターを左右逆に動かす)を採用している。これは交差点を渡った後やボールをつかんだあとの方向修正にとても有用だからである。仮にラインセンサーが回転軸中心にあったのではこの回転では全くラインセンサーの値は全く変化しない。逆に少しだけ前方においていれば値は変化するので斜め前方向への移動にシフトできる。(黒よりのグレーや、グレーよりの白では斜め前方向の移動を採用しているため)~ +次段階として、(順に)十字路手前のライン右端から十字路を超えた先のライン左端/十字路手前のライン左端から十字路を超えた先のライン右側/十字路手前から十字路の向こう(端の変更なし)へ移動するサブルーチンを作った。これが、右端トレースと左端トレースを使い分けるミソとなる。 sub Rcross_line() { OnRev(OUT_C,60);Wait(800); Off(OUT_BC); } sub Lcross_line() { OnRev(OUT_B,60);Wait(800); Off(OUT_BC); } sub cross_line() { OnRev(OUT_BC,50);Wait(350); Off(OUT_BC); } **task main [#ge44c74a] 2回目のK以外は音が鳴って、交差点を渡る合図となるようにした。 +A~B task main() { SetSensorLight(S3); while(SENSOR_3>BLACKandGLAY){ go;} go;Wait(800);//A~Bへ直進 黒になるまで直進する。しかしそれだけでは直進方向と垂直方向の線上に行くので、交差点として認識してしまう。そこで少し直進を入れた。 +B~D Rside_trace();//B~Cを右端走行 PlaySound(SOUND_CLICK);//Cを斜め横断する合図 Lcross_line();//Cを左斜めに横断 Lside_trace();//C~Dを左側走行 short_break;//Dで一時停止 PlaySound(SOUND_CLICK);//Dを横断する合図 cross_line();//Dをまっすぐ渡る Cに到達したことを明確にしたいので、敢えてCまで右側で、C点の交差点で右側から左側への移動を入れた。 +D~G Lside_trace();//D~Gを左側走行 PlaySound(SOUND_CLICK);//横断する合図 OnFwd(OUT_B,30);OnRev(OUT_C,60);Wait(1300); D~Gは左端、枠の外側を移動すれば、このマシンは正確なので(外側の白では回転の動作というのもあるが)、90度の角をトレースできる。 +G~K int n=0;//円周上で横断する回数を定義 while(n<2){ //H,Iで二回。Jで違う挙動を取りたい。 Rside_trace();//円周を右側(外側)走行 n++;//交差点通過時にカウントを1追加 PlaySound(SOUND_CLICK);//横断する合図 OnRev(OUT_B,60);Wait(500);//交差点通過 } Rside_trace();//円周を右側(外側)走行 PlaySound(SOUND_CLICK);//横断する合図 OnFwd(OUT_B,30);OnRev(OUT_C,70);Wait(900);//JKの右側へ出る Rside_trace();//J~Kを右側走行 カウンターを用いてトレース、交差点通過を2回繰り返した。(repeat記号でもよかった)JKの右側に出る動作は、調整して線右側の白い部分に出るようにした。 +K~L PlaySound(SOUND_CLICK);//横断する合図 OnFwd(OUT_C,30);OnRev(OUT_B,70);Wait(1300);//KLの左側へ出る int a=0; int b=0; while((a<2000)&&(b<2500)){ if (SENSOR_3 < BLACK) { b++;turn_left; } else if (SENSOR_3 < BLACKandGLAY) { a++;b++;turn_leftFwd; } else if (SENSOR_3 < GLAYandWHITE) { a++;go; } else if (SENSOR_3 < WHITE) { a++;b++;turn_rightFwd; } else { b++;turn_right;}}//K~Lを左側走行(回数指定で距離を制限) まず、原始的な調整で、KLの左側に行くよう点Kで回る。次だが、論理積を用いてカウンターを用いた条件が両方とも満たされた時だけトレースするようにした。線の外側のカウントと内側のカウントを二つ用意して、ボールのところまでで、止めるようにした。 +LからA Off(OUT_BC);OnFwd(OUT_A,100);Wait(700);Off(OUT_A);// turn_left OnRev(OUT_B,35);OnFwd(OUT_C,35);Wait(300); Rside_trace();//L~Mを右側走行(白の場合左回転なので方向修正の回転もこれに含まれる) cross_line();//Kを横断 Rside_trace();//K~Mを右側走行 short_break;//Mで一時停止 OnRev(OUT_A,100);Wait(700);Off(OUT_A);//ボールを離す OnRev(OUT_BC,-100);Wait(1000);Off(OUT_BC);//後退 OnRev(OUT_A,-100);Wait(700);Off(OUT_A);//アームもどす OnRev(OUT_BC,100);Wait(1000);Off(OUT_BC);//前進 OnRev(OUT_A,100);Wait(700);Off(OUT_A);//ボールを撃つ OnRev(OUT_BC,100);Wait(500);Off(OUT_BC);//進む } Lでの方向転換を少しすれば、あとは線の右側走行に切り替えることで、白の時は回転し軌道修正してくれる。その後は上記注釈通りに実行する。問題はヘアピンカーブだが、これも外側ならば問題なく辿ることができる。 *MOVIE [#d9fa0eec] https://youtu.be/Dt8wVHgczdM *追記 [#ebf9ee14] グレーの綴が間違っていました、GLAYではなくて、GRAYが正解でした。すみませんでした。