tomato 工学部電気電子工学科 ロボット製作責任者、yno8様、satoTなど人を集める、ロボットの材料を持って帰る。好きな食べ物はメーヤウのイエローカレーとか、ローソンの隣にあるお菓子屋のシュークリーム
satoT 農学部応用生命科学科 プログラミング製作責任者、ロボット本体を持ってかえる、オンラインゲーム担当、インフィニットストラトスも担当。好きな食べ物はお母さんが作った肉じゃが
概要 スタート地点から、ライントレースを使ってコースを時計回り、反時計回りに一周ずつに一周する。その際、空き缶が二つ置かれ、その缶をどかし、元の位置に缶を戻さなければならない。缶の位置は自分たちで決める。 ※ライントレースとは、床面に描いたラインをロボットが光センサーを利用して読み取り、ラインに沿って走行すること。http://yakushi.shinshu-u.ac.jp/robotics/?2013a%2FMission1
実際のコースは以下の通り
まず、普通にプログラミングを書いて、普通に課題をこなそうと、とりあえず課題に取り組んでみたをしてみた。
結果として全然うまくいかなかった……。
1、まず、課題の、ライントレースをする上での問題。カーブなどで、四輪ではその場で回転することは不可能、どうしても内輪差、外輪差が発生する。曲がるときも外側に膨らむように動かさなければならない。
2、缶を運ぶために、なんらかの工夫が必要。
3、ロボットが重いせいで、プログラミング上で、何秒と命令しても動作が遅れるようなことが多い。さらに重いせいで動きが鈍い。
この三つが課題をする上で最も難しく、出来ない原因だ。
1の問題点を解決するために、ロボットは三輪にした。この三輪目は、横に自由に回転することが出来るように工夫をしているので、その場でも回転でき、内輪差、外輪差もほぼない。
上がライントレースをするために、地面の色を判定してくれるセンサーである。工夫したのは、プログラミングに合わせ、センサーを高めの位置に設置して、微妙な黒と白の値の変化で進み方を変えることが出来るようにした。
次に、缶を感知できるように、光センサーを設置した。缶は光を反射するので、光センサーで感知しやすく、缶が光にあたるとすぐにロボットがそれを感知することが出来る。
缶はこのアームで掴んで(はさんで?)進んだ後に反転、ロボットごと戻ってきて元に戻す。
この完成したかのように思われたロボットだが、これだけでは上手くいかずさらなる新しい問題点に苦しめられた……。
1、旋回するときに、スムーズに動くようになったのは良かったが、缶を掴んで旋回して元の位置に戻すときに、速く回転しすぎることにより、缶を投げ飛ばしてしまう。
2、設計図通りではなくなったので、satoTがロボットを持って帰った後、なぜかパーツが取れてどこから取れたものか判らなくなるので、ロボットの耐久性が日によって違う。
3、アームが長いため、缶を元の位置に戻せても、その後、缶にアームが触れずに進むために大きくバックしてから旋回しないとならない。その際のプログラミングで、バックのしすぎでカーブに差し掛かってしまうために、ライントレースを続行するための何度旋回してライントレースに戻るという命令の、何度の必要な数字が毎回微妙に変わってしまって安定しない。
まず、新しい問題点の3はアームを短くすることで解決した。
新しい問題点1の缶をどかす際に、缶を投げてしまうことがないように、缶をどかすアームを取り付ける位置を上に修正した。さらに、アームを棒からブロックに変えて、安定感を増した。
アームの長さが左右対称ではないのは、缶を投げ飛ばしてしまうことを防ぐため、遠心力で外に行ってしまう缶を最後までアームの中に収めておくためである。回転する方向とは逆のアームだけが長ければ良いので、この長さにした。
さらに言えば、この場合長いほうのアームしか実は必要がない。缶を持って旋回する方向は一つにプログラミングで固定したので逆回転することがないからだ。なぜ短いほうのアームが存在するかというと、缶がずれたときに、後のコースで缶の位置を調整することが出来るからである↓
この課題でのプログラミングで難しかったのは、交差点とL字路の区別がロボットには判断が難しいという点だ。ライントレース自体が難しいのもあるが、とにかく交差点がうまくいかなかった。交差点を超えることは出来ても次のL字路で、交差点だまっすぐ進もうとロボットが、まっすぐ進んでしまったりした。
ライントレース自体は光センサーが感知した数字から、黒ければ右斜め前に、白ければ左斜め前という風に進ませる、黒と白の中間ではまっすぐ進むということを、状況に合わせ右と左を変えたり、白い黒いを細かく設定し微調整することでスムーズに線の上を走行するように努力した。
問題である交差点は、黒ければ斜め前に進むのプログラムを実行してもまだ黒になった場合が続いた時に、それを交差点もしくはL字路と判断させ、交差点なら左旋回から、前に少し進み、交差点を超えた後に先ほどまでのライントレースを再開するという方法をとることでクリアした。また、そのときに交差点を数えて、その数によってL字路か交差点なのかを認識させた。
さらに缶をどかし、元に戻して進むというのも、チームを苦しめた。缶が安定せず、どかす際に倒れてしまうこともあり、沢山の缶を友達やサークル内で集めてもらって、どの缶が最適かを検証した。
缶をどかすのは、光センサーで缶に当たると、その値が白になることを利用し(使用した缶は光を反射するので、光センサーはそれを白と感知する)缶を感知させ、缶を感知すると、そのまま進んで缶をアームによって缶を持った状態で進み、その後タイマーを使って一定以上進んだ後に九十度旋回、前に進み、後ろに進むことで缶を元の位置に戻した。また、ここでの工夫は、ロボット作成に詳しく記してある。(課題にある缶をどかす際に、缶が倒れてしまうことが稀にあったので、缶をどかすアームを取り付ける位置を上に修正した)
さらに、プログラミング上で缶を元に戻した後、旋回の時にアームに缶が当たることを防ごうと、大きく後退する必要をなくすためにロボット自体のアームは短めにした。缶を戻す時に、その缶の位置を調節する(旋回時の遠心力より、缶の位置が変わる時にもとの場所に戻ることを利用し、その地点でアームから缶が離れるようになる長さにした)ことを容易にするという二点を踏まえ、二本のアームの長さを左右非対称に設計した)※このことが上での缶を投げ飛ばす原因につながってしまう
「#define ○○ 命令式」と書くことで後に○○と打つと、命令式をそこに書いたのと同じ意味になり、プログラミングを短くまとめることが出来る。
このプログラムでの定義付けは以下の通り。
#define kurot 42 //kurotと打つと42という数がそこに書き込まれたのと同じになる。 #define kurok 45 //kurokと打つと45という数がそこに書き込まれたのと同じになる。 #define kurog 48 //kurogと打つと48という数がそこに書き込まれたのと同じになる。 #define shiro 49 //shiroと打つと49という数がそこに書き込まれたのと同じになる。 #define hi 2 //hiと打つと2という数がそこに書き込まれたのと同じになる。 #define low 1 //lowと打つと1という数がそこに書き込まれたのと同じになる。 #define sh SetPower(OUT_AC, hi); //車輪にかける力を7段階のうち2に設定する #define sl SetPower(OUT_AC,low); //車輪にかける力を7段階のうち1に設定する #define slh SetPower(OUT_A,low); SetPower(OUT_C,hi); //左にとても緩やかに旋回するように動かす #define shl SetPower(OUT_A,hi); SetPower(OUT_C,low); //右にとても緩やかに旋回するように動かす #define mae sh OnFwd(OUT_AC); //比較的速く前進する #define ushiro OnRev(OUT_AC); //後進する #define migi0 sl; OnFwd(OUT_A); Off(OUT_C); //右に比較的緩やかに旋回する #define migi1 sl; OnFwd(OUT_A); OnRev(OUT_C); //右に旋回する #define migi2 slh;OnFwd(OUT_A); OnRev(OUT_C); //右に比較的急に旋回する #define hidari0 sl; Off(OUT_A); OnFwd(OUT_C); //左に比較的緩やかに旋回する #define hidari1 sl; OnRev(OUT_A); OnFwd(OUT_C); //左に旋回する #define hidari2 shl; OnRev(OUT_A); OnFwd(OUT_C); //左に比較的急に緩やかに旋回する #define STEP 1 //STEPと打つと1という数がそこに書き込まれたのと同じになる。 #define nMAX 7 //nMAXと打つと7という数がそこに書き込まれたのと同じになる。 #define yasumi Off(OUT_AC); Wait(100); //1秒停止する #define cross 40 //crossと打つと40という数がそこに書き込まれたのと同じになる。 #define cross_line sl; OnFwd(OUT_AC); Wait(cross); yasumi; //交差点を超えるために、前に少し進む。 int p=0; //pという関数を置く int x=1; //xという関数を置く int n=0; //nという関数を置く int y=0; //yという関数を置く
sub migime(){ //交差点を通るためのプログラム(右側ライントレース版) while(n < x){ //交差点をx回渡るまで while(p < nMAX){ //ライントレースのプログラムを繰返す、しかし、黒が連続して感知された場合に少し向きを元に戻し、直進して交差点を突破する if (SENSOR_2 < kurot){ migi1; //右に旋回する p++; //黒が連続で感知されたことを知るためにカウンタを増やす } else { if(SENSOR_2 < kurok){ migi0; } else if(SENSOR_2 < kurog){ mae; } else if(SENSOR_2 < shiro){ hidari0; } else { hidari1; } p=0;//黒が連続しなかったときに、カウンタをゼロに戻す } Wait(STEP);//光センサーで、ライン上か否かを調べる感覚をSTEPごと(0,01秒)にする } yasumi;//実際に交差点を突破する時のプログラム hidari1; Wait(nMAX*STEP*2); cross_line; n++;//交差点の数を数えることで、最後の方の直角を交差点と間違えて直進しないようにこのプログラムを終わらせるためのカウンタを増やす p=0;//ここで交差点かどうかを判断するカウンタをゼロに戻さないと、ずっと交差点だと思いついづける誤作動を起こすので、カウンタをゼロに戻す } }
sub hidarime(){//交差点を通るためのプログラム(左側ライントレース版)//なぜ両側存在するのかというと、カーブを曲がる時など外側を通った方が誤作動が少ないからである。 while(n < x){ while(p < nMAX){ if (SENSOR_2 < kurot){ hidari1; p++; } else { if(SENSOR_2 < kurok){ hidari0; } else if (SENSOR_2 < kurog){ mae; } else if (SENSOR_2 < shiro){ migi0; } else { migi1; } p=0; } Wait(STEP); } yasumi; migi1; Wait(nMAX*STEP*2); cross_line; n++; p=0; } }
sub migikata(){//直角カーブを通る時や、缶を運ぶ時に交差点を判断して直進することのないプログラム(道なりに進む)(右側ライントレース) while(FastTimer(0) <= y ) {//缶を運ぶ時に、何秒か前に進んでから後ろを向くためのタイマー
if (SENSOR_2 < kurot) {//通常のライントレース migi1; } else if (SENSOR_2 < kurok) { migi0; } else if (SENSOR_2 < kurog) { mae; } else if (SENSOR_2 < shiro) { hidari0; } else { hidari1; } Wait(STEP); } }
sub hidarikata(){//直角カーブを通る時や、缶を運ぶ時に交差点を判断して直進することのないプログラム(道なりに進む)(左側ライントレース) while(FastTimer(0) <= y ) { if (SENSOR_2 < kurot) { hidari1; } else if (SENSOR_2 < kurok) { hidari0; } else if (SENSOR_2 < kurog) { mae; } else if (SENSOR_2 < shiro) { migi0; } else { migi1; } Wait(STEP); } }
sub migite(){ //缶を感知するまで通常(交差点で直進しない)ライントレース(右側ライントレース) while(SENSOR_1 < 49) { if (SENSOR_2 < kurot) { migi1; } else if (SENSOR_2 < kurok) { migi0; } else if (SENSOR_2 < kurog) { mae; } else if (SENSOR_2 < shiro) { hidari0; } else { hidari1; } Wait(STEP); } }
sub hidarite(){//缶を感知するまで通常(交差点で直進しない)ライントレース(左側ライントレース) while(SENSOR_1 < 49) { if (SENSOR_2 < kurot) { hidari1; } else if (SENSOR_2 < kurok) { hidari0; } else if (SENSOR_2 < kurog) { mae; } else if (SENSOR_2 < shiro) { migi0; } else { migi1; } Wait(STEP); } }
task main() { SetSensor(SENSOR_1,SENSOR_LIGHT);//缶を感知する光センサー SetSensor(SENSOR_2,SENSOR_LIGHT);//ライントレースをするため、地面の色を感知する光センサー hidarite(); //缶を感知するまで左側をライントレース ClearTimer(0); //リセット y=400;hidarikata(); //普通に三秒間ライントレース sss;OnFwd(OUT_C);OnRev(OUT_A);Wait(80); //180度旋回 sss;OnFwd(OUT_C);OnRev(OUT_A);until(SENSOR_2 < 45);Off(OUT_AC); //180度旋回 ClearTimer(0); //リセット y=100;migikata(); //ちょっとライントレース sss;OnRev(OUT_AC);Wait(75); //缶を置いていく sss;OnFwd(OUT_A);Off(OUT_C);Wait(10); //180度旋回 sss;OnFwd(OUT_A);OnRev(OUT_C);until(SENSOR_2 < 45);Off(OUT_AC); //180度旋回 sss;OnRev(OUT_A);OnFwd(OUT_C);Wait(30); //車体の角度の調整(旋回しすぎてしまうのを調整) x=4;hidarime();//交差点を通るためのプログラム(左側ライントレース版) hidarite();//缶を感知するまで左側をライントレース ClearTimer(0); //リセット y=400;hidarikata(); //普通に三秒間ライントレース sss;OnFwd(OUT_C);OnRev(OUT_A);Wait(80); //180度旋回 sss;OnFwd(OUT_C);OnRev(OUT_A);until(SENSOR_2 < 45);Off(OUT_AC); //180度旋回 ClearTimer(0); //リセット y=100;migikata(); //ちょっとライントレース sss;OnRev(OUT_AC);Wait(100); //缶を置いていく sss;OnFwd(OUT_A);Off(OUT_C);Wait(10); //180度旋回 sss;OnFwd(OUT_A);OnRev(OUT_C);until(SENSOR_2 < 45);Off(OUT_AC); //180度旋回 sss;OnRev(OUT_A);OnFwd(OUT_C);Wait(30); //車体の角度の調整 hidarite();//缶を感知するまで左側をライントレース }
ほぼ毎日集まって、ロボットを作ったり、プログラミングを書いたりと、非常に多くの時間を費やした。完成した今では、いい思い出となったが、正直疲れた。
プログラミングがうまく出来ない時に、ロボットを作り直し、さらに、ロボットが変わればプログラミングも変えなければならないので、そっちも書きなおすというのが面倒で時間を食った。これは一重に計画性というか、完成のビジョンがしっかりしていなかったせいだと思う。だから、次はしっかりとした計画を立て、効率的にロボットを作ることに励みたい。