2018b/Member 目次
今回の課題は・・・
フィールドの説明
基本ルール
基本得点の計算方法
技術点の計算方法
この課題では、ボールを拾って缶に乗せる。その為、ボールを拾った後に缶の高さまで持ち上げる必要があり、更に、ボールを掴むという動作にモータを一つ使うことになる。
私達のチームでは、電池の消耗という点からボールを掴むモータを持ち上げるのは難しいという結論に至った。この問題を解決するに当たって、チームメンバーからマジックハンドという案が挙がり、採用された。
これが実際にできたものである。
上部にあるギアが回ることで、下の二つのギアがそれぞれ逆方向に回り、開閉する仕組みとなっている。
下図はハンドパーツを上から見たものである。上部にあったギアを➡の方向に回すと、その下のギアが➡の方向に回る。その為、ハンドパーツが開く。
こちらは、ハンドを上下させるアームパーツだ。
写真の下側のモータがアームを上下させるようになっている。 実際に動かすと、アームの動きは下の図のようになる。黒いパーツがハンドを取りつけるもので、黄緑のパーツがモータの回転によって動くパーツである。
モータ一つだと負担が大きいので、二つ並べて使用し負荷を軽減している。
また、ハンドへ動力を伝える為に、軸となるパーツにギアを取り付けた。このギアを通して、ハンドの開閉を行うことができる。
車体はブロックでフレームを製作し、そこにモータやタイヤを取り付け、頑丈になるようにした。
しかし、アームが予想以上に重く、車体を支える為アームの下近くに大きなタイヤを二つ追加し、計四つも使用することになった。
また、モータへの負担を軽減するため、ここにもギアを使用した。
ここのギアの配置は次にようになっている。
モータとギア1、ギア2とギア3、ギア4とタイヤが直結している。各ギアの歯数は次の通り。
ギア | 歯数 |
ギア1 | 8 |
ギア2 | 16 |
ギア3 | 8 |
ギア4 | 24 |
表から、ギア比は、次の式に歯数を代入することで求められる。
つまり、モータにかかる負担が6分の1になったことになる。
今回は、缶だけでなくボールも探さないといけないので、超音波センサを二つ使用した。
また、ライントレースに使用する光センサもアームの下に付けてある。
このロボットではハンドに一つ、アームに二つ、タイヤに二つモータを使用している。NXT一つではモータを三つまでしか制御できないが、二つのNXTをBluetoothで通信させることで動かす。
Bluetoothを使用する時、片方を"master"、もう片方を"slave"として動作させる。
ライントレース等、前回の課題のプログラムを流用しているので、新たに追加したり、変更した部分のみ説明する。
通るコースは次のようにした。
Bluetoothの通信はマクロでまとめた。
BluetoothStatus(slave番号)は通信ができているかどうかを示す関数。
RemoteStartProgram(slave番号,実行形式のプログラムファイル名)は指定したslaveの指定プログラムを起動する関数。
/************************************************************ ライントレ____@('ω' )@彡_____ス・プログラム *************************************************************/ /*マクロ*/ #define conn 1 //Bluetoothのスレーブ番号 #define fw Off(OUT_BC);OnFwd(OUT_BC,30); // 直進 #define lt Off(OUT_BC);OnFwd(OUT_B,35); // 左回 #define rt Off(OUT_BC);OnFwd(OUT_C,35); // 右回 #define rrt Off(OUT_BC);OnFwd(OUT_C,20);OnFwd(OUT_B,-60); // 右旋回 #define llt Off(OUT_BC);OnFwd(OUT_B,20);OnFwd(OUT_C,-60); // 左旋回 #define wait_sec Off(OUT_BC);Wait(1000); //一時停止 #define mini_go Off(OUT_BC);OnFwd(OUT_BC,30);Wait(1500);Off(OUT_BC); //少し直進 #define def_arm_up until(BluetoothStatus(conn)==NO_ERR);\ //アームを上げる RemoteStartProgram(conn,"arm_up.rxe");\ Wait(4000); #define def_arm_down until(BluetoothStatus(conn)==NO_ERR);\ //アームを下げる RemoteStartProgram(conn,"arm_down.rxe");\ Wait(4000); #define def_hand_open until(BluetoothStatus(conn)==NO_ERR);\ //放す RemoteStartProgram(conn,"hand_open.rxe");\ Wait(4000); #define def_hand_close until(BluetoothStatus(conn)==NO_ERR);\ //掴む RemoteStartProgram(conn,"hand_close.rxe");\ Wait(4000); const float tire_diameter = 5.75; //タイヤの直径 const float tire_axis = 17.7; //タイヤ間の距離 const int gear_ratio = 6; //ギア比 const float pi=3.14159; //円周率 const int en=360; //円 360度
基本は課題2のプログラムと同じ
void search_throw(string side_select,float rotate_angle){//最初の一周の部分で、旋回方向と角度を指定する引数を追加した。 SetSensorLowspeed(S2); int d_min=1000,d_gosa=11,side_flag=0;//side_flagは旋回方向を変更する為の変数 d_gosaはアームを上げた後に進む距離[cm] long ang_min=0,angle=0,d_angle=0;//d_angleは、缶との距離が遠い場合に近づいた距離を格納 if (side_select=="right"){ //右旋回なら side_flag=1; //1を旋回角度にかける }else if(side_select=="left"){ //左旋回なら side_flag=-1; //-1を旋回角度にかける } ResetTachoCount(OUT_BC); OnFwdSync(OUT_BC,30,100*side_flag); while(((tire_diameter/(tire_axis*gear_ratio))*MotorTachoCount(OUT_C)*side_flag)<=rotate_angle){//rotate_angleまで旋回する if(d_min>SensorUS(S2)){ d_min=SensorUS(S2); angle=MotorTachoCount(OUT_C); } } ang_min=MotorTachoCount(OUT_C)-angle; Off(OUT_BC); Wait(1000); ResetTachoCount(OUT_BC); RotateMotorEx(OUT_BC, 40, ang_min, -100*side_flag, true, true);//物体の方を向く Off(OUT_BC); ResetTachoCount(OUT_BC); if(7<SensorUS(S2)){ //缶との距離が7センチより遠い時 OnFwd(OUT_BC,40); //パワー40で進む while(7<SensorUS(S2) || 40>(pi*tire_diameter*MotorTachoCount(OUT_C))/en*gear_ratio){//缶との距離が7センチ以下になるか、進んだ距離が40センチになるまで続ける d_angle=MotorTachoCount(OUT_C); //モータCの回転角を更新 } Off(OUT_BC);//停止 RotateMotor(OUT_BC,30,en*2*gear_ratio/(pi*tire_diameter));//2センチ進む } def_arm_up;//アームを上げる RotateMotor(OUT_BC,30,en*d_gosa*gear_ratio/(pi*tire_diameter));//d_gosaの分だけ進む Wait(700); def_hand_open;//ボールを放す RotateMotor(OUT_BC,-30,en*d_gosa*gear_ratio/(pi*tire_diameter)+d_angle);//d_gosaとd_angleの分だけ戻る def_arm_down;//アームを下げる ResetTachoCount(OUT_BC); RotateMotorEx(OUT_BC, 40, angle, -100*side_flag, true, true);//元の向きへ戻す Off(OUT_BC); }
基本は課題2のプログラムと同じ
void blk_selection(int blk_select){ if(blk_select!=1){ wait_sec; } if(blk_select!=0){ mini_go; } }
void follow_line(int bw_time,long min_t,int blk_cnt,string invert_info,string sensor_switch){//sensor_switchはセンサの値が一定以下の時ループから抜けるかどうかを決める引数 SetSensorLowspeed(S3); int bb_cnt;//消し忘れた変数 int B_cnt=0;//上に同じ long t0,t1,start_t; // t0,t1,start_t 時間変数の定義 SetSensorLight(S1); // 光センサ定義 t1=CurrentTick(); // 経過時間 t0=CurrentTick(); // カーブ初期時間 start_t=CurrentTick(); // 追尾初期時間 int right_var; // 光センサ値用変数 while(t1-start_t<min_t || min_t<0){ if(sensor_switch=="on"){//sensor_switchがonの時 if(SensorUS(S3)<16){//超音波センサ2の値が16センチ未満の時 break;//ループから抜ける } } /*----------------↓繰り返し↓-----------------*/ right_var = SENSOR_1; // 光センサ値代入 if(invert_info=="on"){ // ラインの右か左か "off"で左,"on"で右 right_var = right_var*(-1)+91;} if (right_var>50){ /*********右回り↓********/ if (t1-t0>bw_time){ // 右回でbw_taime[ms]以上のとき右旋回 if (blk_cnt>=0 && blk_cnt<=2 && invert_info=="on"){ blk_selection(blk_cnt); break; }else{ rrt;} }else{ // 右回 rt;} /**********直進↓********/ }else if (right_var>40){ fw; t0=CurrentTick(); // カーブ初期時間の更新 /*********左回り↓*******/ }else{ if (t1-t0>bw_time){ // 左回で bw_taime[ms] 以上のとき左旋回 if (blk_cnt>=0 && blk_cnt<=2 && invert_info!="on") { blk_selection(blk_cnt); break; }else{ llt;} }else{ // 左回 lt;}} t1=CurrentTick(); //経過時間 /*----------------↑繰り返し↑-----------------*/ }Off(OUT_BC); }
task main(){ // -Y- string invert_str="off"; int b_tyokusin=10,r_back=22;//b_tyokusinは円に入る時に進む距離 r_backはEとLの間まで戻る時の距離 //blue-ball follow_line(1200, -1,1,invert_str,"off");//Eまで進んでEを越える follow_line(800, -1,0,invert_str,"off");//Iまで進んで停止 def_hand_close;//ボールを掴む ResetTachoCount(OUT_BC); RotateMotor(OUT_BC,30,en*b_tyokusin*gear_ratio/(pi*tire_diameter));//円の中心まで進む Off(OUT_BC); search_throw("right",55.0);//右旋回、55°で缶を探す Wait(2000); RotateMotor(OUT_BC,-30,en*b_tyokusin*gear_ratio/(pi*tire_diameter));//Iまで戻る Off(OUT_BC); //red_ball def_arm_up;//アームを上げる RotateMotor(OUT_BC,-30,en*r_back*gear_ratio/(pi*tire_diameter)); //EとLの間まで戻る follow_line(600,1200000,-1,invert_str,"on");//1200秒経つか、センサーの値が一定以下になるまでライントレース RotateMotor(OUT_BC,-30,en*3*gear_ratio/(pi*tire_diameter));// 3センチ下がる def_arm_down;//アームを下げる def_hand_close;//ボールを掴む search_throw("left",270);//左旋回、270°で缶を探す Wait(1000);Off(OUT_BC); }
アーム側では四つの簡単なプログラムを容易して、マスター側から通信があった時、任意のプログラムが動作するようになっている。 モータAがハンドの操作、モータB・Cがアームの操作を担当している。
task main(){ RotateMotor(OUT_BC,-40,90);//モータB・Cをパワー40、90°で逆回転させる }
task main(){ RotateMotor(OUT_BC,10,90);//モータB・Cをパワー10、90°で回転させる Wait(300); //300[ms]待機 Off(OUT_BC); //モータB・Cを停止 ResetTachoCount(OUT_BC);//モータB・Cの回転角をリセット }
task main(){ RotateMotor(OUT_A,-30,320);//モータAをパワー30、320°で逆回転させる。 Wait(300); //300[ms]待機 RotateMotor(OUT_BC,-40,15);//モータB・Cをパワー40、15°で逆回転させる Off(OUT_ABC); //モータA・B・Cを停止 }
task main(){ RotateMotor(OUT_A,30,320);//モータAをパワー30、320°で回転させる。 }