[[2019a/Member]]

目次
#contents
*課題2 [#t1a8307b]
下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成せよ。
#ref(2019a/Member/tsukue/Mission2/linetrace_1.jpg,50%,画像1)
**ミッション [#h5996f83]
A地点を出発し、次の経路を黒い線にそって動くロボットを作成する。

A地点から出発 → B → C(直進) → D(一時停止の後、直進) → E → F → G(一時停止の後、右折) → H → I → J(右折) → K(左折) → L(ピンポン玉をつかむ) → K(直進) → M(一時停止) → シュート→ A地点に入る(ゴール)

交差点では1秒間停止し、丁字路では直角方向に進入する時のみ一時停止すること。

*ロボット [#y641683e]
組み立て説明書にある基本のロボットをベースに、光センサーと玉を扱う部品を組み込んだ。
#ref(2019a/Member/tsukue/Mission2/linetrace_2.jpg,50%,画像1)
光センサーとタイヤの回転軸の距離が遠すぎるとカーブを曲がれないので、注意した。
玉を扱う部品は、アームが一回転するようになっており、玉をつかんだりシュートしたりできる。
#ref(2019a/Member/tsukue/Mission2/linetrace_3.jpg,50%,画像1)
実際に走らせたところ、パワーがなくカーブで止まってしまったので、左右のモーターにギアを組み込みトルクを上げた。
#ref(2019a/Member/tsukue/Mission2/linetrace_4.jpg,50%,画像1)

*プログラム [#oe67ef8b]
まず、線の左側の、白と黒の境界線をたどるプログラムを作った。
黒、黒寄りのグレー、白寄りのグレー、白の閾値を定義し、黒なら左、グレーなら直進、白寄りのグレーなら少しだけ右、といったように進むようにした。
また、D地点のような交差点やK地点のような丁字路では、黒の値をとる時間が一定値以上になったときに交差点と判断し、直進や左折等をするようにした。

しかし、このシステムでは G→H→I→J 間で円の内側をたどるため交差点の判断をすることができず、また K→M 間の R=4 のカーブを曲がりきることができなかった。
そこで、G地点を超えたところから線の右側の境界線をたどるようにすることでどちらもクリアすることができた。

これで一通りミッションを遂行できるが、問題は左右の境界線それぞれのプログラムが必要になり、サブルーチンの量が倍になってしまうことだ。
試行錯誤の結果、左を1、右を-1とする方法を見つけた。
--follow_line(left);
--turn_right(right);

のように使い、マクロ内の変数に1または-1が代入される。こうすることで、左右で反対の命令を出すことができる。
のように使い、マクロ内の変数に1または-1が代入される。こうすることで、プログラムの量を増やさずに左右で反対の命令を出すことができる。

**定数、マクロ [#tf3d9b84]
-left、rightを定数として扱い、サブルーチンの変数に代入して使う。
 #define left 1
 #define right -1
-左(右)に回転する(xに1または-1が代入される)
-左(右)に回転する
 #define go(x)         OnRev(OUT_B,30*x); OnRev(OUT_C,-30*x);
-左前(右前)に進む(xに1または-1が代入される)
--x=1のとき、 OnRev(OUT_B,30); OnRev(OUT_C,-30);
--x=-1のとき、OnRev(OUT_B,-30); OnRev(OUT_C,30);
-左前(右前)に進む
 #define go_little(x)  OnRev(OUT_B,35*(x+1)/2); OnRev(OUT_C,35*(-1*x+1)/2);
--x=1のとき、 OnRev(OUT_B,35); OnRev(OUT_C,0);
--x=-1のとき、OnRev(OUT_B,0); OnRev(OUT_C,35);
-まっすぐ進む
 #define go_straight   OnRev(OUT_BC,35);
-その他の定義
 #define BLACK      40
 #define GRAY_BLACK 45
 #define GRAY_WHITE 52
 #define WHITE      60
 #define short_brake Wait(1000);
( brake は「ブレーキ」を意味するが、気にしないでほしい。)

**サブルーチン [#ndf1c92b]
-線の左側(右側)境界線に沿って進み、交差点で止まる(aに1または-1が代入される)
***ライントレース [#zdc30397]
左側境界線をたどる仕組みを説明する。赤い丸は光センサー、数字はセンサーの値、青い四角はタイヤを示す。右側の場合はその逆である。
#ref(2019a/Member/tsukue/Mission2/lt1.JPG,50%,画像1)
↑センサーがちょうど境界線上にあるとき、値は45から52の範囲にあるので、まっすぐ進む。
#ref(2019a/Member/tsukue/Mission2/lt2.JPG,50%,画像1)
↑軌道からずれてセンサーが黒と認識すると、左に進んで軌道修正する。
#ref(2019a/Member/tsukue/Mission2/lt3.JPG,50%,画像1)
↑反対ににセンサーが白と認識すると、右に進んで軌道修正する。
#ref(2019a/Member/tsukue/Mission2/lt4.JPG,50%,画像1)
↑このようにセンサーが150/1000秒以上黒と認識すると、交差点と判断する。(実際には、黒でないときに現在時刻t0が更新され、黒であるときに最後に更新されたt0と現在時刻の差を求めている。)
 sub follow_line(int a)
 {
   SetSensorLight(S3);
   long t0=CurrentTick();
   while(CurrentTick()-t0<150) {
     if (SENSOR_3 < BLACK) {
       go(a);
     } else if (SENSOR_3 < GRAY_BLACK) {
       go_little(a);    t0=CurrentTick();
     } else if (SENSOR_3 < GRAY_WHITE) {
       go_straight;     t0=CurrentTick();
     } else if (SENSOR_3 < WHITE) {
       go_little(-1*a); t0=CurrentTick();
     } else {
       go(-1*a);        t0=CurrentTick();
     }
   }
   Off(OUT_BC);
 }
-交差点を直進(bに1または-1が代入される)
***交差点での動作、玉の扱い [#xbecb68b]
交差点での動作も左右のどちらの境界線に沿っているかによってそれぞれプログラムが必要だが、前述のとおり right と left を定義することでプログラムを増やさなくて済んだ。
-交差点を直進
 sub cross_line(int b)
 {
   go_little(-1*b);
   Wait(300);
   go_straight;
   Wait(300);
   Off(OUT_BC);
 }
-交差点を左折(cに1または-1が代入される)
-交差点を左折
 sub turn_left(int c)
 {
   go_little(left);
   Wait(800-100*c);
   go(left);
   Wait(800-100*c);
   Off(OUT_BC);
 }
-交差点を右折(dに1または-1が代入される)
-交差点を右折
 sub turn_right(int d)
 {
   go_little(right);
   Wait(800+100*d);
   go(right);
   Wait(800+100*d);
   Off(OUT_BC);
 }
-線の左側から右側(右側から左側)に切り替える(eに1または-1が代入される)
-線の左側から右側(右側から左側)に切り替える
 sub change_line(int e)
 {
   go(-1*e);
   Wait(750);
   Off(OUT_BC);
 }
-K地点からL地点まで移動、玉をつかむ
 sub ball_catch()
 {
   go(left);
   Wait(1000);
   go_little(left);
   Wait(1200);
   go_straight;
   Wait(800);
   Off(OUT_BC);
   OnFwd(OUT_A,30);
   Wait(600);
   Off(OUT_A);
   go(left);
   Wait(1000);
 }
-玉をシュートする
 sub shoot()
 {
   go(left);
   Wait(200);
   Off(OUT_BC);
   Wait(1000);
   OnRev(OUT_A,70);
   Wait(900);
   Off(OUT_A);
 }
**task  main [#ga971036]
あとはサブルーチン等を並べるだけ。同じものが連続する箇所はrepeatでまとめている。
 task main()
 {
  go_straight; Wait(500);   // A→B
  follow_line(left); short_brake; cross_line(left);   // B→C→D
  follow_line(left); short_brake; turn_right(left);   // D→E→F→G
  change_line(left);   // 左側境界線→右側境界線
  go_straight; Wait(500);                              // A→B
  follow_line(left); short_brake; cross_line(left);    // B→C→D
  follow_line(left); short_brake; turn_right(left);    // D→E→F→G
  change_line(left);                                   // 左側境界線→右側境界線
  repeat(2){follow_line(right); cross_line(right);};   // G→H→I
  follow_line(right); turn_right(right);   // I→J
  follow_line(right);   // J→K
  ball_catch();   // K→L→ボールをつかむ
  follow_line(right); cross_line(right);   // L→K
  follow_line(right);   // K→M
  shoot();   // シュート
  go_straight; Wait(1500);   // M→A
  follow_line(right); turn_right(right);               // I→J
  follow_line(right);                                  // J→K
  ball_catch();                                        // K→L→玉をつかむ
  follow_line(right); cross_line(right);               // L→K
  follow_line(right);                                  // K→M
  shoot();                                             // シュート
  go_straight; Wait(1500);                             // M→A
 }

*まとめ [#l45d68f0]
今回は早く完成させることができた。サブルーチンや定数を使いこなせるようになった。プログラムを短くするのに苦労したが、結果的にうまくいってよかった。



トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS