[[2011a]] #contents *メンバー紹介 [#s79573c3] ***くまちゃん [#e0c68ca0] ***hiros [#t0a10068] *コース[#o7b65f32] #ref(map.JPG) 交差点がナナメだったりして、対策に苦労した。 というのは、センサが同時に黒線に達するとは限らなかったため。 ロータリーの処理にも苦労した。 *ハードウェア [#f69f9e3c] **マシン解説 [#c5dc4b39] #ref(machine.JPG) これがマシン。どことなくアプ○ラス。この形にしたのは、以前の形が直角を曲がるのに不向きな形だった為。 特徴は、タイヤの間を縮める事で回転したときのずれを少なくした事。 回転の中心から、4cmほどしか離れていない。 #ref(front.JPG) センサをタイヤのそばに置いて、制御のずれを減らした。 色々のせられる広いボディ。センサなどもつけやすい。 #ref(bottom.JPG) タイヤが近いので小回りが利く。センサを2つ設置して、それで黒線を挟み、センサが黒を探知したときに条件分岐して処理することにした。2つにしたのは1つだとロータリーの判定に苦労すると考えたから。 *ソフトウェア [#x9bb2a61] **くまちゃん [#g371cfff] このように各ブロックで役割を分けて、プログラムを簡略化させた。そして、その各ブロックへの切り替えは幅を持たせたタイマーをセットした。 #ref(bearmap.jpg) 毎回分岐点でタイマーをセットして、簡単に分岐点での進路方向を決定することができた。 #define THRESHOLD 40 #define FIRST_TIME 1700 #define SECOND_TIME 1500 #define THIRD_TIME 1500 #define FOURTH_TIME 1500 #define FIFTH_TIME 1500 #define TURN_TIME 0 task main() { SetSensor(SENSOR_1,SENSOR_LIGHT); SetSensor(SENSOR_3,SENSOR_LIGHT); **第1ブロック [#ra7196e5] ***十字路の交差点で右折でも左折でもなく、直進させるためのプログラムをうった。 [#i9bb5773] ClearTimer(0); while (Timer(0) <= FIRST_TIME){ if ((SENSOR_1>THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_A+OUT_C); } else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); Off(OUT_A);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C);} else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C); PlaySound(SOUND_CLICK) ;} } **第2ブロック [#n7696375] ***三叉路では右折することなく直進するプログラムをうった。 [#r4dc8d7c] ClearTimer(1); while (FIRST_TIME < Timer(1) <= SECOND_TIME) { if ((SENSOR_1<THRESHOLD)&&(SENSOR_3<THRESHOLD)){ Off(OUT_A+OUT_C); OnFwd(OUT_C); Wait(30); OnRev(OUT_A); Wait(30);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C);} else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); Off(OUT_A);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C); PlaySound(SOUND_CLICK) ;} } **第3ブロック [#w1a18fcd] ***三叉路では直進ではなく右折するプログラムをうった。 [#aadbd4b4] ClearTimer(2); while ( SECOND_TIME < Timer(2) <= THIRD_TIME ) { if ((SENSOR_1<THRESHOLD)&&(SENSOR_3<THRESHOLD)){ Off(OUT_A+OUT_C); OnFwd(OUT_C); Wait(50);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C);} else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); Off(OUT_A);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C); PlaySound(SOUND_CLICK) ;} } **第4ブロック [#c5eea8e8] ***このブロックでは、右折はせずに三叉路があっても直進するプログラムをうった。 [#e8d2d571] ClearTimer(3); while ( THIRD_TIME < Timer(3) <= FOURTH_TIME ) { if ((SENSOR_1<THRESHOLD)&&(SENSOR_3<THRESHOLD)){ Off(OUT_A+OUT_C); OnFwd(OUT_A); Wait(30); OnRev(OUT_C); Wait(30);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C);} else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); Off(OUT_A);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C); PlaySound(SOUND_CLICK) ;} **第5ブロック [#w1d002d2] ***十字路があったら、右折でも左折でもなく直進するプログラムをうった。 [#k6472388] ClearTimer(0); while (FOURTH_TIME <= Timer(0) <= FIFTH_TIME){ if ((SENSOR_1>THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); } else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3>THRESHOLD)){ OnFwd(OUT_C); Off(OUT_A);} else if ((SENSOR_1>THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C);} else if ((SENSOR_1<THRESHOLD)&&(SENSOR_3<THRESHOLD)){ OnFwd(OUT_A); Off(OUT_C); PlaySound(SOUND_CLICK) ;} } } **hiros [#gfa35d61] 目標: できるだけ短く書きたい、とにかくわかりやすくしたい。そして出来ればどの紙でやっても動くようにしたい。 基本的な考えは下。 #ref(basicop.jpg) ***作戦(ロータリーなし)<- これをベースに作っていく。 [#wbec58d9] 2つの光センサを使って、黒線を挟み、適時修正しながら進めていく。 処理がわかりにくくなってしまいそうだったので、まずはロータリーについては考えない事にした。 交差点に到達、つまり、右のセンサも左のセンサも黒線を発見したら、まっすぐに進んでいくというプログラムを考えた。 ***コード [#ca6c9af8] //わかりやすいように色々定義 //黒線か白線かの判定に使う #define isBlack <40 #define isWhite >40 //センサに名前を #define R SENSOR_3 #define L SENSOR_1 //モータにも名前を #define RM OUT_C #define LM OUT_A //ラインの太さ(乗り越える線) #define LINE 8 //まっすぐ進む void go(){ OnFwd(RM+LM); } //右に少し回転 void tr(){ OnFwd(LM); //Off(RM); OnRev(RM); } //左に少し回転 void tl(){ OnFwd(RM); // Off(LM); OnRev(LM); } //一連の動き void move(){ //右も左も白の上なら、まっすぐ進む if (L isWhite && R isWhite) go(); //左が白で右が黒なら、右に道があると判断し、右に曲がる else if (L isWhite && R isBlack) tr(); //右が白で左が黒なら、左に道があると判断し、左に曲がる else if (L isBlack && R isWhite) tl(); //右も左も黒なら、交差点に到達したと判断し、線の太さ分進む else if (L isBlack && R isBlack){ PlaySound(SOUND_CLICK); go(); Wait(LINE); } } task main (){ SetSensor (R, SENSOR_LIGHT); SetSensor (L, SENSOR_LIGHT); //無限ループ while (1){ move(); } } ***作戦(ロータリーなし改) [#qc954620] 上のコードでは、直角な交差点ではまっすぐに進むが、私たちの交差点はかなり斜めで、「右も左も黒」、という判定になりにくい。よって、コードを新しく考える必要があった。このコードは寝ぼけていたのでアイディア自体がおかしいかもしれない。 まず、右か左のどちらが最初に黒(道)にあたったかを変数で覚えておいて、そのときに反対側(つまり回転の外側)が何回行われたかを格納しておき、その回数だけ反対側(内側)のモータを動かせば機体を道に対して平行に出来ると考えた。 ***コード [#pf8e7b07] #define isBlack <40 #define isWhite >40 #define R SENSOR_3 #define L SENSOR_1 #define RM OUT_C #define LM OUT_A #define LINE 8 void go(){ OnFwd(RM+LM); } void tr(){ OnFwd(LM); OnRev(RM); } void tl(){ OnFwd(RM); OnRev(LM); } int whichWay = 0; // if 1 then it means that it found the way on its right first. (else if 2 then left first.) int rcount = 0; //L is rcount late. int lcount = 0; //R is lcount late. void move(){ if (L isWhite && R isWhite) go(); else if (L isWhite && R isBlack){ tr(); whichWay = 1; rcount++; lcount = 0; } else if (L isBlack && R isWhite){ tl(); whichWay = 2; lcount++; rcount = 0; } else if (L isBlack && R isBlack){ if (whichWay == 1) PlaySound(SOUND_CLICK); if (whichWay == 2) PlaySound(SOUND_DOUBLE_BEEP); while (1){ go(); if (L isWhite && R isWhite){ if (whichWay == 1) { tl(); Wait (lcount); go(); Wait (LINE); / / if (L isWhite && R isWhite) break; }else if (whichWay == 2){ tr(); Wait (rcount); go(); Wait (LINE); / / if (L isWhite && R isWhite) break; } break; } } go(); Wait (LINE); Off(RM+LM); } } task main (){ SetSensor (R, SENSOR_LIGHT); SetSensor (L, SENSOR_LIGHT); while (1){ move(); } } ***作戦(ロータリーなし改改) [#w644155f] 上のコードは、交差点に入る前、機体が線に対して平行であればうまく行くが、現実的にはそれは難しいので、改良する必要があった。例えば、右が黒で、左は白であるとき、上のコードでは「右に道がある」ということになり、すぐ右に回転をしてしまう。これだと、斜めな交差点にさしかかると「右に回転ー>左に回転」の無限ループに陥って、その場から動かなくなってしまう。 なので、回転する前に判定を入れる事にした。 ***コード [#zdd515b3] ***作戦(ロータリーあり) [#kbebd074] ロータリーについて考えてみると、交差点との共通点である、「右も左も黒」という判定がある。 違いとしては、「前に道がない」ことなので、左右のセンサがどちらも黒を探知したときに、いったん前に進み、先に道があるかを探索して、もしなければロータリーなので戻り、右の道に進む。そのとき、「ロータリーである」と判定し、ロータリーの先にあるはずの、右へ曲がる道に到達したときにそれを一回とばす。 ちなみに、ここでハードウェアが大きく変わった。というのも、センサが機体の中心が離れすぎていて、センサが大きく回転してしまい、機体の位置とは離れた位置の情報を得てしまうため。 ***コード [#z0dc924d] #define isBlack <40 #define isWhite >40 #define R SENSOR_3 #define L SENSOR_1 #define RM OUT_A #define LM OUT_C #define LINE 15 void go(){ OnFwd(RM+LM); } void tr(){ Off(LM + RM); OnRev(RM); OnFwd(LM); } void tl(){ Off(LM + RM); OnRev(LM); OnFwd(RM); } void gogo(){ if (R isWhite && L isWhite){ //road ahead go(); } else if (R isBlack && L isWhite){ //road on right tr(); } else if (R isWhite && L isBlack){ //road on left tl(); } else if (R isBlack && L isBlack){ //rotary or intersection // Cross line, and look for road. If there was no road, then it must be the rotary. go(); Wait (LINE); // turn right a little, and if L isBlack then turn left to correct. // else turn right so that it will find the way, and this should be the rotary. tr(); Wait (5); if (L isBlack) { tl(); Wait (5); } else { // R (W -> B -> W) while (1){ if (R isBlack) break; tr(); } while (1){ if (R isWhite || L isBlack) break; tr(); } } } } task main (){ SetSensor (R, SENSOR_LIGHT); SetSensor (L, SENSOR_LIGHT); while (1){ gogo(); } } ***作戦(ロータリー改) [#eb1a9c52] 前回のコードは、何度も実行すれば何度か無事ゴールできた。ロータリーの処理をうっかり忘れていたので、まぐれだと考えた。今回は、自分の紙の上を走ればよしとした。よって、左右ともに黒の判定を返す回数をカウントして、機体がロータリーに到達したら左の道を通るようにした。そして、移動中に、ななめの線や、道ではないのに黒が中途半端に突き出して、左回転と右回転が永遠に続くループに陥り前進しなくなったときは、前に進むように、bbbbという変数でコントロールする事にした。つまり、回転( tr(), tl() )したときに bbbb に1を足し、それが10を越したときは、前進する事にした。前進したときは、 bbbb を0とした。 基本的な作戦 #ref(op.jpg) ロータリーの飛ばすべきところは、右が黒、左が白となる位置、つまり、右に道があると判定される位置と考えていたが、実際には左右ともに黒を返し、失敗となった。これは、右に道があると判定されるべき位置にたどり着いたとき、ロータリーが円形なので、ナナメに入ってきてしまい、結果どちらも黒を返してしまうのが問題だった。 ここに気をつける必要があった。 #ref(markedmap.jpg) 上のピンクが黒黒判定の場所。青のところでループに陥る。 ***コード [#ac6664c5] //言い換え #define isBlack <40 #define isWhite >40 #define R SENSOR_3 #define L SENSOR_1 #define RM OUT_A #define LM OUT_C //各秒数 線を越すための値と90度回転に必要な時間 #define LINE 27 #define TTIME 70 //とばすところ #define SKIP1 4 #define SKIP2 9 //直角に曲がるところ #define NEEDSTURN bb==3||bb==5||bb==6||bb==7||bb==8||bb==10 int bb = 0; //黒&黒のときの回数 int bbbb = 0; //動かなくなったかを判定 void go(){//前に進む OnFwd(RM+LM); } void tr(){//右に回転 Off(LM + RM); OnRev(RM); OnFwd(LM); } void tl(){//左に回転 Off(LM + RM); OnRev(LM); OnFwd(RM); } void gogo(){//一連の動き if (bbbb > 10) { //もし動かなかったら線は無視して前に進む go(); Wait (LINE); bbbb = 0; } if (R isWhite && L isWhite){ //road ahead go(); bbbb = 0; } else if (R isBlack && L isWhite){ //road on right tr(); bbbb++; } else if (R isWhite && L isBlack){ //road on left tl(); bbbb++; } else if (R isBlack && L isBlack){ //rotary or intersection bb++; PlaySound(SOUND_CLICK); Off(RM+LM); Wait (100); if (NEEDSTURN){ //rotary PlaySound(SOUND_UP); go(); Wait (LINE); tl(); Wait (TTIME); } else if (bb == SKIP1 || bb == SKIP2){ //skip ロータリー内のスキップ PlaySound(SOUND_LOW_BEEP); go(); Wait (LINE); tr(); Wait (40); } else { //skip 通常時の交差点 PlaySound(SOUND_DOWN); go(); Wait (LINE); } } } task main (){ SetSensor (R, SENSOR_LIGHT); SetSensor (L, SENSOR_LIGHT); while (1){// 無限ループ gogo(); } } *考察 [#y22b0902] **くまちゃん [#jd2fe435] ロータリーと急角度十字路の違いをプログラムするのが大変でした。 プログラムで乗り越えようと思ったのですが、できなかったのでコースを5ブロックに分けてタイマーをセットして乗り切りました。 反省点ですが、プログラムで難点を乗り切れずタイマーに頼り時間設定次第で成功か不可かが決まってしまったのが残念でした。 **hiros [#b3330aaa] 最後のロータリー改で一応クリアしたが、納得がいく結果にならなかった。これだと、線を変えたときいちいち調整する必要が出てきて、非常に面倒。満足が出来ない結果となった。あと、紙のでこぼこ具合がかなり影響してしまうのがつらかった。比較的短く書けたのはよかったかもしれない。 レポートを書くのに一番苦労した。どうやったらわかりやすいか、と考え、最初は図でわかりやすくしようと考えたが、後で読んでみると、自分にしかわからない書き方で、レポートというよりメモのようになってしまっていた。 - 可能な限り説明は画像ではなく通常のテキストで記述しましょう。 -- [[松本]] &new{2011-06-24 (金) 14:59:01}; - 説明のための図を作成するのには、DebianLiveの中に入っている xfigやinkscapeあたりを使うとよいでしょう。ペイント系のソフトよりも簡単にできると思います。 -- [[松本]] &new{2011-07-04 (月) 14:26:20}; #comment