#author("2019-12-20T17:42:35+09:00","kzy_k","kzy_k")
#author("2020-01-10T00:38:42+09:00","kzy_k","kzy_k")
[[2019b/Member]]
#contents
*課題2の概要 [#u3a97da7]

*課題2の概要 [#u3a97da7]
-図の赤い線の経路を黒い線にそって動くロボットを作成する。
#ref(2019b/Member/kzy_k/Mission2/2019b-mission2'.png,100%,課題図)
-ただし、交差点や丁字路では1秒一時停止する。~
また、I地点にある球をつかみI'地点で離す。
*ロボットの説明 [#y255cad1]
-ロボットの全体像は以下である
#ref(2019b/Member/kzy_k/Mission2/IMG_20191220_172047.jpg,10%,ロボ図)
#br
#br
-黒い線に沿って行くためには線を検知しなければならないので光学センサーを車輪の間にできるだけ近づけて装着する。~
あまり車輪から離れていると、カーブの時などにセンサーが線から大きくずれてしまいトレースができないからである。

#ref(2019b/Member/kzy_k/Mission2/IMG_20191211_201823.jpg,10%,センサー図)
#br
#br
-ボールをつかむ機構はギアでモーターの前後の回転を左右の回転に変換することで実現させた。
#ref(2019b/Member/kzy_k/Mission2/gear.png,60%,ギア図)
*プログラムの説明 [#g3d09da0]
**ライントレース [#p0575474]
-今回の課題の主要部分である。~
基本原理として取り付けたセンサーにより得た地面の明るさの値により本体の動きを制御して線上を走らせる。~
センサーの値が35以下だと左旋回(左の車輪を前に回転、右の車輪をを後ろに回転)~
43以下で左折(左の車輪を前に回転、右の車輪は停止)~
57以下で直進~
65以下で右折~
65以上で右旋回~
センサーの値から判断して動くという一連の動作をwhile文でループすることで、本体が常に線の左側に沿って進むようにする。
#ref(2019b/Member/kzy_k/Mission2/トレース.png,40%,ライントレース)
#br
#br
-さらにこれに交差点を認識する機構を組み込む。~
交差点に差し掛かるとセンサーは通常より長い間黒(センサー値が35以下)を認識し続けるので、これを利用して黒の時間が一定時間を超えればトレースのループから抜けるようにする。~
具体的には、t0という変数にまず現在時刻を記録させる。そして左旋回(センサーが35以下)を実行してるとき以外で逐一t0に現在時刻を入れ、更新させる。
そしてwhileのループが成立するために現在の時刻とt0の差がある値以下という条件を付け加えれば、ずっと黒だとループから抜けることができる。
#ref(2019b/Member/kzy_k/Mission2/交差点.png,40%,交差点)
#br
#br
-ただし、急カーブなどは交差点でないがずっと黒であり続けるので上の機構が働きループから抜けてしまう。それを阻止するため、ループが始まってからある時間までは、ループを抜けるという機構を実行させなくする。~
具体的には、ループの外でt_startという変数にCurrentTick()で現在時刻を記録させる。そうすればCurrentTick()-t_start<t_minはループが始まってからの時間となり、whileのループが成立するために、その始まってからの時間がある値より小さいという条件を論理和で上の条件とつなげば、初めはずっと黒でもループから抜けないということが実現できる。
#br
#br
-また、ループを強制終了させるという機構を組み込む。上と同様にt_startを使いループが始まってからの時間が強制終了させたい時間以下であるという条件をこれまでの条件にまとめて論理積でつなげば実現できる。
#br
#br
-以下がこれらをすべてまとめたライントレースのプログラムである。ここではループから抜けるようなずっと黒である時間を100、また初めのループからの脱出を無視する時間t_min、強制終了時間t_maxを引数として指定している。
 #define t 50
 #define SPEED 30
 void gf (long x,long y){
  OnFwd(OUT_C,x);OnFwd(OUT_B,y);
 }
 void left_line(long t_min,long t_max) {
 SetSensorLight(S1);
    long t0=CurrentTick();
    long t_start=CurrentTick();
    while((CurrentTick()-t0<=100||CurrentTick()-t_start<t_min)&&
     (CurrentTick()-t_start<t_max)){//100以上黒で外れるが始めてからtmin以内だったら外れない。tmax経ったら強制終了
     if(SENSOR_1 < (t-15)){//真っ黒
       gf(-SPEED,SPEED);//左旋回
      }
     else{
       t0=CurrentTick();
       if(SENSOR_1<(t-7)){ //黒
         gf(0,SPEED);//左折
        }
       else if(SENSOR_1 <(t+7)){ //灰
         gf(SPEED,SPEED); //直進
        }  
       else if(SENSOR_1>t+15){ //白
         gf(SPEED,0); //右折
        }
       else{ //真っ白
         gf(SPEED,-SPEED); //右旋回
        }
       } //else
      } //while
      Off(OUT_BC);
 }
これは線の左側をトレースするプログラムであるが、課題を達成するためには右側をトレースするプログラムも必要である。右側のライントレースは左と動きを対称的に逆にするだけでよい。つまり左に行くのを右、右に行くのを左にする。
**ボールをつかむ [#ldbcd658]
-ボールをつかむ機構はセンサーを使いボールを認識させて掴むという案もあったが、センサーの感度が悪く難しかったので、時間や回る角度を調節して一定の動作をさせるプログラムにした。
+H地点から少し進む~
+ボールをつかむ~
+線をまたぎ、右側をH'の丁字路に差し掛かるまでトレースする。~
+回れ右をする~
+ボールを離す~
+少しバックをする~
+回れ左をする~
#br
#ref(2019b/Member/kzy_k/Mission2/ボール掴み.png,100%,ボール掴み図)
#br
-以下がプログラムである
 #define SPEED 30
 #define halfturn_time 3000
 #define quarterturn_time 500
 #define catch_release_time 1900
 #define t_3 1000//バック時間
 void gf (long x,long y){
  OnFwd(OUT_C,x);OnFwd(OUT_B,y);
 }
 void turn_right (long z){//右回転
   OnFwd(OUT_C,25);OnFwd(OUT_B,-20);Wait(z);Off(OUT_BC);
 }
 void turn_left (long w){//左回転
   OnFwd(OUT_C,-25);OnFwd(OUT_B,20);Wait(w);Off(OUT_BC);
 }
 void release (){
   OnFwd(OUT_A,30);Wait(catch_release_time);Off(OUT_A);
 }
 void catch(){
  OnFwd(OUT_A,-30);Wait(catch_release_time);Off(OUT_A);
 }
 void catch_ball()//ボールつかみ
 {
    gf(SPEED,SPEED);Wait(500);
    gf(0,0);catch();//つかむ
    gf(20,0);Wait(1000);right_line(3000,100000);//置く向こう側まで進む
    turn_right(halfturn_time);//uターン
    gf(0,0);release();//はなす
    gf(-SPEED,-SPEED);Wait(t_3);//バック
    turn_left(quarterturn_time);//少しターン
 }
**つなぎのプログラム [#hdf28aee]
-まっすぐ線を渡るプログラムと少し回転して線を渡るプログラムをあらかじめ作っておく。
 #define t_4 800//線を超える時間
 void gf (long x,long y){
  OnFwd(OUT_C,x);OnFwd(OUT_B,y);
 }
 void closs_line1()//線を渡る
 {
     gf(20,20);Wait(t_4);
 }
 void closs_line2()//線を渡り向こうへ
 {
     gf(20,0);Wait(1300);
 }
**メインプログラム [#x141ec72]
あとは、上で述べたプログラムを組み合わせて、各時間を調節することで課題を実行する。

+初めにA地点を出発。~
 ここはトレースでなく直線的に1秒進む。
+ここから左側のライントレースをH地点までする。~
 ただし、初めのカーブを交差点と認識してしまうのでt_minをt_0、つまり8秒として停止を無視させる。~
 そしてH地点で1秒間一時停止し、closs_line2()で渡る。
+上で説明したボールをつかむプログラム、catch_ball()を実行。
+H'地点からG'の交差点まで右側のライントレース。
+G'の交差点で一時停止。
+G'をcloss_line1()で渡る。
+E'、F'を右側のライントレースで通過する。~
 この時、D'、E'、F'を交差点と認識して止まってしまうがここでは止まってほしくないのでt_minをt_5、つまり8秒として停止を無視させる。
+G'の交差点で一時停止。
+G'をcloss_line1()で渡る。
+C'まで右側のライントレース。
+左に約90°旋回。
+C'をcloss_line1()で渡る。
+B'まで左側をライントレース。
+A'に入って止まる。
#ref(2019b/Member/kzy_k/Mission2/main.png,100%,メインプログラム)
#br
#br
-以下が実際のプログラムである
 #define SPEED 30
 #define halfturn_time 3000
 #define t_5 8000//終盤の四角を行く時間
 #define t_6 5000//最後の直線の時間
 void gf (long x,long y){
   OnFwd(OUT_C,x);OnFwd(OUT_B,y);
 }
 void turn_right (long z){//右回転
   OnFwd(OUT_C,25);OnFwd(OUT_B,-20);Wait(z);Off(OUT_BC);
 }
 void turn_left (long w){//左回転
   OnFwd(OUT_C,-25);OnFwd(OUT_B,20);Wait(w);Off(OUT_BC);
 }
 task main(){
    gf(SPEED,SPEED);Wait(1000);//a
    left_line(t_0,1000000);Wait(1000);closs_line2();//jhi
    catch_ball();//ボールつかむ
    right_line(0,100000);//h'g'
    Wait(1000);//g'
    closs_line1();//g'渡る
    right_line(t_5,100000);//g'まで止まらずライントレース
    Wait(1000);//g'
    closs_line1();//g'渡る
    right_line(0,100000);//g'c'
    turn_left(halfturn_time/2);//c'
    closs_line1();//c'渡る
    left_line(t_6,10000000);//c'b'
    gf(SPEED,SPEED);Wait(1000);gf(0,0);//a'
  }
**プログラム全文 [#qe850f7a]
これらをまとめて、コンピュータに入れた全てのプログラムは以下である。
 #define t 50
 #define SPEED 30
 #define halfturn_time 3000
 #define quarterturn_time 500
 #define catch_release_time 1900
 #define t_0 8000//はじめの時間
 #define t_1 500//ボールまで行く時間
 #define t_2 7000//置く場所の向こうへ行く時間
 #define t_3 1000//バック時間
 #define t_4 800//線を超える時間
 #define t_5 8000//終盤の四角を行く時間
 #define t_6 5000//最後の直線の時間
 
 
 void gf (long x,long y){
   OnFwd(OUT_C,x);OnFwd(OUT_B,y);
 }
 void turn_right (long z){//右回転
   OnFwd(OUT_C,25);OnFwd(OUT_B,-20);Wait(z);Off(OUT_BC);
 }
 void turn_left (long w){//左回転
   OnFwd(OUT_C,-25);OnFwd(OUT_B,20);Wait(w);Off(OUT_BC);
 }
 void release (){
   OnFwd(OUT_A,30);Wait(catch_release_time);Off(OUT_A);
 }
 void catch(){
   OnFwd(OUT_A,-30);Wait(catch_release_time);Off(OUT_A);
 }
 void left_line(long t_min,long t_max) {
 SetSensorLight(S1);
     long t0=CurrentTick();
     long t_start=CurrentTick();
     while((CurrentTick()-t0<=100||CurrentTick()-t_start<t_min)&&
      (CurrentTick()-t_start<t_max)){//100以上黒で外れるが始めてからtmin以内だったら外れない。tmax経ったら強制終了
      if(SENSOR_1 < (t-15)){//真っ黒
        gf(-SPEED,SPEED);//左旋回
       }
      else{
        t0=CurrentTick();
        if(SENSOR_1<(t-7)){ //黒
          gf(0,SPEED);//左折
         }
        else if(SENSOR_1 <(t+7)){ //灰
          gf(SPEED,SPEED); //直進
         }  
        else if(SENSOR_1>t+15){ //白
          gf(SPEED,0); //右折
         }
        else{ //真っ白
          gf(SPEED,-SPEED); //右旋回
         }
 
       } //else
 
      } //while
      Off(OUT_BC);
 }
 
 void right_line(long t_min,long t_max) {
 SetSensorLight(S1);
     long t0=CurrentTick();
     long t_start=CurrentTick();
     while((CurrentTick()-t0<=100||CurrentTick()-t_start<t_min)&&
      (CurrentTick()-t_start<t_max)){
      if(SENSOR_1 < (t-15)){//真っ黒
        gf(SPEED,-SPEED);//右旋回
       }
      else{
        t0=CurrentTick();
        if(SENSOR_1<(t-7)){ //黒
          gf(SPEED,0);//右折
         }
        else if(SENSOR_1 <(t+7)){ //灰
          gf(SPEED,SPEED); //直進
         }  
        else if(SENSOR_1>t+15){ //白
          gf(0,SPEED); //左折
         }
        else{ //真っ白
          gf(-SPEED,SPEED); //左旋回
         }
 
       } //else
 
      } //while
      Off(OUT_BC);
 }
 
 void catch_ball()//ボールつかみ
 {
     gf(SPEED,SPEED);Wait(500);
     gf(0,0);catch();//つかむ
     gf(20,0);Wait(1000);right_line(3000,100000);//置く向こう側まで進む
     turn_right(halfturn_time);//uターン
     gf(0,0);release();//はなす
     gf(-SPEED,-SPEED);Wait(t_3);//バック
     turn_left(quarterturn_time);//少しターン
 }
 void closs_line1()//線を渡る
 {
     gf(20,20);Wait(t_4);
 }
 void closs_line2()//線を渡り向こうへ
 {
     gf(20,0);Wait(1300);
 }
 task main(){
     gf(SPEED,SPEED);Wait(1000);//a
     left_line(t_0,1000000);Wait(1000);closs_line2();//jhi
     catch_ball();//ボールつかむ
     right_line(0,100000);//h'g'
     Wait(1000);//g'
     closs_line1();//g'渡る
     right_line(t_5,100000);//g'まで止まらずライントレース
     Wait(1000);//g'
     closs_line1();//g'渡る
     right_line(0,100000);//g'c'
     turn_left(halfturn_time/2);//c'
     closs_line1();//c'渡る
     left_line(t_6,10000000);//c'b'
     gf(SPEED,SPEED);Wait(1000);gf(0,0);//a'
   }
 //end

*改善点 [#p8f38731]
-ロボット発表会本番に最後まで行けなかった理由として、ライントレースで直角カーブに入るとき入る状態が悪くつっかかってしまい曲がる時間が異なってしまったことが挙げられる。時間が違ってしまうとt_minを超過してしまい無視してほしいところを無視せず止まってしまうことがある。センサーで読み取る値はほんのちょっとの違い(マジックで書いた線の微妙な濃淡や外部による影の有無)で異なってしまい、直線だとしてもまっすぐブレずに進むというのは難しくそのためカーブに入る状態を統一できず、曲がりきる時間も異なってしまう。~
今回は行えなかったが、改良案として考えたものは、通過した交差点の数(つまり黒の時間がある値を超えた回数)を記録して、それがある回数以下だったら交差点で止まるのを無視させるというものであり、これならルートの時間のブレを気にせずまた、いちいち時間を調節する必要もなくなる。
*感想 [#c7b4dde9]
-今回の課題は基本的にライントレースのプログラムができれば本体は割と自律的に動いてくれるため交差点やボールをつかむ機構を後からそれに加える形で完成できた。~
細かいこととして交差点で止まるとき少し交差点を曲がろうとして止まるのでどうしてもまっすぐに止まってくれず悩んだが、そのために今回は線を渡るマクロを別で用意した。~
ライントレースのt_maxで強制終了というのは今回は特に使わなかった。


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