[[2019a/Member]]

目次
#contents
*課題3 [#d5962a7c]
ピンポン玉または青と赤のボールを運搬して、所定の容器の中に入れる。
#ref(2019a/Member/tsukue/Mission3/field.jpg,50%,画像1)
**フィールドの説明 [#sbe3faab]
--フィールドは課題2で使用した紙を使用する。
--生協のお弁当の四角いプラ容器2つをそれぞれ円内に置き、片方に玉を2個入れる。
--残りの2個の玉は課題2と同じ位置に置く。その際、ゴムタイヤやプレートの上に置いてもよい。
--プラ容器には色をつけたり文字や記号を書いてもよい。
--プラ容器は両面テープ等でフィールドに固定してもよい。
--2枚の紙の境界にはそれぞれ幅1cm、合計2cmの黒線を描いてもよい。
**基本ルール [#tb9f413f]
--競技時間は審判が続行不能と判断するまで、あるいはリタイアするまで。
--図のA地点または(および)A'地点からスタートする。ただし接地している部分はそれぞれの領域内に収まるものとする(線上はOK)。上空部分は領域からはみ出していてもよい。
--開始の合図から5秒以内にスタートボタンを押す作業を完了すること。
--競技が終了するまで、ロボットに触ったり人間が遠隔で操作してはならない。
--途中でうまく動かなくなった場合、1回限り再スタートすることができる(再スタートの際に別プログラムで起動してよい)。
--競技終了後、ロボットが、ゴールのプラ容器に触れていてはいけない。
--競技終了後、もともと玉が入っていたプラ容器が、ゴールのプラ容器に触れていてはいけない。

詳細は[[こちら:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMission3]]。

*方針 [#m22424d4]
とにかく安定性を重視した。移動はほぼすべてライントレースで行うため、貼り合わせたフィールドの境界線に線を引いた。ボールを持つ機構はアーム式とし、そのためにはモーターが最低4つ必要であったため、NXTを2台組み合わせた一体型とした。移動関連はマスターが、アーム関連はスレーブが担う。

*ロボット [#pf42f547]
#ref(2019a/Member/tsukue/Mission3/robot1.jpg,50%,画像1)
↑側面。NXTが対称に配置されており、見えているNXTがスレーブ、反対側がマスターである。マスターにはタイヤのモーターと光センサー、スレーブにはアーム関連のモーターがつながっている。
#ref(2019a/Member/tsukue/Mission3/robot2.jpg,50%,画像1)
↑斜め上。NXT本体やモーターのような大きなパーツをなるべく少ないパーツで組み合わせることで、頑丈になる。
#ref(2019a/Member/tsukue/Mission3/robot3.JPG,50%,画像1)
↑背面。こちら側はキャスターが支えている。配線をしまう空間をあけて、見た目にも配慮した。(この画像のみアームの構造が旧型である。)
#ref(2019a/Member/tsukue/Mission3/robot4.jpg,50%,画像1)
↑アームを取り払った前面。デフォルトのようにタイヤを回すモーターを寝かせると、重心の関係でアームの配置が難しくなった。そこで[[課題1:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMember%2Ftsukue%2FMission1#qa2f051c]]のようにモーターを立てることで、重いNXT本体をアームの反対側に低く配置することができ、重心の問題を解決した。
#ref(2019a/Member/tsukue/Mission3/robot5.jpg,50%,画像1)
↑底面。毎度のごとくタイヤにギアを組み込んでいる。今回は光センサーを2つ使用しており、センサー間は線の幅より少しだけ大きい。タイヤの軸とセンサーの距離は[[課題2:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMember%2Ftsukue%2FMission2#y641683e]]と同様。
#ref(2019a/Member/tsukue/Mission3/robot6.jpg,50%,画像1)
↑真ん中に挟まれたモーターはアームの上げ下げを担う。ここもギアを使用。
#ref(2019a/Member/tsukue/Mission3/robot7.jpg,50%,画像1)
↑このようにアームが上下する。アームはそれなりの重量があり自然に落ちてきてしまうため、プログラムでカバーすることにした(後述)。アームの一部になっているモーターは、玉をつかむ部分の開閉を担う。
#ref(2019a/Member/tsukue/Mission3/robot8.jpg,50%,画像1)
↑玉を2つ同時につかめる大きさとすることで、容器内の玉を効率よく運べる。画像の下の部分は、玉を持ち上げたときに落とさないようにするストッパーである。ギアで回転方向を変えるのが難しかった。

*プログラム [#v09a4685]
**マスター [#v3fd8025]
***定数、マクロ [#ebb204f8]
 #define KOSATEN 37
*マスターのプログラム [#v09a4685]
**定数、マクロ [#ebb204f8]
 #define KOSATEN 37     //37以下は黒
 #define completed 15
 #define turn_left   OnRev(OUT_A,70);OnFwd(OUT_B,70);   //左に回転する
 #define go_left     OnRev(OUT_A,85);Off(OUT_B);        //左に進む
 #define go_straight OnRev(OUT_AB,70);                  //まっすぐ進む
 #define go_right    OnRev(OUT_B,85);Off(OUT_A);        //右に進む
 #define turn_right  OnRev(OUT_B,100);OnFwd(OUT_A,100); //右に回転する
 #define short_break Off(OUT_AB);Wait(1000);
 #define brake       Off(OUT_AB)
completed は通信に使う(後述)。
brake は「ブレーキ」を意味するが、break として捉えてほしい。

***サブルーチン [#a6f8fad4]
-ライントレース
**サブルーチン [#a6f8fad4]
***ライントレース [#nba5f481]
光センサーを2つ取り付けており、課題2のプログラムを流用できないため、新たに作り直した。左右の値の差を求め、差が大きければ曲がり、0に近ければ直進する仕組みとなっている。以下の画像では、赤い丸が光センサー、数字が光センサーの値、青い四角がタイヤを示している。
-直線

光センサーを2つ取り付けており、課題2のプログラムを流用できないため、新たに作り直した。左右の値の差を求め、差が大きければ曲がり、0に近ければ直進する仕組みとなっている。
 sub follow_line()                  //交差点までライントレース
#ref(2019a/Member/tsukue/Mission3/lt1.JPG,50%,画像1)
↑このとき、左右の値の差は0のため直進する。
#ref(2019a/Member/tsukue/Mission3/lt2.JPG,50%,画像1)
↑軌道からずれてこのようになると、差が大きくなり軌道を修正する。

-交差点

#ref(2019a/Member/tsukue/Mission3/lt3.JPG,50%,画像1)
↑このように両側で黒と認識すると交差点と判断する。
#ref(2019a/Member/tsukue/Mission3/lt4.JPG,50%,画像1)
↑このとき、まず右側が黒と認識するため、右に進む。
#ref(2019a/Member/tsukue/Mission3/lt5.JPG,50%,画像1)
↑やがて左側も黒と認識するため、交差点と判断する。
-交差点までライントレース
 sub follow_line()
 {
   while((SENSOR_1>KOSATEN)||(SENSOR_2>KOSATEN)){
    int d=SENSOR_1-SENSOR_2;
    int d=SENSOR_1-SENSOR_2;     //センサー1が右側、センサー2が左側である
     if (d<-6){
      turn_right;
      turn_right;     //右より左のほうが大きい(左が白、右が黒)
    }else if (d<-3){
      go_right;
      go_right;       //右より左のほうが少し大きい
    }else if (d<=3){
      go_straight;
      go_straight;    //ほぼ同じ(どちらも白)
    }else if (d<=6){
      go_left;
      go_left;        //左より右のほうが少し大きい
    }else {
      turn_left;
      turn_left;      //左より右のほうが大きい(左が黒、右が白)
    }
   }
   PlaySound(SOUND_CLICK);
   brake;Wait(200);
 }
while の条件について、「センサー1が黒でない」または「センサー2が黒でない」となっているが、これはド・モルガンの法則によるもので、『「センサー1が黒でない」または「センサー2が黒でない」』でなくなったとき、すなわち『「センサー1が黒である」かつ「センサー2が黒である」』となったときに while の外に出るようになっている。

 sub follow_line2(int followtime)   //一定時間までライントレース
-指定した時間だけライントレース
 sub follow_line2(int followtime)
 {
   long t0=CurrentTick();
   while(CurrentTick()-t0<followtime){
    int d=SENSOR_1-SENSOR_2;
     if (d<-6){
      turn_right;
    }else if (d<-3){
      go_right;
    }else if (d<=3){
      go_straight;
    }else if (d<=6){
      go_left;
    }else {
      turn_left;
    }
   }
   PlaySound(SOUND_CLICK);
   brake;Wait(200);
 }

 sub chokusinL()                    //交差点を直進(左)
***交差点や玉扱い後の動作 [#z45684b3]
ライントレースの性質上、T字路の行き止まりでない方向から進入すると分岐している方向に向いてしまうため、直進のプログラムは2通り作った。
 sub chokusinL()                    //交差点を直進(右を向いているとき)
 {
  go_left;Wait(500);
 }

 sub chokusinR()                    //交差点を直進(右)
 sub chokusinR()                    //交差点を直進(左を向いているとき)
 {
  go_right;Wait(500);
 }

 sub sasetsu()                      //交差点を左折
 {
  turn_left;Wait(500);
  go_left;Wait(500);
 }

 sub usetsu(int migi)               //交差点を右折
 {
  turn_right;Wait(500);
  go_right;Wait(migi);
 }

 sub turn_behind()                  //振り向く
 {
  turn_left;Wait(1000);
  while(SENSOR_1>KOSATEN){
  turn_left;
  }
  }                          //(線を認識するまで回転)
  Off(OUT_BC);
 }

 sub backleft()                     //後退し右を向く
 {
  OnFwd(OUT_A,100);Off(OUT_B);Wait(1500);
  turn_right;Wait(600);
  Off(OUT_AB);
 }

 sub backright()                    //後退し左を向く
 {
  OnFwd(OUT_B,100);Off(OUT_A);Wait(1400);
  turn_left;Wait(500);
  Off(OUT_AB);
 }

-通信

前述のとおりアーム関連はスレーブで制御するため、メッセージで指示を出し、completed と完了の合図が来るまで待つような仕組みとなっている。
***アーム関連(通信) [#y0325372]
アーム関連はスレーブで制御するため、メッセージで指示を出し、completed と完了の合図が来るまで待つような仕組みとなっている。
 sub ball_catch()                   //玉をつかむ
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,12);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

 sub ball_release()                 //玉を放す
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,11);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

 sub arm_up()                      //アームを上げる
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,13);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

 sub arm_down()                    //アームを下げる
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,14);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

 sub up_catch()                    //アームを上げながら閉じる
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,16);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

 sub down_release()                //アームを下げながら開く
 {
  int msg;
  SendRemoteNumber(1,MAILBOX1,17);
  while(msg==completed){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
  }
 }

***task main [#c6912e8f]
**task main [#c6912e8f]
移動の流れを下の画像に示す。この順番にしたがってサブルーチン等を組み合わせる。
#ref(2019a/Member/tsukue/Mission3/field'.jpg,50%,画像1)
 task main()
 {
  SetSensorLight(S1);                                        //右カラーセンサー
  SetSensorLight(S2);                                        //左カラーセンサー
  SetSensorLight(S1);                                        //右光センサー
  SetSensorLight(S2);                                        //左光センサー
  arm_up(); short_break; ball_release();
  go_straight; Wait(2000);                                   //A→M
  follow_line();                                             //M→K
  chokusinR(); follow_line2(1500); short_break;              //K→L
  arm_down(); short_break; ball_catch(); short_break;
  arm_up(); short_break;                                     //玉をつかむ
  turn_behind(); follow_line();                              //L→K
  chokusinL();   follow_line();                              //K→M
  usetsu(500);   follow_line();
  sasetsu();     follow_line();                              //M→B
  chokusinL();   follow_line();                              //B→X
  usetsu(500);   follow_line();                              //X→X'
  sasetsu();     follow_line();                              //X'→B'
  chokusinR();   follow_line();                              //B'→M'
  sasetsu();     follow_line();                              //M'→K'
  sasetsu(); follow_line2(1000); short_break;                //K'→J'
  ball_release(); short_break;                               //玉を放す
  backleft();   //J'→K'
  follow_line2(3700); short_break;                           //K'→L'
  arm_down(); short_break; ball_catch(); short_break;
  arm_up(); short_break;                                     //玉をつかむ
  turn_behind(); OnFwd(OUT_AB,30); Wait(100); follow_line(); //L'→K'
  usetsu(500); follow_line2(500); short_break;               //K'→J'
  ball_release(); short_break;                               //玉を放す
  backright(); follow_line();                                //J'→K'
  chokusinL(); follow_line();                                //K'→M'
  usetsu(500); follow_line();                                //M'→B'
  chokusinL(); follow_line();                                //B'→X'
  usetsu(500); follow_line();                                //X'→X
  sasetsu();   follow_line();                                //X→B
  sasetsu();   follow_line();                                //B→C
  usetsu(400); follow_line2(1650); short_break;              //C→I
  arm_down(); short_break; ball_catch(); short_break;
  up_catch(); short_break;                                   //玉をつかむ
  backleft();                                                //I→C
  follow_line();                                             //C→B
  usetsu(500); follow_line();                                //B→X
  usetsu(500); follow_line();                                //X→X'
  sasetsu();   follow_line();                                //X'→B'
  sasetsu();   follow_line();                                //B'→C'
  usetsu(700); follow_line2(900); short_break;               //C'→I'
  down_release(); short_break;                               //玉を放す
  arm_up(); short_break;
  backright();                                               //I'→C'
 }
**スレーブ [#e8faa47f]
***定数 [#k3e7c833]
*スレーブのプログラム [#h92f623c]
通信してアームを動かすだけなので、シンプルである。
**定数 [#k3e7c833]
すべてマスターと対応している。
 #define ball_release 11
 #define ball_catch   12
 #define arm_up       13
 #define arm_down     14
 #define completed    15
 #define up_catch     16
 #define down_release 17

***task main [#k7227ad9]
**task main [#k7227ad9]
マスターの指示に従って動作し、完了したら completed とメッセージを送る。
 task main()
 {
  int msg;
  while(true){
   ReceiveRemoteNumber(MAILBOX1,true,msg);
   if(msg==ball_release){
    OnFwd(OUT_A,50);Wait(500);Off(OUT_A);Wait(500);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //玉をつかむ
   }                                                 //玉をつかむ
   if(msg==ball_catch){
    OnRev(OUT_A,50);Wait(600);Off(OUT_A);Wait(500);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //玉を放す
   }                                                 //玉を放す
   if(msg==arm_up){
    OnFwd(OUT_B,50);Wait(600); OnFwd(OUT_B,5);Wait(500);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //アームを上げる
   }                                                 //アームを上げる
   if(msg==arm_down){
    OnRev(OUT_B,30);Wait(600); OnFwd(OUT_B,5);Wait(500);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //アームを下げる
   }                                                 //アームを下げる
   if(msg==up_catch){
    OnFwd(OUT_B,40);OnRev(OUT_A,15);Wait(300);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //アームを上げながら閉じる
   }                                                 //アームを上げながら閉じる
   if(msg==down_release){
    OnRev(OUT_B,10);OnFwd(OUT_A,20);Wait(150);
    SendResponseNumber(MAILBOX1,completed);
   }                                                      //アームを下げながら開く
   }                                                 //アームを下げながら開く
   Wait(100);
   msg=0;
  }
 }
アームが重力で落ちてくるのを防ぐため、アームの上げ下げのプログラムは Off(OUT_B)から OnFwd(OUT_B,5) に置き換えている。
アームが重力で落ちてくるのを防ぐため、アームの上げ下げのプログラムは Off(OUT_B)から OnFwd(OUT_B,5) に置き換えている。すると、常にアームに上向きの力が働き、重力とつり合う。

*まとめ [#d918eade]
アーム式というありきたりな仕組みではあるが、安定性を突き詰めたおかげで容器外の玉2つを確実に運ぶことができた。容器内の玉もあと少し調整を加えれば運べただろう。3人チームでもそれぞれ役割分担をして完成に近づけたことに大変達成感を感じている。


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