[[2013b/Member]] #contents *コースと攻略方法 [#y2bdf8ae] ●コースについて &ref(2013b/Member/Redcicudu/Mission1/らいん1.jpg,39.9KB,何とも苦労したコースである); コースは写真の通りである。今回は空き缶をコース上に配置し、それをどかしてもとに位置に戻しつつゴールを目指さなくてはならない。このコースの特徴を以下に示す。 +交差点は合計4回通過しなくてはならない。 +急なカーブや直角なカーブがあり、プログラムによっては交差点と誤認をしてしまう。 +輪っか状になっている部分が二つあり、こちらもプログラムによっては交差点と誤認してしまう。 +空き缶は交差点から10センチ以上離して設置しなくてはならない。 以上が主な特徴である。 ●攻略方法 まず、私のプログラムはテキストの例を参考に作られているため、急なカーブや直角のカーブを見分けることができない。そこで、交差点を渡った回数を数え、その回数ごとにトレースする方法を切り替えることでこのコースを攻略することにした。 *ロボットについて [#vb01f325] &ref(2013b/Member/Redcicudu/Mission1/ろぼ4.jpg,36.4KB,いいロボだろう?); ロボットは写真の通り少し背高ノッポになってしまった。それはできる限り縦と横に広がってしまわないように配慮したからだ。縦と横に広いとセンサーを上手くつけられなかったり、旋回するのに余計タイヤを回さなくてはならなくなってしまった、という経験を生かしているつもりである。また、重心を中心にするという目的もある。また、初期のものから若干の改良を加えており強度的に問題のないパーツを極力外すことで軽量化を行っている。一応パーツは減ったもののセンサーの位置やアームの動作は初期のものとなんら変わりない。 &ref(2013b/Member/Redcicudu/Mission1/ろぼ3.jpg,70.8KB,みどころですよ!); アームは両側からはさむ形にした。広げきっているときはできるだけ邪魔にならないように胴体に近くなるように調整した。アーム用のモーターの回転軸が後方にあるため、長いリンケージロッドを利用しているのが特徴である。リンケージロッドを使用することによって、ギヤを使用するときとは違いモーターの動きがダイレクトに伝わるし、ギヤをマウントするためのパーツを新たにつける必要もないので構造の簡略化と軽量化に寄与している。ロッドの接合部分は三次元的な動作に対応しており、なかなか工夫したポイントの一つでもある。 *プログラムについて [#q17d2328] このプログラムはテキストのプログラムを参考にしている。私がプログラムに不慣れであったためにとんでもなく長いプログラムになってしまった。そこで定義やサブルーチンごとに区切って説明していく。 ●定義 主にモーター関係のものが多い、ついでなのでカウンターの設定も一緒にしておいた。 #define BB 30 //黒線の上なのかを識別するための閾値(しきいち)、この値を元に五段階の動作 を行うようにした #define BW 38 #define WB 52 #define WW 45 #define OnRL(speedR,speedL) OnFwd(OUT_A,speedR);OnFwd(OUT_B,speedL); //出力を入力すればモ ーターを動かせるように定義 #define FWD OnRL(25,25); //前進をFWDとして定義 #define T_LEFT1 OnRL(30,0); //左折 サブルーチンCROSS_1,2,4,5,6,7で使用 #define T_RIGHT1 OnRL(-10,20); //右折 #define Q_LEFT1 OnRL(25,-15); //左旋回 #define Q_RIGHT1 OnRL(-15,15); //右旋回 #define T_LEFT2 OnRL(20,0); //左折 サブルーチンCROSS_3で使用 #define T_RIGHT2 OnRL(8,25); //右折 #define Q_LEFT2 OnRL(20,-20); //左旋回 #define Q_RIGHT2 OnRL(0,25); //右旋回 #define STEP 5 //一回判断で行う動作の長さ #define nMAX 30 //交差点識別用カウンターの最大値、ある動きをこの数値以上行った時を交差点 として識別 #define cross_line RotateMotorEx(OUT_AB,speed_ab,75,0,true,true); //交差点通過の動作 #define RMC(speed_c,-rad_c) RotateMotor(OUT_C,speed_c,rad_c); //アームの動きを定義 #define speed_c 25 //アームの出力 #define rad_c 140 //アームの動作範囲 #define ARM1 RMC(speed_c,rad_c); //アームが空き缶をつかむ動作をARM1として定義 #define ARM2 RMC(speed_c,-rad_c); //アームが空き缶をはなす動作をARM2として定義 #define speed_ab 30 //サブルーチンCATCH_CANの時のモーターの出力 int Online1=0; //交差点識別用カウンター、ある動作を数えるためのカウンターであり動作はサブルーチンによって異なる int Online2=0; //交差点を渡った回数を数えるカウンター、Online2~7まですべて同じ用途 int Online3=0; // int Online4=0; // int Online5=0; // int Online6=0; // int Online7=0; // int OnRightangle=0; //ゴール付近のT字路を通過した回数を数えるカウンター、 OnRightangle1と2は同じ用途 int OnRightangle2=0; // このプログラムは交差点の識別やサブルーチンの切り替えタイミングを計るためにカウンターを利用している、ある動作を行った回数を記録するすることであらかじめ与えておいた数値を超える回数を記録したら次の動作やサブルーチンに移行するように設定してある。 ●空き缶を掴むサブルーチン sub CATCH_CAN() //空き缶をつかむ一連の動作のサブルーチン { RotateMotorEx(OUT_AB,speed_ab,70,0,true,true); //掴む位置まで移動 ARM1; //缶を掴む Off(OUT_C); //小休止、すぐに回頭すると微妙に位置がズレてしまうため Wait(500); RotateMotorEx(OUT_AB,speed_ab,240,0,true,true); //回頭位置まで移動 RotateMotorEx(OUT_AB,speed_ab,145,-100,true,true); //90度回頭 Off(OUT_AB); //小休止 Wait(500); RotateMotorEx(OUT_AB,speed_ab,330,0,true,true); //回頭位置まで移動(その2) Off(OUT_AB); //小休止 Wait(500); RotateMotorEx(OUT_AB,speed_ab,360,100,true,true); //180度回頭 ARM2; //缶を離す Off(OUT_C); //小休止 Wait(500); RotateMotorEx(OUT_AB,-speed_ab,200,0,true,true); //缶から離れる RotateMotorEx(OUT_AB,speed_ab,410,100,true,true); //回頭して前を向く } 私の場合は空き缶を直角カーブ上においたので空き缶をもとに戻す際に90度回頭しなければならなかったので、比較的正確に動作させることができるようにサーボモーターの機能を利用した。 ●CROSS_1 sub CROSS_1() //はじめのT字路を通過するためのサブルーチン { while(OnRightangle<1){ //T字路を通過したらサブルーチンCROSS_2へ移行するように設定 while(Online1<nMAX){ //右旋回(Q_RIGHT1)をnMAX回以上行ったら交差点とみな し、通過するように設定 if(SENSOR_1<BB){ //黒線上では右旋回を行う Q_RIGHT1; Online1++; //右旋回の回数をカウント } else { if(SENSOR_1<BW){ //黒線よりのところでは右折を行う T_RIGHT1; } else if(SENSOR_1<WB){ //黒線と紙面の中間ではまっすぐ前進する FWD; } else if(SENSOR_1<WW){ //紙面よりのところでは左折を行う T_LEFT1; } else { //紙面上では左旋回を行う Q_LEFT1; } Online1=0; //カウンターをリセット } Wait(STEP); //左折や右折などを一回行うごとの長さ } RotateMotorEx(OUT_AB,speed_ab,50,0,true,true); //T字路を通過 RotateMotorEx(OUT_AB,speed_ab,180,100,true,true); //T字路を通過 OnRightangle++; //T字路を通過した回数をカウント Online1=0; //カウンタをリセット } } 普通にライントレースさせたのではT字路を上手く曲がることができなかったので専用のサブルーチンを作った。以降のライントレースに関するサブルーチンは類似する点が多いので各命令に関する説明を少し省略してある。このサブルーチンにおいても90度回頭する動作を要求されるのでサーボモーターの機能を利用した。また、プログラムの説明を呼んでいただけると分かると思うがライントレースといってもこのプログラムは黒線そのものをたどるのではなく、黒い線の「縁」をトレースしていくのである。 ●CROSS_2 sub CROSS_2() //一回目の交差点を渡るまでのサブルーチン { while(Online2<1){ //交差点を一回渡ったらサブルーチンCROSS_3へ移行するように設定 while(Online1<nMAX){ if(SENSOR_1<BB){ Q_RIGHT1; Online1++; //カウントするのは右旋回 } else { if(SENSOR_1<BW){ T_RIGHT1; } else if(SENSOR_1<WB){ FWD; } else if(SENSOR_1<WW){ T_LEFT1; } else { Q_LEFT1; } Online1=0; } Wait(STEP); } cross_line; //交差点を渡る Online2++; //交差点を渡った回数をカウント Online1=0; //カウンタをリセット } } 基本的にCROSS_1と変わりがない、カウンターの数値が上限を越すとT字路を曲がる動作の代わりに交差点を渡る動作が行われる。 ●CROSS_3 sub CROSS_3() //輪っかになっているところを動くサブルーチン、黒線の左の縁に沿って動くので前のものとは逆になってる { while(Online3<1){ //交差点を一回渡ったらサブルーチンCROSS_4へ移行するように設定 while(Online1<nMAX){ if(SENSOR_1<BB){ //黒線上では左旋回を行う Q_LEFT2; Online1++; //今回は動きが逆になるので左旋回の回数をカウントする } else { if(SENSOR_1<BW){ //黒線よりのところでは左折を行う T_LEFT2; } else if(SENSOR_1<WB){ //黒線と紙面の中間ではまっすぐ前進する FWD; } else if(SENSOR_1<WW){ //紙面よりのところでは右折を行う T_RIGHT2; } else { //紙面上では右旋回を行う Q_RIGHT2; } Online1=0; } Wait(STEP); } cross_line; Online3++; Online1=0; } } 輪っかを動くサブルーチン。輪っかの内側をトレースしてしまうとカウンターが動作を余計にカウントしてしまい交差点と誤認してしまう。そこで輪っかの外側をトレースするようにサブルーチンを作った。 ●CROSS_4 sub CROSS_4() { while(Online4<1){ //交差点を1回渡ったら、CROSS_5へ移行するように設定 while(Online1<nMAX){ if(SENSOR_1<BB){ Q_RIGHT1; Online1++; } else { if(SENSOR_1<BW){ T_RIGHT1; } else if(SENSOR_1<WB){ FWD; } else if(SENSOR_1<WW){ T_LEFT1; } else { Q_LEFT1; } Online1=0; } Wait(STEP); } cross_line; Online4++; Online1=0; } } こちらはCROSS_2とほとんど同じなので説明は割愛。 ●CROSS_5 sub CROSS_5() //このサブルーチンで交差点を識別するためにカウントするのは右折(T_RIGHT1) の動作 { while(Online5<1){ //交差点を渡ったらCROSS_6へ移行するように設定 while(Online1<nMAX){ if(SENSOR_1<BB){ Q_RIGHT1; } else if(SENSOR_1<BW){ T_RIGHT1; Online1++; //右折の回数をカウントする } else { if(SENSOR_1<WB){ FWD; } else if(SENSOR_1<WW){ T_LEFT1; } else { Q_LEFT1; } Online1=0; } Wait(STEP); } cross_line; Online5++; Online1=0; } } このサブルーチンもほとんどはCROSS_2と同じなのだが一つ違う点はカウントする動作は「右折」の動作であるということ。このサブルーチンが向かう先の交差点はコースの関係上完全な90度の交差点ではないため右旋回の動作を交差点で行ってくれないので右折の動作をカウントすることで解決した。 ●CROSS_6 sub CROSS_6() //空き缶をつかむ直前のライントレース、前進を行った回数をカウントして空き缶を掴むサブルーチンへと移行する { while(Online6<1){ //空き缶を動かした後はCROSS_7へ移行するように設定 while(Online7<180){ //前進を180回行ったら超音波センサーで空き缶を探すよう に設定 if(SENSOR_1<BB){ Q_RIGHT1; } else if(SENSOR_1<BW){ T_RIGHT1; } else if(SENSOR_1<WB){ FWD; Online7++; //前進した回数をカウントする } else { if(SENSOR_1<WW){ T_LEFT1; } else { Q_LEFT1; } Online7=0; } Wait(STEP); } OnFwdSync(OUT_AB,20,0); //空き缶を感知するまで前進 until(SensorUS(S2)<=15); //空き缶を15cm手前以内に感知したらサブルーチンCATCH_CANへ 移行 CATCH_CAN(); //空き缶をつかむ一連の動作のサブルーチン Online6++; Online1=0; } } 空き缶を掴む直前のライントレース、あるいは掴む動作とライントレースとの切り替えのためのサブルーチンといってもいい、超音波センサーでしっかりと空き缶を捉え尚且つアームでがっちりと掴むためにはライントレースと超音波センサーで探知する動作をスムーズに切り替える必要があったあったためこのサブルーチンでは「前進」の動作をカウントして探知の動作へと切り替えている。 ●CROSS_7 sub CROSS_7() //ゴール手前のT字路を通過するためのサブルーチン { while(OnRightangle2<1){ //直角を通過 while(Online1<nMAX){ if(SENSOR_1<BB){ Q_RIGHT1; Online1++; } else { if(SENSOR_1<BW){ T_RIGHT1; } else if(SENSOR_1<WB){ FWD; } else if(SENSOR_1<WW){ T_LEFT1; } else { Q_LEFT1; } Online1=0; } Wait(STEP); } RotateMotorEx(OUT_AB,speed_ab,40,0,true,true); //T字路を通過 RotateMotorEx(OUT_AB,speed_ab,180,100,true,true); //T字路を通過 OnRightangle2++; //T字路を通過した回数をカウント Online1=0; //カウンタをリセット } } こちらは基本的にCROSS_1と同じサブルーチンなので説明を割愛する。 ●メインタスク ここで今までのサブルーチンを利用して一連の動作をすべてつなげている。 task main() { SetSensorLight(S1); //光センサーを設定 SetSensorLowspeed(S2); //超音波センサーを設定 CROSS_1(); //T字路まで CROSS_2(); //輪っかの直前 OnFwd(OUT_A,50); //反対の縁をトレースするので反対側へ移動 Wait(500); CROSS_3(); //輪っかを進む OnFwd(OUT_B,50); //またトレースする縁が変わるので反対側へ移動 Wait(600); CROSS_4(); //短い直線を進む CROSS_5(); //2つ目の輪っかを進む CROSS_6(); //空き缶の直前までトレース CROSS_7(); //ゴールのT字路まで CROSS_2(); //ゴールを目指す } これがメインタスクである。多分文字ばかり読んでいても分かりにくいと思うので写真を加工して簡単な図を作成してみた。 &ref(2013b/Member/Redcicudu/Mission1/らいん3.jpg,48.9KB,おわかりいただけただろうか?); 上の図において赤い線はCROSS_1、オレンジの線はCROSS_2、黄色い線はCROSS_3、緑の線はCROSS_4、青い線はCROSS_5、紫の線はCROSS_6、茶色の線はCROSS_7、ピンクの線は空き缶を探知するまでの動作となっている。この図のように輪っかになっている部分は内側とトレースせず、外側をトレースするようにできている。そうすることで交差点と急なカーブを誤認する事なくゴールを目指すことができるのだ。 *考察 [#m2fc770a] プログラムに関しては考え方自体はシンプルで、自分なりに「交差点」という難敵を克服したしつもりである。しかし、プログラムに不慣れであったためにとてつもなく長くなってしまった。もっと定義の仕方やサブルーチンの使い方を考え直そうと思った。 また、超音波センサーが思った以上の曲者で、視野が狭いうえに動きながら検知を行うとスムーズに次の動作へ移れなったりとなかなか手を焼かされてしまった。こちらの改善もまだまだ必要だと感じた。 ロボットの製作はほとんど自分が自由勝手にやらせてもらった。講義に出席するたびにカタチの違うロボットのプログラムを行う羽目になった相方のpianoman君にはとても申し訳ないと思っている。それでもロボット自体に関しては自分なりにベストを尽くせたと考えている。 改善点を挙げるとするならば、 +プログラムを見やすくシンプルにする。 +超音波センサーによる検知の精度を向上させる、もしくはそれ以外の方法で空き缶を検知する。 +ロボットをよりコンパクトに、低重心に、かつ部品点数を少なくする。 以上の三つを改善すればよりよいロボットに生まれ変わると思う。