[[2018a/Member]] [[2018a/Member]] #contents *課題2:ライントレースをして缶を移動させるロボットの作成 [#nbdb1255] 指定のコースを指定の順にたどり缶を移動させる。 *選んだコース [#l43ebba3] コース2。詳細はhttp://yakushi.shinshu-u.ac.jp/robotics/?2018a%2FMission2 *車体構造 [#c0fb87f9] 画像のような形にした。それぞれ、Aが左車輪、Cが右車輪、Bがつかむ機構、2が光センサとなっている。 &ref(2018a/Member/kan777/Mission2/1.jpg,40%,車体); **車輪 [#a6a3e9dd] 2輪駆動にし、前輪をなくした。代わりに下の画像のようにして滑らせるようにした。 &ref(2018a/Member/kan777/Mission2/5.jpg,40%,前輪の構造); **センサ [#k2f13bbf] センサは前輪(滑らせるところ)の直前につけ3mm程度浮かせた。 **アームの機構 [#z403b48e] アームの機構はクワガタのようにしてつかみ取る形にした。2つのアームが連結された真ん中部分を引っ張ることでつかむことのことのできる様になっている。 &ref(2018a/Member/kan777/Mission2/2.jpg,20%,構造1);&ref(2018a/Member/kan777/Mission2/4.jpg,20%,構造2); **車体考察 [#x6a84fb4] -2輪駆動にしたことでセンサをうまく機動性は上がったが、タイヤのゴムが伸びて劣化していたせいかホイールとタイヤのゴムの間で滑ってしまうことがあり、誤差が生まれてしまうことがあった。 [#b3baa0fb] -前回の反省点を生かしギアをうまく取りまわすことができた。 -つかみ取る機構がつかむ力もモータに依存してしまうため力が緩むと開いてしまった。モータを回しっぱなしにすれば解決はできそうだったが、モータに負荷がかかってしまうことを懸念されできなかった。長時間つかみっぱなしにするとき、risにはこのアームの機構は良くなかった。また、このアームの機構だとうまく動かないとアームが缶にあたってしまうという欠点もあった。 -その他車体の構造においては前回の反省点を生かすことができたと思う。 *プログラム [#r580b3bf] #define mid 45 //センサーが白と黒の境目の上 境界の上値 #define OVER 4 //カーブか直進以外だと断定するまでの探査時間 #define STOP Off(OUT_AC);PlaySound(SOUND_FAST_UP);Wait(100); #define OPEN OnRev(OUT_B);Wait(7);Off(OUT_B); #define CLOSE OnFwd(OUT_B); Wait(7); Off(OUT_B); #define turnL OnRev(OUT_A);OnFwd(OUT_C);Wait(100);OnFwd(OUT_AC);while(SENSOR_2>mid){} #define turnR OnFwd(OUT_A);while(SENSOR_2>mid){}; int S; //何回目の読みすぎなのか数える関数 int LR=1; //1で右側をトレースそれ以外で左側をトレース(今回は左側はー1を使用) task main(){ //コース2 int a=0; SetSensor(SENSOR_1,SENSOR_TOUCH); SetSensorMode(SENSOR_1,SENSOR_MODE_RAW); SetSensor(SENSOR_2,SENSOR_LIGHT); SetPower(OUT_B,7); //アーム用のモータの力を上げた SetPower(OUT_A,2); //タイヤは細かい動作でぶれてしまうので出力を多少下げた SetPower(OUT_C,2); while(a<17){ trace(LR); CLOSE Wait(10); PlaySound(SOUND_DOUBLE_BEEP); //曲がりすぎるたびに音を鳴らす switch (a){ //曲がり過ぎたら交差点かT字路かなどを回数で判断しそれに合う操作を実行。 case 0: //Bを右折 way(90); a++; LR=-1; //左読みに切り替え OPEN break; case 1: //Jで止まってYを回収してJに戻る STOP cat(); a++; break; case 2: //Jを左折 way(90); OnRev(OUT_AC); //進みすぎてしまうので下がる Wait(40); Off(OUT_AC); a++; break; case 3: //Kを直進 way(1); a++; break; case 4: //Lを直進 way(1); a++; break; case 5: //Iを左折 OnFwd(OUT_AC); //調整 Wait(30); Off(OUT_AC); way(90); OnFwd(OUT_C); //調整 Wait(10); Off(OUT_AC); a++; break; case 6: //Hで一時停止 左折 STOP way(90); OnFwd(OUT_C); //調整 Wait(10); Off(OUT_AC); a++; break; case 7: //小回り 左 turnL a++; LR=1; //右読みに切り替え break; case 8: //小回り 右 turnR a++; LR=-1; break; case 9: //G前を左折 way(90); a++; LR=1; break; case 10: //Gを一時停止して右折 STOP way(90); a++; break; case 11: //Fで一時停止して右折 STOP way(90); a++; LR=-1; break; case 12: //Eを直進 way(1); a++; break; case 13: //Dで一時停止してXにおいて(おそらくアームが引っかかるので)そのまま直角までバックしてr=5の後まで STOP put(); a++; LR=1; break; case 14: //Dのあとの直角を右に way(90); OnRev(OUT_AC); //進みすぎてしまうので下がる Wait(40); Off(OUT_AC); a++; break; case 15: //r=5を曲がる turnL a++; break; case 16: //Cを一時停止後直進 STOP way(1); a++; break; case 17: //Aの敷居を読んだら中にはいって終了 OnFwd(OUT_AC); Wait(100); } } Off(OUT_AC); } void trace(int LR){ ClearTimer(0); while(Timer(0)<OVER){ //黒がずっと続いたらトレースをやめる S=SENSOR_2; if(S>mid+3){ //黒なら if(LR==1){ Off(OUT_A);OnFwd(OUT_C); //左 }else{ OnFwd(OUT_A);Off(OUT_C); //右 } ClearTimer(0); }else{ //白なら if(LR==1){ OnFwd(OUT_A);Off(OUT_C); //右 }else{ Off(OUT_A);OnFwd(OUT_C); //左 } } } } void way(int drct){ //drct(direction)が90ならラインをトレースしてる側へ曲がる 1なら直進。 drct=LR*drct; //直進か曲がるかの値に今どっちをトレースしてるかの情報を付加 OnFwd(OUT_AC); while(SENSOR_2<mid-3){} //白まで前進 Wait(30); //少し多めに Off(OUT_AC); Wait(100); if(drct==90||drct==(-1)){ //右側トレースで90なら右へ行ってライン復帰すれば90度曲がれる 左側トレースで1なら右に行ってラインに復帰すると直進する。 OnFwd(OUT_A); OnRev(OUT_C); } if(drct==(-90)||drct==1){ //左側トレースで90なら左へ行ってライン復帰すれば90度曲がれる 右側トレースで1なら左に行ってラインに復帰すると直進する。 OnFwd(OUT_C); OnRev(OUT_A); } while(SENSOR_2>mid){} //ライン復帰するまで曲がり続ける。 Off(OUT_AC); } void cat(){ //defineしようと思ったが長くなりすぎたので仕方なく。 OnRev(OUT_C); Wait(40); OPEN OnFwd(OUT_AC); Wait(50); CLOSE OnRev(OUT_AC); while(SENSOR_2>mid){} //黒まで下がって Wait(95); //少し多めに下がって } void put(){ //defineしようと思ったが長くなりすぎたので仕方なく。 OnRev(OUT_A); while(SENSOR_2<mid-5){} //白まで Wait(30); Off(OUT_AC); OPEN OnRev(OUT_AC); while(SENSOR_2<mid-3){} //黒まで下がって Off(OUT_AC); //Dまで戻った OnFwd(OUT_A); OnRev(OUT_C); Wait(150); OnFwd(OUT_C); while(SENSOR_2<mid-3){} Off(OUT_AC); } **プログラム説明 [#cd9df6dd] +基本的にどちらかラインの左端もしくは右端をトーレスをし続け左右どちらかに曲がりすぎたらカーブもしくは直線だと判断しトレースをいったんやめる。 +トレースがいったん止まるとswitch case文に入りこれが何回目の交差点や直角曲がりなのかを判断し、それに合った動作を実行する。(それぞれのcase文がどこの時点での動作かはプログラム内のコメントの通り。) +1のトレースに戻る。 <をcaseがなくなるまで繰り返す構造になっている。 (曲がりすぎの検出は時間で行った) -ところどころに「調整」とコメントが入っているのは、カーブなどの時に前に進みすぎてしまったり、コースの狭さの関係上うまく進めなかったところがあったので微調整した。 ***Defineの説明。 [#m3f8cb86] -mid 白と黒との境界の上の時の値 -OVER トレースしているラインがカーブか直進以外だと断定するまでの探査時間 -STOP 交差点だった時に一時停止してSOUND_FAST_UPの音を鳴らす -OPEN アームを開く -CLOSE アームを閉じる -turnL 半径の小さいRを曲がるときに使う。カーブの内側をトレースし曲がり切れずに止まったら左に旋回し黒い線を踏むまで前進する。 -turnR 上の右に旋回するバージョン。 ***サブルーチンの説明。 [#u933b2ca] -trace 渡された値が1なら右側をそれ以外なら左側(今回は左側の時は-1を入れた)をトレースするようになっている。 -way 渡された値が90ならトレースしている方向に曲がり、1なら直進していくプログラム。 現在トレースしている側が右か左かは変数LRの値でわかるのでその値を掛け合わせて情報をふかしている。 --wayのトレースの仕組みと流れを画像で示した。この仕組みをまとめるとこのサブルーチンのようになる。 &ref(2018a/Member/kan777/Mission2/trace.png,90%,way説明); -cat,put 缶をキャッチして元の位置に戻るプログラムと缶を置いてトレースのラインに復帰するプログラム。 **プログラムについて考察 [#b8ccd152] -前回は吟味する時間がなく無駄なサブルーチンが多かった。しかし、今回は極力サブルーチンを減らし中身も簡潔なものにできた。 -コースの構造上のことを考慮しきれず微調整が増えてしまったのが残念だった。 -アームの力が足りず缶を転ばせてしまうこと、話してしまうことが3回に1回ぐらい起きてしまう。上でも挙げたように閉め続ければ治せるがモータの負荷を懸念し断念した。 -タイヤが滑ったりモータの力が時と場合で違ったりするので「黒になるまで」や「白になるまで」を用いてなるべくセンサーを用いるようにした。 -どうしても時間で制御するところがあるためにうまく曲がらなかったりカーブを検出できないことが多々あった。 *全体を通して [#fd8763b4] -車体構造、プログラムともに前回の反省点を生かすことができたと思う。 -時間に頼った制御はタイヤが滑ったり、モータの調子が変わったりしてしまうのでかなり難しいことが分かった。また、そのせいでうまくいかないことが多々あり、何回か走らせれば成功するような感じになった。 -プログラムは時間をかけて吟味することができた。 -タイヤがずれたりモータの調子が変わってしまうことの完璧な解決策は見つけることができなかった。 -以下追加 -switch case文にすることで途中からの動作のチェックをしやすくテストしやすくなる。 -risは前後の動作で大きく動きが変わってしまうのでこのような形にした。 ***以下追加 [#u59263f2] -switch case文にすることで変数aの値を変えるだけで途中からの動作ができるためチェックやテストをしやすくなる。risは前後の動作で大きく動きが変わってしまうのでこのような形にした。 ほかの作りでももちろんできるがどこがステップいくつなのかわからなくなってしまうので見やすさのためにもswitch caseを用いた。何がどこの動作なのかわかりやすくなっていると思う。