[[2019a/Member]] 目次 #contents *課題3 [#d5962a7c] ピンポン玉または青と赤のボールを運搬して、所定の容器の中に入れる。 #ref(2019a/Member/tsukue/Mission3/field.jpg,50%,画像1) **フィールドの説明 [#sbe3faab] --フィールドは課題2で使用した紙を使用する。 --生協のお弁当の四角いプラ容器2つをそれぞれ円内に置き、片方に玉を2個入れる。 --残りの2個の玉は課題2と同じ位置に置く。その際、ゴムタイヤやプレートの上に置いてもよい。 --プラ容器には色をつけたり文字や記号を書いてもよい。 --プラ容器は両面テープ等でフィールドに固定してもよい。 --2枚の紙の境界にはそれぞれ幅1cm、合計2cmの黒線を描いてもよい。 **基本ルール [#tb9f413f] --競技時間は審判が続行不能と判断するまで、あるいはリタイアするまで。 --図のA地点または(および)A'地点からスタートする。ただし接地している部分はそれぞれの領域内に収まるものとする(線上はOK)。上空部分は領域からはみ出していてもよい。 --開始の合図から5秒以内にスタートボタンを押す作業を完了すること。 --競技が終了するまで、ロボットに触ったり人間が遠隔で操作してはならない。 --途中でうまく動かなくなった場合、1回限り再スタートすることができる(再スタートの際に別プログラムで起動してよい)。 --競技終了後、ロボットが、ゴールのプラ容器に触れていてはいけない。 --競技終了後、もともと玉が入っていたプラ容器が、ゴールのプラ容器に触れていてはいけない。 詳細は[[こちら:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMission3]]。 *ロボット [#pf42f547] とにかく安定性を重視したため、移動はすべてライントレースで行う。ボールを持つ機構はアーム式にした。偶然から生まれたNXTが浮遊しているようなデザインが個人的にお気に入りである。 #ref(2019a/Member/tsukue/Mission3/robot1.jpg,50%,画像1) ↑側面。NXTが対称に配置されており、見えているNXTがスレーブ、反対側がマスターである。 #ref(2019a/Member/tsukue/Mission3/robot2.jpg,50%,画像1) ↑斜め上。NXT本体やモーターのような大きなパーツをなるべく少ないパーツで組み合わせることで、頑丈になる。 #ref(2019a/Member/tsukue/Mission3/robot3.JPG,50%,画像1) ↑背面。配線をしまう空間をあけて、見た目にも配慮した。キャスターがさりげなくついている。(この画像のみアームの構造が旧型である。) #ref(2019a/Member/tsukue/Mission3/robot4.jpg,50%,画像1) ↑アームを取り払った前面。デフォルトのようにタイヤを回すモーターを寝かせると、重心の関係でアームの配置が難しくなった。そこで[[課題1:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMember%2Ftsukue%2FMission1#qa2f051c]]のようにモーターを立てることで、重いNXT本体をアームの反対側に低く配置することができ、重心の問題を解決した。 #ref(2019a/Member/tsukue/Mission3/robot5.jpg,50%,画像1) ↑底面。毎度のごとくタイヤにギアを組み込んでいる。今回は光センサーを2つ使用しており、センサー間は線の幅より少しだけ大きい。タイヤの軸とセンサーの距離は[[課題2:http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMember%2Ftsukue%2FMission2#y641683e]]と同様。 #ref(2019a/Member/tsukue/Mission3/robot6.jpg,50%,画像1) ↑真ん中に挟まれたモーターはアームの上げ下げを担う。ここもギアを使用。 #ref(2019a/Member/tsukue/Mission3/robot7.jpg,50%,画像1) ↑このようにアームが上下する。アームの一部になっているモーターは、玉をつかむ部分の開閉を担う。 #ref(2019a/Member/tsukue/Mission3/robot8.jpg,50%,画像1) ↑玉を2つ同時につかめる大きさである。画像の下の部分は、ボールを持ち上げたときに落とさないようにするストッパーである。ギアで回転方向を変えるのが難しかった。 *マスターのプログラム [#v09a4685] **定数、マクロ [#ebb204f8] #define KOSATEN 37 #define completed 15 #define turn_left OnRev(OUT_A,70);OnFwd(OUT_B,70); //左に回転する #define go_left OnRev(OUT_A,85);Off(OUT_B); //左に進む #define go_straight OnRev(OUT_AB,70); //まっすぐ進む #define go_right OnRev(OUT_B,85);Off(OUT_A); //右に進む #define turn_right OnRev(OUT_B,100);OnFwd(OUT_A,100); //右に回転する #define short_break Off(OUT_AB);Wait(1000); #define brake Off(OUT_AB) completed は通信に使う(後述)。 brake は「ブレーキ」を意味するが、break として捉えてほしい。 **サブルーチン [#a6f8fad4] ***ライントレース [#nba5f481] 光センサーを2つ取り付けており、課題2のプログラムを流用できないため、新たに作り直した。左右の値の差を求め、差が大きければ曲がり、0に近ければ直進する仕組みとなっている。以下の画像では、赤い丸が光センサー、数字が光センサーの値、青い四角がタイヤを示している。 -直線 #ref(2019a/Member/tsukue/Mission3/lt1.JPG,50%,画像1) ↑このとき、左右の値の差は0のため直進する。 #ref(2019a/Member/tsukue/Mission3/lt2.JPG,50%,画像1) ↑軌道からずれてこのようになると、差が大きくなり軌道を修正する。 -交差点 #ref(2019a/Member/tsukue/Mission3/lt3.JPG,50%,画像1) ↑このように両側で黒と認識すると交差点と判断する。 #ref(2019a/Member/tsukue/Mission3/lt4.JPG,50%,画像1) ↑このとき、まず右側が黒と認識するため、右に進む。 #ref(2019a/Member/tsukue/Mission3/lt5.JPG,50%,画像1) ↑やがて左側も黒と認識するため、交差点と判断する。 sub follow_line() //交差点までライントレース { while((SENSOR_1>KOSATEN)||(SENSOR_2>KOSATEN)){ int d=SENSOR_1-SENSOR_2; if (d<-6){ turn_right; }else if (d<-3){ go_right; }else if (d<=3){ go_straight; }else if (d<=6){ go_left; }else { turn_left; } } PlaySound(SOUND_CLICK); brake;Wait(200); } while の条件について、「センサー1が黒でない」または「センサー2が黒でない」となっているが、これはド・モルガンの法則によるものである。 sub follow_line2(int followtime) //一定時間までライントレース { long t0=CurrentTick(); while(CurrentTick()-t0<followtime){ int d=SENSOR_1-SENSOR_2; if (d<-6){ turn_right; }else if (d<-3){ go_right; }else if (d<=3){ go_straight; }else if (d<=6){ go_left; }else { turn_left; } } PlaySound(SOUND_CLICK); brake;Wait(200); } ***交差点での動作 [#z45684b3] ライントレースの性質上、T字路の行き止まりでない方向から進入すると分岐している方向に向いてしまうため、直進のプログラムは2通り作った。 sub chokusinL() //交差点を直進(左) { go_left;Wait(500); } sub chokusinR() //交差点を直進(右) { go_right;Wait(500); } sub sasetsu() //交差点を左折 { turn_left;Wait(500); go_left;Wait(500); } sub usetsu(int migi) //交差点を右折 { turn_right;Wait(500); go_right;Wait(migi); } sub turn_behind() //振り向く { turn_left;Wait(1000); while(SENSOR_1>KOSATEN){ turn_left; } //(線を認識するまで回転) Off(OUT_BC); } sub backleft() //後退し右を向く { OnFwd(OUT_A,100);Off(OUT_B);Wait(1500); turn_right;Wait(600); Off(OUT_AB); } sub backright() //後退し左を向く { OnFwd(OUT_B,100);Off(OUT_A);Wait(1400); turn_left;Wait(500); Off(OUT_AB); } ***通信 [#y0325372] アーム関連はスレーブで制御するため、メッセージで指示を出し、completed と完了の合図が来るまで待つような仕組みとなっている。 sub ball_catch() //玉をつかむ { int msg; SendRemoteNumber(1,MAILBOX1,12); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } sub ball_release() //玉を放す { int msg; SendRemoteNumber(1,MAILBOX1,11); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } sub arm_up() //アームを上げる { int msg; SendRemoteNumber(1,MAILBOX1,13); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } sub arm_down() //アームを下げる { int msg; SendRemoteNumber(1,MAILBOX1,14); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } sub up_catch() //アームを上げながら閉じる { int msg; SendRemoteNumber(1,MAILBOX1,16); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } sub down_release() //アームを下げながら開く { int msg; SendRemoteNumber(1,MAILBOX1,17); while(msg==completed){ ReceiveRemoteNumber(MAILBOX1,true,msg); } } **task main [#c6912e8f] あとはサブルーチンを組み合わせるだけだ。 task main() { SetSensorLight(S1); //右カラーセンサー SetSensorLight(S2); //左カラーセンサー arm_up(); short_break; ball_release(); go_straight; Wait(2000); //A→M follow_line(); //M→K chokusinR(); follow_line2(1500); short_break; //K→L arm_down(); short_break; ball_catch(); short_break; arm_up(); short_break; //玉をつかむ turn_behind(); follow_line(); //L→K chokusinL(); follow_line(); //K→M usetsu(500); follow_line(); sasetsu(); follow_line(); //M→B chokusinL(); follow_line(); //B→X usetsu(500); follow_line(); //X→X' sasetsu(); follow_line(); //X'→B' chokusinR(); follow_line(); //B'→M' sasetsu(); follow_line(); //M'→K' sasetsu(); follow_line2(1000); short_break; //K'→J' ball_release(); short_break; //玉を放す backleft(); //J'→K' follow_line2(3700); short_break; //K'→L' arm_down(); short_break; ball_catch(); short_break; arm_up(); short_break; //玉をつかむ turn_behind(); OnFwd(OUT_AB,30); Wait(100); follow_line(); //L'→K' usetsu(500); follow_line2(500); short_break; //K'→J' ball_release(); short_break; //玉を放す backright(); follow_line(); //J'→K' chokusinL(); follow_line(); //K'→M' usetsu(500); follow_line(); //M'→B' chokusinL(); follow_line(); //B'→X' usetsu(500); follow_line(); //X'→X sasetsu(); follow_line(); //X→B sasetsu(); follow_line(); //B→C usetsu(400); follow_line2(1650); short_break; //C→I arm_down(); short_break; ball_catch(); short_break; up_catch(); short_break; //玉をつかむ backleft(); //I→C follow_line(); //C→B usetsu(500); follow_line(); //B→X usetsu(500); follow_line(); //X→X' sasetsu(); follow_line(); //X'→B' sasetsu(); follow_line(); //B'→C' usetsu(700); follow_line2(900); short_break; //C'→I' down_release(); short_break; //玉を放す arm_up(); short_break; backright(); //I'→C' } *スレーブのプログラム [#h92f623c] **定数 [#k3e7c833] すべてマスターと対応している。 #define ball_release 11 #define ball_catch 12 #define arm_up 13 #define arm_down 14 #define completed 15 #define up_catch 16 #define down_release 17 **task main [#k7227ad9] マスターの指示に従って動作し、完了したら completed とメッセージを送る。 task main() { int msg; while(true){ ReceiveRemoteNumber(MAILBOX1,true,msg); if(msg==ball_release){ OnFwd(OUT_A,50);Wait(500);Off(OUT_A);Wait(500); SendResponseNumber(MAILBOX1,completed); } //玉をつかむ if(msg==ball_catch){ OnRev(OUT_A,50);Wait(600);Off(OUT_A);Wait(500); SendResponseNumber(MAILBOX1,completed); } //玉を放す if(msg==arm_up){ OnFwd(OUT_B,50);Wait(600); OnFwd(OUT_B,5);Wait(500); SendResponseNumber(MAILBOX1,completed); } //アームを上げる if(msg==arm_down){ OnRev(OUT_B,30);Wait(600); OnFwd(OUT_B,5);Wait(500); SendResponseNumber(MAILBOX1,completed); } //アームを下げる if(msg==up_catch){ OnFwd(OUT_B,40);OnRev(OUT_A,15);Wait(300); SendResponseNumber(MAILBOX1,completed); } //アームを上げながら閉じる if(msg==down_release){ OnRev(OUT_B,10);OnFwd(OUT_A,20);Wait(150); SendResponseNumber(MAILBOX1,completed); } //アームを下げながら開く Wait(100); msg=0; } } アームが重力で落ちてくるのを防ぐため、アームの上げ下げのプログラムは Off(OUT_B)から OnFwd(OUT_B,5) に置き換えている。 *まとめ [#d918eade]