目次 #contents *課題2 [#n2cefe5f] 下の図のようなコースを各チームで作成し、「ミッション」を遂行するためのロボットを作成せよ。 **ミッション [#fcc071b5] 次のいずれかのコースで黒い線に沿って動き、途中でボールをゴール付近に立てた350mlの空き缶(黄色で表示)に当てるロボットを製作せよ。 また、ボールはロボットが弧IHKJ上にある時にP地点の空き缶に当てる。 **コース [#j508b7aa] 私は下の第2コースを選んだ。 #ref(2018b/Member/riho/Mission2/2018b-mission2.jpg,80%,コースの通る道順) 矢印の先端にある交差点で1秒一時停止をする必要がある。 *ロボットの説明 [#z2fdfc94] 今回はロボットに光センサ、超音波センサ、ボールを投げるアームをつける必要があった。 #ref(2018b/Member/riho/Mission2/IMG_1046.jpg,40%,ロボットの全体画像) この写真の説明は、部品ごといかにまとめてある。 **光センサ [#mb6a681e] 光センサは、あまりにも床と近すぎるとうまく測定できないということを先生からアドバイスをいただいたので、床スレスレにならない程度に高さを出した。 **超音波センサ [#w042cfbb] 超音波センサは、床からの距離が高すぎて物体を測定できないということがないように、センサをつける高さを意識した。 **アーム [#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つに分けたプログラムを作った。 上のプログラムを少し改良したものが線に沿って進む基本的なプログラムである。 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; //左旋回 } } } このプログラムは「真っ黒」の領域を0.09秒通るとこのプログラムが終了するようになっている。 **交差点を右折するプログラム [#cc0792ce] ロボットは線の左を沿って走っているので、交差点を右折するには一回「真っ黒」の領域を走らなければならない。上で説明した通常のライントレース用のプログラムでは右折できないので、右折ようのプログラムを作った。 #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秒そのまま実行する } 上の交差点を右折するプログラムのように、時間で決めるのではなく、センサで測定した数値になるまで直進するというプログラムも考えたが、以下のようなことが起こり、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; } 上のプログラムは、メインプログラムで回転する範囲を「360」とすれば、ロボット自体が360度回って、超音波センサを用いて近くにある物との距離(プログラムではd_min)を測る。 #ref(2018b/Member/riho/Mission2/IMG_1053.jpg,70%,超音波センサの仕組み) d_minは物体を見つけるたびに更新されていき、一番距離の近い物体の方向を向く。これより、空き缶より別のものがロボットの近くにあると、そっちの方向を向いてしまうのでそこに注意することが必要である。 **ボールを蹴るプログラム [#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%絶対成功するロボットやプログラムが作れるかをもっと考えながら作りたい。