課題3:ボールを離れた場所から缶に当てるロボットとボールを回収するロボット
ロボットAがボールをつかみ、ボールを缶に向けて発射する。缶に当たって跳ね返ったボールをロボットBがつかみ、ゴールまで運ぶ。(要約)
今回は二台のロボットを使って課題を遂行します。
私は主にボールを受け取る側のロボットB(右)を製作していました。
ロボットAは主にS_Toya君とsun16君が製作していました。 横から見ていただけですが、ロボット本体の大部分は割と早めに出来ていたようです。(前日で一から作ったロボットBとは違いますね)
ロボットAはボールをキャッチして缶に向かってボールを放つ必要がありますが、アームを回転させるだけで掴むのと放つ動作が出来るようにしていました。
ライントレースしながらボールの位置を距離で判別させるため、回転センサと赤外線センサをつけています。
RISのロボットを使うチームはアルミ缶の上にランタンを置くので、缶の方向を推定するためにロボット上部にもう一つ赤外線センサをつけています。
私はこのプログラムに全く触れていませんが、独自の解釈で解説します。 このプログラムはS_Toya君のレポートが一番詳しいと思うのでそちらを参考にしてください。
ロボットAはボールをキャッチして放つ動作があります。ボールは指定の場所で、放つ場所も私たちは命中率を上げるために場所をある程度近い場所で指定しています。 trace_0〜3がそれぞれのボールまでのトレースと放つ場所までのトレースで、mainで一つずつにサブルーチンを実行していく形です。 コメント文は一応書き直しました。
#define power(s) SetPower(OUT_A+OUT_C,s); #define shiki 48 #define tomaru(t) Off(OUT_AC);Wait(t); #define FWD OnFwd(OUT_AC); #define BACK OnRev(OUT_AC); #define LEFT_t OnRev(OUT_A); OnFwd(OUT_C); #define LEFT Off(OUT_A); OnFwd(OUT_C); #define RIGHT_t OnRev(OUT_C); OnFwd(OUT_A); #define RIGHT Off(OUT_C); OnFwd(OUT_A); #define TMAX 20 #define open(t) OnFwd(OUT_B);Wait(t); #define close(t) OnRev(OUT_B);Wait(t);Off(OUT_B); #define stop_B Off(OUT_B); #define SUN 50//ランタンの輝度 #define shoot power(2);open(15);Wait(10);close(15);
通常のライントレースに加え、左車輪に付けた回転センサを使って距離を測定してボールの位置を把握しています。
sub trace_0() { power(2); ClearSensor(SENSOR_1); while(1){ if (SENSOR_2 < shiki -7){ LEFT_t; } else if (SENSOR_2 < shiki -4){ LEFT; } else if (SENSOR_2 < shiki){ FWD; } else if(SENSOR_2 < shiki +4){ RIGHT; } else { RIGHT_t; } if(SENSOR_1 > 335){ power(3); close(50); ClearSensor(SENSOR_1); break; } } }
赤玉の設置している場所からボールを缶に当てるのは難しいので、近くまでライントレースします。 trace_0と同様に、ボールを発射する位置も回転センサで距離を測定して決めています。
sub trace_1() { ClearSensor(SENSOR_1); close(10); Off(OUT_B); power(2); while(1){ if (SENSOR_2 < shiki -7){ LEFT_t; } else if (SENSOR_2 < shiki -4){ LEFT; } else if (SENSOR_2 < shiki){ FWD; } else if(SENSOR_2 < shiki +4){ RIGHT; } else { RIGHT_t; } if(SENSOR_1 > 62){ tomaru(10); break; } } }
trace_0,trace_1まではラインの左側をトレースしていましたが、ここからは右側をトレースします。 これは、ボール手前のカーブは直進部分が少ないため右側の緩いカーブを沿ってトレースすることによって誤差を少なくするための配慮です。
sub trace_2() { power(1); ClearSensor(SENSOR_1); while(1){ if (SENSOR_2 < shiki -7){ RIGHT_t; } else if (SENSOR_2 < shiki -4){ RIGHT; } else if (SENSOR_2 < shiki){ FWD; } else if(SENSOR_2 < shiki +4){ LEFT; } else { LEFT_t; } if(SENSOR_1 > 44){ power(3); close(50); ClearSensor(SENSOR_1); break; } } }
trace_2と同様に右側をトレースします。
sub trace_3() { power(2); ClearSensor(SENSOR_1); while(1){ close(1); Off(OUT_B); if (SENSOR_2 < shiki -7){ RIGHT_t; } else if (SENSOR_2 < shiki -4){ RIGHT; } else if (SENSOR_2 < shiki){ FWD; } else if(SENSOR_2 < shiki +4){ LEFT; } else { LEFT_t; } if(SENSOR_1 > 110){ power(3); close(50); ClearSensor(SENSOR_1); break; } } }
約一周回って一番反応がいい方向に止まります。
sub LIGHT() { PlaySound(SOUND_UP); int sun_MAX = 0; int sensor_MAX = 0; close(20); ClearSensor(SENSOR_1); power(1); while(SENSOR_1 < 85){ RIGHT_t; if(SENSOR_3 > sun_MAX){ sun_MAX = SENSOR_3; sensor_MAX = SENSOR_1; } } tomaru(10); LEFT_t; until(SENSOR_1 < sensor_MAX); tomaru(10); }
task main() { // 回転センサ、トレース用センサ、光源検出用センサです。 SetSensor(SENSOR_1,SENSOR_ROTATION); SetSensor(SENSOR_2,SENSOR_LIGHT); SetSensor(SENSOR_3,SENSOR_LIGHT); // 赤玉まで進む FWD;Wait(150); trace_0(); tomaru(50); // 赤玉を持って発射地点へ trace_1(); LIGHT(); shoot; tomaru(20); // 青玉の方向へ回転 ClearSensor(SENSOR_1); RIGHT_t; until(SENSOR_1 > 28); tomaru(10); // ラインにぶつかるまで直進 ClearSensor(SENSOR_1); FWD; until(SENSOR_1 > 26); Off(OUT_B); FWD; until(SENSOR_2 < 48); open(7); Off(OUT_B); // 青玉に向かって進む trace_2(); tomaru(10); // 発射地点まで移動 FWD;Wait(100); trace_3(); tomaru(10); LIGHT(); shoot; trace_1(); }
今回は角度センサーを使って概ねの距離を測っていました。 繰り返すたびに光源を発見するためのズレが生じてしさまうので、誤差に苦労していたようです。 最終的には、ラインを上手く使ってその問題が解消出来ていたようです。
ロボットBは主に私とcst321君で製作しました。 ロボットBは缶に跳ね返ったボールをキャッチしてゴールまで運ぶ必要があります。 当初、ボールを探す方法をかなり試行錯誤しましたが、まともに見つけられる方法が分かりませんでした。最終的に、ライン上をトレースしながらボールを回収するだけでも上手くいくことが分かったので、写真のようにライントレースをしながら近くのボールを回収し保持するようにベルトを回すロボットにしました。
ロボットBは見て分かるようにボールを書き込む動作を行います。 ロボットを運ぶ際にボールは想像以上に軽いため、単純な押す動作だけではロボットから離れて行ってしまいます。 今回はボールを検知せずにボールをゴールまで運ぶという方針で決めたので、これが最善の形だと判断しました。 ちなみに、ベルトが上部についているのは横からボールが入ってもロボットに当たって跳ね返らないようにするためです。
缶の部分は本体が円に対して外を向いてしまうため、どうしても缶にぴったりついてしまうボールが取れない問題がありました。 そこで、一旦入ったボールが外に出ないかつ少しでも広くボールが拾えるように、左右に外側へ沿った透明の棒を付けています。
この部品は細かい部品ではありますが。これがあるかないかで、ボールが一つ取れるか取れないかが決まる程重要なパーツでした。
プログラムはほとんど課題2の私のプログラムを流用しています。
赤線が一週目のコースで青線が二週目のコースです。 タイミング調節ポイントで指定した時間動作を止めて缶に跳ね返ったボールが止まるまで待ちます。
/* 37 black 53 white */ int startime=300; #define CHECKCROSSTIME 25 #define fwd(OUT) Off(OUT);OnFwd(OUT) #define rev(OUT) Off(OUT);OnRev(OUT) #define TURNLEFT fwd(OUT_AC) #define LEFT Off(OUT_C);fwd(OUT_A) #define TURNRIGHT rev(OUT_AC) #define RIGHT Off(OUT_A);rev(OUT_C) #define STRAIGHT rev(OUT_C);fwd(OUT_A) #define BACK fwd(OUT_C);rev(OUT_A) #define ISNOTCROSS FastTimer(0) <= CHECKCROSSTIME || FastTimer(1) <= startime #define RELEASE rev(OUT_B); #define LOADING fwd(OUT_B);
ラインの左側に沿ってトレースをします。 交差点らしきものを見つけた場合、このサブルーチンを終了します。
sub trace(){ ClearTimer(0); while(ISNOTCROSS){ if(FastTimer(1) < startime){ ClearTimer(0); } if(52 <= SENSOR_1){ TURNRIGHT; }else if(50 <= SENSOR_1){ RIGHT; }else if(41 <= SENSOR_1){ STRAIGHT; }else if(39 <= SENSOR_1){ LEFT; }else { TURNLEFT; } if(39 <= SENSOR_1) ClearTimer(0); } Off(OUT_AC); ClearTimer(0); Wait(100); }
交差点において直進する動作をするサブルーチンです。
sub go_straight(){ STRAIGHT; Wait(30); ClearTimer(1); }
交差点において左折する動作をするサブルーチンです。
sub turn_left(){ ClearTimer(1); ClearTimer(0); }
交差点において右折するサブルーチンです。 今回は使用していませんが、右折する可能性もあったので念のため
sub turn_right(){ ClearTimer(1); while(ISNOTCROSS){ if(FastTimer(1) >= startime){ ClearTimer(0); ClearTimer(1); break; } if(53 <= SENSOR_1){ TURNLEFT; }else if(50 <= SENSOR_1){ LEFT; }else if(47 <= SENSOR_1){ STRAIGHT; }else if(44 <= SENSOR_1){ RIGHT; }else { TURNRIGHT; } } TURNLEFT; Wait(10); Off(OUT_AC); ClearTimer(0); ClearTimer(1); }
メインのプログラムです。 前回同様startimeという交差点判定を無効化する時間を使っています。 電池残量によってカーブを交差点と誤判定することがあるので、startimeを交差点ごとに変更したりしてカーブの誤判定を防ぎました。
task main(){ SetSensor(SENSOR_1,SENSOR_LIGHT); ClearTimer(0); LOADING; go_straight(); trace(); // 23秒間待ち続ける->直進 go_straight(); Off(OUT_AC); Wait(2300); ClearTimer(1); startime=900; trace(); startime=300; // 直進 缶の周りをまわる go_straight(); trace(); // 直進 缶の円を抜けてゴールへ go_straight(); trace(); // 直進 go_straight(); startime=1400; trace(); startime=300; // ゴールを見つけたら方向を調節してボールを出す TURNRIGHT; Wait(10); Off(OUT_AC); RELEASE; Wait(300); // バックして半回転 BACK; Wait(100); TURNRIGHT; Wait(40); // 二週目 LOADING; ClearTimer(0); trace(); // 左折 同じコースを二回目 turn_left(); startime=900; trace(); startime=300; // 直進 缶の周りをまわる go_straight(); trace(); // 直進 缶の円を抜けてゴールへ go_straight(); trace(); // 直進 go_straight(); startime=1400; trace(); startime=300; // ゴールを見つけたら方向を調節してボールを出す TURNRIGHT; Wait(10); Off(OUT_AC); RELEASE; Wait(300); // ロボットも一緒にゴール! go_straight(); Wait(10); Off(OUT_ABC); }
体調管理はしっかりしましょう。振り返ってみて今回はあんまり反省すべき点も少なかったのかなと思います。今まで一人が持って帰ってやるというのが多かったですが、最後は四人で夜中までかけてやっていい結果が出せたのでこれ以上に望むことはありません。あえて言えば毎回レポートを出す時間ワースト1位を争っていることでしょうか。