目次
#contents
*課題2 [#n2cefe5f]
下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成せよ。
**ミッション [#fcc071b5]
次のいずれかのコースで黒い線に沿って動き、途中でボールをゴール付近に立てた350mlの空き缶(黄色で表示)に当てるロボットを製作せよ。 
また、ボールはロボットが弧IHKJ上にある時にP地点の空き缶に当てる。
**コース [#j508b7aa]
私は下の第2コースを選んだ。
#ref(2018b/Member/riho/Mission2/2018b-mission2.jpg,80%,コースの通る道順)

+ロボットを正方形Y内におき、Lをスタート
+Eを一時停止して直進
+Iを一時停止して左折
+Hを直進
+Kを直進
+Jを左折
+Cを一時停止して右折
+Eを一時停止して直進
+Gを一時停止して直進
+長方形X内に入って停止

矢印の先端にある交差点で1秒一時停止をする必要がある。
*ロボットの説明 [#z2fdfc94]
今回はロボットに光センサ、超音波センサ、ボールを投げるアームをつける必要があった。

**光センサ [#mb6a681e]
光センサは、あまりにも床と近すぎるとうまく測定できないということを先生からアドバイスをいただいたので、光センサの値を見ながら白色と黒色の値がはっきり変わるように床と適度な間隔をあけた。
#ref(2018b/Member/riho/Mission2/IMG_1136.jpg,30%,光センサの写真)
**超音波センサ [#w042cfbb]
超音波センサは、床からの距離が高すぎて物体を測定できないということがないように、センサをつける高さを意識した。
#ref(2018b/Member/riho/Mission2/IMG_1046.jpg,30%,ロボットの全体画像)

**アーム [#u107343d]
アームを閉じてボールを挟んで運び、アームを広げてロボットの機体をぶつけることで、蹴るようにボールを飛ばすものにした。

#ref(2018b/Member/riho/Mission2/IMG_1047.jpg,40%,ロボットがアームを広げている時)

*定義したプログラムの説明 [#e848e9f6]
**ライントレース [#y11dff08]
まず以下のものを定義した。
 #define turn_l1 OnFwd(OUT_B,30);OnRev(OUT_C,30);  //左旋回(左折よりも左に進む)
 #define turn_r1 OnFwd(OUT_C,30);OnRev(OUT_B,30);  //右旋回(右折よりも右に進む)
 #define turn_l0 OnFwd(OUT_B,30);Off(OUT_C);  //左折
 #define turn_r0 Off(OUT_B);OnFwd(OUT_C,30);  //右折
 #define go_s OnFwd(OUT_BC,30); //直進
 #define stoop Off(OUT_BC);  //止まる
 #define speed 50  //モータを50%の力で動かす
 #define speed_s 30  //モータを30%の力で動かす
 #define tomaru Wait(1000) //1秒一時停止するため
線に沿って走るために、白と黒を認識して進むプログラムを作成した。
「真っ白」「白よりの境界線」「白と黒の境界線」「黒よりの境界線」「真っ黒」の5つに分けて値を光センサで測定すると、下の図のような結果になった。

#ref(2018b/Member/riho/Mission2/IMG_1048.jpg,70%,光センサで測定した値)

測定より、真っ黒は31以下、黒よりの境界線は32~41、白と黒の境界線は42~47、白よりの境界線は48~54、真っ白は55以上だということがわかった。

このロボットは線の左側に沿って進むように作るので、下の図のように、色を測定して黒ければ黒いほど左に、白ければ白いほど右に進まなくてはならない。

#ref(2018b/Member/riho/Mission2/IMG_1049.jpg,70%,光センサで測定した値による進む方向)

よって、測定値を利用してif文を使った以下のようなプログラムを作った。

 if(SENSOR_1>55){   //もし光センサで55以下の値(真っ白)が測定されると
     turn_r1;                        //右旋回
 }else if(SENSOR_1>48){     //もし光センサで48~54の値(白よりの境界線)が測定されると
     turn_r0;                         //右折
 }else if(SENSOR_1>42){   //もし光センサで42~47の値(白と黒の境界線)が測定されると
     go_s;       //直進
 }else if(SENSOR_1>32){ //もし光センサで32~41の値(黒よりの境界線)が測定されると
     turn_l0;                     //左折                       
 }else{                            //もし光センサで31以下の値(真っ黒)が測定されると
     turn_l1;      //左旋回
 }

これは、同じ要領で「白」「白と黒の境界線」「黒」の3つに分けてもロボットは動きますが、細かく分けるとより滑らかに動くので5つに分けたプログラムを作った。

これを応用して交差点を見つけたら終わるプログラムを作る。上記のような線をジグザグしながら動くプログラムの場合、交差点では以下の図のように光センサは「真っ黒」を長い時間測定し続ける。

#ref(2018b/Member/riho/Mission2/IMG_1133.jpg,60%,交差点を認知する仕組みの説明図)

図の赤い矢印は全て同じ長さである。直線上にある矢印は、白から黒、黒から白の部分を走っているが、交差点に入っている矢印だけ、矢印全体が黒い部分を走っている。これより、ロボットが交差点に入った時だけ光センサはずっと「真っ黒」を測定し続ける。

これを利用して以下のような交差点に入ると終わるプログラムを作る。

 void tuuzyou()
 {
     SetSensorLight(S1);
     long t0;                //long型のt0を定義                                                  
     t0=CurrentTick();   //t0は現在の時間である
     while(CurrentTick()-t0<90){ //今からt0になるまで0.09秒経つまでの間プログラムを繰り返す
         if(SENSOR_1>55){ //もし光センサで55以上の値(真っ白)が測定されると
             turn_r1;   //右旋回
             t0=CurrentTick();  //t0を更新する                       
         }else if(SENSOR_1>48){ //もし光センサで48~54の値(白よりの境界線)が測定されると
             turn_r0; //右折
             t0=CurrentTick(); //t0を更新する
         }else if(SENSOR_1>42){ //もし光センサで42~47の値(白と黒の境界線)が測定されると
             go_s; //直進
             t0=CurrentTick(); //t0を更新する
         }else if(SENSOR_1>32){ //もし光センサで32~41の値(黒よりの境界線)が測定されると
             turn_l0; //左折
             t0=CurrentTick(); //t0を更新する                                                         
         }else{ //もし光センサで31以下の値(真っ黒)が測定されると
             turn_l1;  //左旋回
         }
     }
 }


これは、「真っ黒」以外が測定されるたびにt5を更新すると現在の時間とt5の差が0.09秒にならず、while文のプログラムを繰り返し続ける。しかし交差点に入ると、「真っ黒」が測定されて、t5は更新されないため、現在の時間とt5の差が0.09秒以上になるのでwhile文の繰り返しが終わり、プログラムが終わる。

**交差点を右折するプログラム [#cc0792ce]
ロボットは線の左を沿って走っているので、交差点を右折するには一回「真っ黒」の領域を走らなければならない。上で説明した通常のライントレース用のプログラムでは右折できないので、右折ようのプログラムを作った。
ロボットは線の左を沿って走っているので、交差点を右折するには一回「真っ黒」の領域を走らなければならない。上で説明した「tuuzyou」のプログラムでは右折できないので、右折ようのプログラムを作った。

#ref(2018b/Member/riho/Mission2/IMG_1051.jpg,70%,右折するためのセンサの値)

上の図のように、交差点の右の線の奥側(線の左側)に行きたい。そのために交差点から、センサが「黒よりの境界線」の値である48を測定するまで右折をするプログラムを作成した。

 void usetu()
 {
     SetSensorLight(S1);
     while(SENSOR_1>48){ //光センサが48以上の時以下のプログラムを繰り返す
         turn_r0 //右折
     }
 }

**交差点を直進するプログラム [#q019f618]
交差点で一時停止した後にそのまま直進するプログラム。

 void massugu()  //直線の交差点で直進する時
 {
     turn_r1; //右旋回
     Wait(60); // 0.06秒そのまま実行する
     go_s; //直進
     Wait(150); //0.15秒そのまま実行する
 }

 void massugu2()  //円の弧にある交差点で直進する時
 {
     turn_r1; //右旋回
     Wait(200); //0.02秒そのまま実行する
     go_s; //直進
     Wait(300); //0.03秒そのまま実行する
 }

交差点を見つけてロボットが「tuuzyou」のプログラムを終えると、ロボットの機体が少し左に曲がってプログラムが終わるので「turn_r1(右旋回)」によって右に機体を戻さないとラインから外れてしまうので、「turn_r1(右旋回)」はロボットの機体をライン上に戻すための微調整である。

上の交差点を右折するプログラムのように、時間で決めるのではなく、センサで測定した数値になるまで直進するというプログラムも考えたが、以下のようなことが起こり、100%成功する保証はなかったので、ある時間だけ実行するプログラムにした。

#ref(2018b/Member/riho/Mission2/IMG_1054.jpg,70%,直進するときに起こった問題)


**急なカーブでも止まらないプログラム [#x2620670]
通常のライントレース用のプログラムだと、コースにあるDの直角の曲がり角や、FとGの途中にある急カーブでは、「真っ黒」の領域を0.09秒以上通るため、一時停止してしまう。そのため、急カーブに差し掛かる時間をあらかじめ時間を測って置いて、その時間内だけは「真っ黒」の領域を0.09秒以上通っても一時停止しないプログラムを作った。

***Dの直角の曲がり角やFとGの途中にある急カーブでも止まらないプログラム [#xabd0d74]

 void special()
 {
     SetSensorLight(S1);
     long t1;  //long型のt1を定義                                                                     
     t1=CurrentTick() //t1は現在の時間である 
     while(CurrentTick()-t1<20000){  //今からt1になるまで20秒経つまでの間プログラムを繰り返す
         if(SENSOR_1>55){  //もし光センサで55以上の値(真っ白)が測定されると
             turn_r1; //右旋回                                                    
         }else if(SENSOR_1>48){  //もし光センサで48~54の値(白よりの境界線)が測定されると
             turn_r0; //右折
         }else if(SENSOR_1>42){ //もし光センサで42~47の値(白と黒の境界線)が測定されると
             go_s; //直進
         }else if(SENSOR_1>33){ //もし光センサで33~41の値(黒よりの境界線)が測定されると
             turn_l0; //左折                                                          
         }else{ //もし光センサで32以下の値(真っ黒)が測定されると
             turn_l1; //左旋回
         }                                                                             
     }
 }

specialのプログラムはtuuzyouのプログラムとは違い、毎回t0(tuuzyouのプログラムではt0だが、specialのプログラムではt1のこと)を更新していない。毎回更新すると光センサで測定するたびにt0が更新されるため、specialのプログラムで更新してしまうと、今からt1になるまで20秒以上経つ時は永久にこなくなるため、注意する。

***KとJの途中にある交差点でも止まらないプログラム [#r13b8e5c]

 void special2()
 {
     SetSensorLight(S1);
     long t2;  //long型のt2を定義                                                                       
     t2=CurrentTick() //t2は現在の時間である 
     while(CurrentTick()-t2<7000){   //今からt2になるまで7秒経つまでの間プログラムを繰り返す
         if(SENSOR_1>55){  //もし光センサで55以上の値(真っ白)が測定されると
             turn_r1; //右旋回                                                    
         }else if(SENSOR_1>48){  //もし光センサで48~54の値(白よりの境界線)が測定されると
             turn_r0; //右折
         }else if(SENSOR_1>42){ //もし光センサで42~47の値(白と黒の境界線)が測定されると
             go_s; //直進
         }else if(SENSOR_1>33){ //もし光センサで33~41の値(黒よりの境界線)が測定されると
             turn_l0; //左折                                                          
         }else{ //もし光センサで32以下の値(真っ黒)が測定されると
             turn_l1; //左旋回
         }                                                                             
     }
 }
***簡潔なのプログラム [#pe42b7e5]
上の2つのプログラムは、プログラムを行う時間が違うだけなので、「20000」や「7000」となっている数字を文字に置き換えて、メインプログラムでその文字に時間を入れれば、一つのプログラムにできたと、このレポートを書いていて気づいた。

 void special3(t_t) //メインプログラムで()に数字を入れると、以下のプログラムのt_tにその数字が代入される
 {
     SetSensorLight(S1);
     long t1; //long型のt1を定義                                                                      
     t1=CurrentTick() //t1は現在の時間である 
     while(CurrentTick()-t1<t_t){  //今からt1になるまでt_t/1000秒(t_tはメインプログラムで入力)経つまでの間プログラムを繰り返す
         if(SENSOR_1>55){  //もし光センサで55以上の値(真っ白)が測定されると
             turn_r1; //右旋回                                                    
         }else if(SENSOR_1>48){  //もし光センサで48~54の値(白よりの境界線)が測定されると
             turn_r0; //右折
         }else if(SENSOR_1>42){ //もし光センサで42~47の値(白と黒の境界線)が測定されると
             go_s; //直進
         }else if(SENSOR_1>33){ //もし光センサで33~41の値(黒よりの境界線)が測定されると
             turn_l0; //左折                                                          
         }else{ //もし光センサで32以下の値(真っ黒)が測定されると
             turn_l1; //左旋回
         }                                                                             
     }
 }


これでメインプログラムで書くときに、
 special3(20000);

や

 special3(7000);
と書けば、1つのプログラミングでできたと思う。

**超音波センサ [#w4ba0799]
このミッションでは、空き缶にボールを当てなくてはならなかったので、超音波センサで空き缶を探しす必要があった。そのプログラムが以下である。

 const float diameter=5.54;  //タイヤの直径(cm)                                              
 const float track=10.35; //タイヤのトレッド幅(cm)
 const float pi=3.1415; //円周率
と定義し、

 void fwdDist(float d)
 {
     long angle;                                                         
     angle= d/(diameter*pi)*360.0;
     RotateMotorEx(OUT_BC,speed_s,angle,0,true,true);                      
 }
 
 void turnAng(long ang) //角度ang度の時計回りの旋回をする
 {
     long angle;
     angle=track/diameter*ang;
     RotateMotorEx(OUT_BC,speed_s,angle,100,true,true);
 }                                                                     
 
 int searchDirection(long ang)  //現在の方向を中心にang度の範囲で探す                                      
                                                  //障害物までの距離を返す
 {
     long angle,tacho_min=0,tacho_corr;
     int d_min;
 
     d_min=300; //仮の最小値
 
     angle=(track/diameter)*ang;  //旋回角度からタイヤの回転を計算する                                                
     turnAng(ang/2); //指定された角度の半分を旋回する
     ResetTachoCount(OUT_BC);       //角度計測をリセット                                       
 
     OnFwdSync(OUT_BC,speed_s,-100); //反時計回りに旋回する
     while(MotorTachoCount(OUT_B)<=angle){
         if(SensorUS(S4)<d_min){
             d_min=SensorUS(S4); //仮の最小値を更新する
             tacho_min=MotorTachoCount(OUT_B);
         }
     }                                                                           
     OnFwdSyncEx(OUT_BC,speed_s,100,RESET_NONE);
     until(MotorTachoCount(OUT_B)<=tacho_min||SensorUS(S4)<=d_min);             
 
     Wait(14); //微調整
     Off(OUT_BC);Wait(500);
     return d_min;
 }
上のプログラムは、ロボットが回転することによって、ロボットの周りにある物体との距離を測定し、それをd_minとする。d_minは測定されたら更新されてることで、別の物体がロボットの周りにあったらその物体との距離もd_minとして記録する。そして、測定が終わったら、測定したd_minの中で1番小さい値を見つけて、d_minが1番小さかった物体の方向へ向く。

#ref(2018b/Member/riho/Mission2/IMG_1053.jpg,70%,超音波センサの仕組み)

空き缶より別のものがロボットの近くにあると、そっちの方向を向いてしまうのでそこに注意することが必要である。

**ボールを蹴るプログラム [#n6ff0b2e]
今回のロボットは、ボールを置いてロボットで押すことでボールをけるようにしたので、ロボットを後退や前進させるてシュートさせた。

 void shoot()
 {                                                                              
     OnRev(OUT_BC,speed); //ロボットを後退
     Wait(1400);  //1.4秒そのまま実行する
 
     OnFwd(OUT_A,speed_s); //アームを広げる
     Wait(250);  //0.25秒そのまま実行する
     Off(OUT_A);     //アームの動きを止める                     
     OnFwd(OUT_BC,speed); //ロボットを前進させてボールを蹴る
     Wait(1400);  //1.4秒そのまま実行する
     OnRev(OUT_A,speed_s); //アームを閉じる
     Wait(250); //0.25秒そのまま実行する
     Off(OUT_A); //アームの動きを止める
     turn_r1; //右折
     Wait(600); //0.6秒そのまま実行する
 }

*メインプログラム [#f8a1b15c]
メインプログラムは上の定義したプログラムを組み合わせるなどして完成させた。

交差点では一時停止する必要があった。その仕組みは、通常のライントレース用のプログラムで「真っ黒」の領域を0.09秒以上通る所(これが交差点)を見つけ通常プログラムを終了させ、その下にモータを停止させるプログラム(stoop;)と一秒間続けるプログラム(Wait(1000))を置くことで交差点で1秒間一時停止する。

 task main()
 {
     go_s; //長方形Yから直線に行くまで直進
     tomaru; // 微調整
     tuuzyou(); //通常のライントレース
     stoop;    //Eで一時停止                                            
     tomaru; //1秒間止まる
     massugu(); //Eを直進
     tomaru; //一秒間直進
     tuuzyou(); //通常のライントレース
     stoop; //Iで一時停止
     tomaru; //1秒間止まる
     turn_l1; //左折するための微調整
     Wait(200); //0.2秒そのまま実行する
     tuuzyou(); //通常のライントレース
     massugu2(); //Hで交差点を見つけたが止まらず直進
     SetSensorLowspeed(S4); //ここから缶を探す
     int d=searchDirection(360); //360度回って缶を探す
     if (d>38){ //もし、缶との距離が38cm以上離れていたら
         fwdDist(d-38.0);      //38cmの距離まで移動する                              
     }
     shoot(); //ボールを蹴る
     tuuzyou(); //通常のライントレース
     massugu2(); //Kで交差点を見つけたが止まらず直進
     special2(); //Jで交差点を見つけたが止まらずライントレースを続ける
     tuuzyou(); //通常のライントレース
     stoop;  //Cで一時停止                                           
     tomaru; //1秒間止まる
     usetu(); //Cを右折する
     special(); //通常のライントレースのプログラムだとDの直角の曲がり角で止まってしまうのでDの曲がり角を無視してライントレースを続ける
     tuuzyou(); //通常のライントレース
     stoop; //Eで一時停止
     tomaru; //1秒間止まる
     massugu(); //Eを直進する
     tomaru; //1秒間直進する
     special(); //通常のライントレースのプログラムだとFとGの間の急カーブで止まってしまうので急カーブを無視してライントレースを続ける
     tuuzyou(); //通常のライントレース
     stoop; //Gで一時停止
     tomaru; //1秒間止まる
     massugu(); //Gを直進する
     tomaru; //1秒間直進
     tuuzyou(); //通常のライントレース
     massugu(); //長方形X手前の線を見つけるがそのまま直進する
     Wait(2000); //2秒直進して長方形Xの中に入る
     stoop; //終了
 }


*最後のまとめ [#a2a66780]
**反省 [#qb43d62d]
-ボールを蹴るプログラムではロボット自体が動くことでボールを蹴るというプログラムにしたので、残りの電池の量や少しの環境の変化で線から外れてしまうこともあり、すごく不安定だった → ロボットは動かないでボールを落とす様なロボットにすればよかった。

-上でも述べたが、急カーブではとまらないプログラムを2つ作るのではなく、数字の部分を文字に置くなどしたら、プログラムを1つにできてスッキリする → 次回からはプログラムを作る際、なるべく同じことを何回も書かない様に、省略できるところはないか考えながら作る。

-超音波センサで缶を探す時に缶の方向に向く時と向かない時があった → 超音波センサをロボットに取り付ける際に、正面から平行に設置できていなかったかもしれない。正直、正確な理由はわからないので、超音波センサについて復習をする。


**感想 [#w05a04ce]
今回のミッションでは課題を達成することだけに必死で、あまり自分らしさや自己流の工夫ができなかった。ロボットに関してもプログラムに関しても、「注目ポイントはありますか?」と聞かれても正直答えるのが難しい。次回は、ただミッションを達成させるロボットを作るのではなく、どう作れば簡潔か、どうすれば100%絶対成功するロボットやプログラムが作れるかをもっと考えながら作りたい。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS