目次 #contents *課題3:ボール運搬ロボット[#l3956274] 青と赤のボールを運搬して、それぞれ所定の350ml缶の上に乗せる。 **フィールドの説明 [#n9b54b4c] フィールドは課題2で使用した紙を使用する。 350ml缶(中が入っていても空でよい)は逆さまにして使い、ロボットのスタート直後にサイコロを振って出た目の番号の位置に置く。 その番号の次の番号のところにもダミーの缶を置くことができる。ただし6の目が出た場合は6の位置と25cm以上離れていない場所ならどこに置いてもよいものとする。距離は缶と缶のもっとも近い部分で測る。 空き缶には色をつけたり文字や記号を書いてもよい。あるいは周囲に紙を張ってもよい。 赤と青のボールは、図のように所定の場所に置いておく。その際、キットに含まれない小さな輪ゴムを使用するものとする。 **ルール [#u58a7a3d] 基本ルール 競技時間は審判が続行不能と判断するまで、あるいはリタイアするまで。 図のX地点または(および)Y地点からスタートする。ただし接地している部分はそれぞれの領域内に収まるものとする(線上はOK)。上空部分は領域からはみ出していてもよい。 赤いボールを図のピンクのいずれかに置いた缶に、青いボールを図の水色のいずれかに置いた缶に、それぞれ乗せる。 開始の合図から5秒以内にスタートボタンを押す作業を完了すること。 競技が終了するまで、ロボットに触ったり人間が遠隔で操作してはならない。 途中でうまく動かなくなった場合、1回限り再スタートすることができる(再スタートの際に別プログラムで起動してよい)。 **基本得点の計算方法 [#pa88fab6] ボールを一つ乗せればそれぞれ10点、二つとも乗せればボーナス点としてさらに10点。 ダミーの缶を設置した上で、正しい缶に乗せれば、それぞれさらに6点加点する。 ボールを目的の缶に当てることができれば、それぞれ4点。 ボールを同じ領域内の間違った缶に乗せた場合は、それぞれ6点。 ボールを同じ領域内の間違った缶に当てた場合は、それぞれ2点。 ボールを違う領域内の缶に乗せた場合は、それぞれ2点。 ボールを違う領域内の缶に当てた場合は、0点。 目的の缶をもとの位置(直径7cmの円)から少し出してしまった場合は1点減点、半分以上出してしまった場合は2点または取得した得点の半分のいずれか少ないほうを減点、その缶を完全にだしてしまったときは点数を半分にする。 ダミーの缶がもとの位置から移動しても減点はしない。 技術点の計算方法 以下の動作の精度・スピード・確実性などを含めた技術的な工夫や芸術性について他の全てのチーム(5チーム)が20点満点で採点し、その平均点を求める。 得点の目安: ボール探し取りにいくまでの動作 (3点) ボール掴む動作 (3点) ボールを運ぶ動作 (2点) ボールを缶に置く動作 (2点) 2台のNXT、EV3の連携の良さ(2点) 自立型のロボットとしての形や動作の美しさ、斬新さ(2点) その他 (3点) *今回のロボットについて [#e2ba4992] *ロボットを動かすためのプログラム [#t28312fb] **ライントレース [#xad04020] 今回も課題2の時と同様に「黒と白の境界線をライントレースする」というプログラムを制作した。ただ、今回のライントレースでは、光センサを2つ使っているため、交差点で停止するためのライントレースと交差点を左折するためのライントレースという2種類のライントレースを行うことにした。 **缶を認識し、缶に近づくためのプログラム(赤いボールをつかんだ時) [#qb69c2fe] ***缶を認識するためのプログラム [#ya1ed339] #define SPEED 50 #define SPEED_SLOW 30 #define SPEED_MSLOW -30 const float diameter = 5.45; //タイヤの直径(cm) const float track = 10.35; //タイヤのトレッド幅(cm) const float pi = 3.1415; //円周率 void turnAng(long ang) //角度 ang 度の時計回りの旋回 { long angle; angle = track/diameter*ang; RotateMotorEx(OUT_AC, SPEED_SLOW, angle, 100, true, true); } int searchDirection(long ang)// 現在の方向を中心にang度の範囲で探す。 // 障害物までの距離を返す。 { long angle, tacho_min=0, tacho_corr; int d_min; d_min=300; angle = (track/diameter)*ang;// 旋回角度からタイヤの回転を計算する。 turnAng(ang/2); // 旋回角度からタイヤの回転を計算する。 ResetTachoCount(OUT_AC); // 角度計測をリセットする。 OnFwdSync(OUT_AC,SPEED_SLOW,-100); while(MotorTachoCount(OUT_A)<=angle){ if(SensorUS(S2)<d_min){ d_min=SensorUS(S2); // 仮の最小値を更新する。 tacho_min=MotorTachoCount(OUT_A); } } OnFwdSyncEx(OUT_AC,SPEED_SLOW,100,RESET_NONE); until(MotorTachoCount(OUT_A)<=tacho_min||SensorUS(S2)<=d_min); Wait(14); //微調整 Off(OUT_AC);Wait(500); return d_min; ResetTachoCount(OUT_AC); // 角度計測をリセットする。 } ***缶に近づくためのプログラム [#k02ee8ec] void fwdDist(float d) //距離 d cm 前進する。 { long angle; angle = d/(diameter*pi)*360.0 ; //角度を計算する。 RotateMotorEx(OUT_AC,SPEED_SLOW,angle,0,true,true); } このプログラムにより、さきほどのsearchDirection()という関数によって得られたd_min (cm) の値の分だけロボットが前進する。また、このプログラムのモータを逆回転し下記のようなプログラムをつくることでd_min (cm) だけロボットを後退させ、缶を探し始めた位置まで戻ることが可能となる。 void MfwdDist(float d) //距離 d cm 後退する。 { long angle; angle = d/(diameter*pi)*360.0 ; ////角度を計算する。 RotateMotorEx(OUT_AC,SPEED_MSLOW,angle,0,true,true); } **缶を認識し、缶に近づくためのプログラム(青いボールをつかんだ時) [#v2bf3074] ***缶を認識するためのプログラム [#t550e9de] void turnAng2(long ang2) //角度 ang2 度の時計回りの旋回 { long angle2; angle2 = track/diameter*ang2; RotateMotorEx(OUT_AC, SPEED_SLOW, angle2, 100, true, true); } int e_searchDirection(long ang2) //現在の方向を中心にang2度の範囲で探す。 // 障害物までの距離を返す。 { long angle2, tacho_min2=0, tacho_corr2; int e_min; //仮の最小値 e_min=300; angle2 = (track/diameter)*ang2; //旋回角度からタイヤの回転を計算する。 turnAng2(ang2/2); //指定された角度の半分を旋回する。 ResetTachoCount(OUT_AC); //角度計測をリセットする。 OnFwdSync(OUT_AC,SPEED_SLOW,-100); //反時計回りに旋回する。 while(MotorTachoCount(OUT_A)<=angle2){ if(SensorUS(S2)<e_min){ e_min=SensorUS(S2); //仮の最小値を更新する。 tacho_min2=MotorTachoCount(OUT_A); } } OnFwdSyncEx(OUT_AC,SPEED_SLOW,100,RESET_NONE); until(MotorTachoCount(OUT_A)<=tacho_min2||SensorUS(S2)<=e_min); Wait(14); 微調整 Off(OUT_AC);Wait(500); return e_min; ResetTachoCount(OUT_AC); //角度計測をリセットする。 } ***缶に近づくためのプログラム [#k000b718] void e_fwdDist(float e) //距離 e cm 前進する。 { long angle2; angle2 = e/(diameter*pi)*360.0 ; //角度を計算する。 RotateMotorEx(OUT_AC,SPEED_SLOW,angle2,0,true,true); } このプログラムにより、さきほどのe_searchDirection()という関数によって得られたe_min (cm) の値の分だけロボットが前進する。また、このプログラムのモータを逆回転し下記のようなプログラムをつくることでe_min (cm) だけロボットを後退させ、缶を探し始めた位置まで戻ることが可能となる。 void e_MfwdDist(float e) //距離 e cm 後退する。 { long angle2; angle2 = e/(diameter*pi)*360.0 ; //角度を計算する。 RotateMotorEx(OUT_AC,SPEED_MSLOW,angle2,0,true,true); } **ボールを掴むためのプログラム(子機への命令) [#kdc79699] void Ball_catch() { RemoteStartProgram(1,"Last(S1).rxe"); //子機内にあるLast(S1)というプログラムを実行するように命令する。 while(msg != 11) //子機から「11」というメッセージが来るまで待つ。{ ReceiveRemoteNumber(MAILBOX1,true,msg); //メールボックス1を空にする。←子機からのメッセージが親機の卯木気を妨げるのを防ぐため。 } Wait(3000); } ボールを掴む動作自体は子機が行うため、このプログラムは子機に向かって親機が「ボールを掴め」と命令するようなプログラムとなっている。子機のプログラミングが終了したことを知らせるための合図としてmsgという変数を用いる。 int msg;// 子機のプログラムが終了したことを知らせるための合図。 **ボールを掴むためのプログラム(子機が動く) [#ba95a8da] Last(S1) #define Ball_catch ResetTachoCount(OUT_B);RotateMotor(OUT_B,-25,75); //電池残量によらず、常に一定の動きをする。 task main() { Ball_catch; //ボールを掴む。 SendResponseNumber(MAILBOX1,11); //親機に「11」というメッセージを送る。 Wait(1000); } **ボールを放出するためのプログラム(子機への命令) [#s71877b2] void Ball_release() { RemoteStartProgram(1,"Last(S2).rxe"); //子機内にあるLast(S1)というプログラムを実行するように命令する。 while(msg != 12) //子機から「12」というメッセージが来るまで待つ。{ ReceiveRemoteNumber(MAILBOX1,true,msg); //メールボックス1を空にする。←子機からのメッセージが親機の卯木気を妨げるのを防ぐため。 } Wait(3000); } ボールを放出する動作自体は子機が行うため、このプログラムは子機に向かって親機が「ボールを放出しろ」と命令するようなプログラムとなっている。子機のプログラミングが終了したことを知らせるための合図としてmsgという変数を用いる。 int msg;// 子機のプログラムが終了したことを知らせるための合図。 **ボールを放出するためのプログラム(子機が動く) [#b2fb030b] Last(S2) #define Ball_release ResetTachoCount(OUT_B);RotateMotor(OUT_B,25,75); //電池残量によらず、常に一定の動きをする。 task main() { Ball_release; //ボール放出する。 SendResponseNumber(MAILBOX1,12); //親機に「12」というメッセージを送る。 Wait(1000); } *工夫 [#k1d26e3c] #define THRESHOLD 50 #define go_forward OnFwd(OUT_A,40);OnFwd(OUT_C,40) //直進,Aは一番右のモータ,Cは一番左のモータ #define turn_left1 OnFwd(OUT_A,35);OnFwd(OUT_C,-30) //左旋回 #define turn_right1 OnFwd(OUT_C,35);OnFwd(OUT_A,-30) //右旋回 #define SPEED 50 #define SPEED_SLOW 30 #define SPEED_MSLOW -30 #define Ball_up RotateMotor(OUT_B,-50,160);Wait(2000); #define Ball_up2 RotateMotor(OUT_B,-50,50);Wait(2000); #define Ball_down RotateMotor(OUT_B,30,60);Wait(2000); #define Ball_down2 RotateMotor(OUT_B,30,150);Wait(2000); #define follow_intersection ResetTachoCount(OUT_AC);RotateMotor(OUT_AC,30,100); #define follow_intersection2 ResetTachoCount(OUT_AC);RotateMotor(OUT_AC,30,300); #define Mfollow_intersection ResetTachoCount(OUT_AC);RotateMotor(OUT_AC,-30,210); #define CONN 1 // スレーブの接続番号 int msg;// 子機を動かすための合図。 const float diameter = 5.45; // const float track = 10.35; // const float pi = 3.1415; // void Ball_release() { RemoteStartProgram(1,"Last(S2).rxe"); while(msg != 12){ ReceiveRemoteNumber(MAILBOX1,true,msg); } Wait(3000); } void Ball_catch() { RemoteStartProgram(1,"Last(S1).rxe"); while(msg != 11){ ReceiveRemoteNumber(MAILBOX1,true,msg); } Wait(3000); } void e_fwdDist(float e) { long angle2; angle2 = e/(diameter*pi)*360.0 ; // RotateMotorEx(OUT_AC,SPEED_SLOW,angle2,0,true,true); } void e_MfwdDist(float e) { long angle2; angle2 = e/(diameter*pi)*360.0 ; // RotateMotorEx(OUT_AC,SPEED_MSLOW,angle2,0,true,true); } void turnAng2(long ang2) { long angle2; angle2 = track/diameter*ang2; RotateMotorEx(OUT_AC, SPEED_SLOW, angle2, 100, true, true); } int e_searchDirection(long ang2) { long angle2, tacho_min2=0, tacho_corr2; int e_min; e_min=300; angle2 = (track/diameter)*ang2; turnAng2(ang2/2); ResetTachoCount(OUT_AC); OnFwdSync(OUT_AC,SPEED_SLOW,-100); while(MotorTachoCount(OUT_A)<=angle2){ if(SensorUS(S2)<e_min){ e_min=SensorUS(S2); tacho_min2=MotorTachoCount(OUT_A); } } OnFwdSyncEx(OUT_AC,SPEED_SLOW,100,RESET_NONE); until(MotorTachoCount(OUT_A)<=tacho_min2||SensorUS(S2)<=e_min); Wait(14); Off(OUT_AC);Wait(500); return e_min; ResetTachoCount(OUT_AC); } void Lfollow_line(long min_t) //min_tにいれた時間分だけラインの左側をライントレースする。 { long t0=CurrentTick(); //現在の時間を記録する。 while(CurrentTick()-t0<=min_t) //現在の時間とt0の時間の差がmin_t以下の時、以下のプログラムを実行する。{ if ((SENSOR_1<THRESHOLD-7)&&(SENSOR_4<THRESHOLD-11)){ Off(OUT_AC);Wait(1000); //S1の明るさの値が43未満かつS4明るさの値が39未満の時、ロボットが停止する。 } else if (SENSOR_1<THRESHOLD-7){ turn_left1; //明るさの値が43未満の時、turn_left1の動きをする。 } else if (SENSOR_1<THRESHOLD+7){ go_forward; //明るさの値が43以上57未満の時、go_forwardの動きをする。 } else { turn_right1; //それ以外の時、turn_right1の動きをする。 } } } void follow_line(long min_t) //min_tにいれた時間分だけラインの左側をライントレースする。 { long t0=CurrentTick(); //現在の時間を記録する。 while(CurrentTick()-t0<=min_t) //現在の時間とt0の時間の差がmin_t以下の時、以下のプログラムを実行する。{ if (SENSOR_1<THRESHOLD-7){ turn_left1; //明るさの値が43未満の時、turn_left1の動きをする。 } else if (SENSOR_1<THRESHOLD+7){ go_forward; //明るさの値が43以上57未満の時、go_forwardの動きをする。 } else { turn_right1; //それ以外の時、turn_right1の動きをする。 } } } *全体のプログラム [#h97cd118] SetSensorLight(S1); //右のセンサ SetSensorLowspeed(S2); //超音波センサ SetSensorLight(S4); //左のセンサ Wait(2000); //スタートボタンを押してから、離れるまでの時間。 Ball_catch(); //子機にボールを掴ませる。 follow_intersection; //スタートラインを横断する。 Lfollow_line(3000); //次の交差点まで進んで停止する。 follow_intersection; //交差点を横断する。 Ball_up; //アームをあげる。←このまま進行していくと青いボールにぶつかってしまう。 Lfollow_line(18000); //次の交差点まで進んで停止する。 int d=searchDirection(120);//缶を探す。 fwdDist(d-18.0); //缶に近づく。 searchDirection(20); //缶にボールを乗せられるように微調整する。 Ball_down; //アームを下げる。 Ball_release(); //子機で缶にボールを乗せる。 MfwdDist(d-17.0); //缶から離れる。 Ball_up2; //ライントレースを行う際に、ボールや缶にぶつかるのを防ぐためにアームを上げる。 follow_line(2000); //ライントレースに戻る。この時、地点を左折する。 Lfollow_line(3000); //次の交差点まで進んで停止する。 Mfollow_intersection; //青いボールを確実につかむことができるように後退する。 Ball_down2; //アームを下げる。 Ball_catch(); //子機にボールを掴ませる。 Ball_up; //アームをあげる。 follow_intersection2; //赤いボールを当てるための缶を超音波センサで感知しないように、前進する。 int e=e_searchDirection(120); //缶を探す。 e_fwdDist(e-18.0); //缶に近づく。 e_searchDirection(20); //缶にボールを乗せられるように微調整する。 Ball_down; //アームを下げる。 Ball_release(); //子機で缶にボールを乗せる。 e_MfwdDist(e-18.0); //缶に近づく。