[[2013b/Member]] #contents *メンバー [#k8c3c921] Tomo,Naokiが基本的にハードの製作 Naoyoshiがプログラミング Yukiはアームを担当(?!)した *課題 [#g4a54fa9] &ref(robocon2013b.png); 図のコースを周回し、配置された缶を収集、積み上げを行うロボットを作成する。 **作戦 [#lc5c94a2] 今回私たちが課題に取り掛かるにあたって、最初に考えたのが、二つのNXTをそれぞれ独立して分離させるか、一体にするかである。 ***分離型 [#fca4b2a5] &ref(bunri.png); 最初私たちが考えたのがこの分離型で、その名の通り、二台のNXTを分離させ、それぞれ独立したロボットとして動かし、相互間の通信を適宜行うという物であった。 具体的には缶を収集するロボットを一つと、積み上げるロボット一つをゴールにて待機させるという物であったが、二台を連携させるまでの精度を実現するのは難しいという事で却下された。 ***一体型 [#g22e5d77] &ref(ittai.png); これが今回の課題で実際に実現された一体型で、二つのNXTを載せた巨大なロボットで、缶の収集、積み上げを一台で行う。上記の分離型に比べて、精度は調節しやすいが、二台のNXTという荷重を持ったままコースを周回しなければならないというデメリットもある。 *ロボット [#f816d538] &ref(1.jpeg); これが私たちのロボット。この形に落ち着くまで、数えきれないほどの試行錯誤があった。 **センサー [#daf0950b] 今回のロボットでは、NXTのキットを二つ使えるという事で、センサー類の数も単純に倍になったので、それを様々な点で利用した。 ***光センサー [#b3931ad2] &ref(6.jpeg); &ref(light.png); 光センサーを二つ使うことで、コースの黒線を挟む形で、ライントレース、交差点認識などを容易にさせた。 ***超音波センサー [#xee6b1ae] 超音波センサーを、正面だけでなく側面にも配置することで、コース全体の缶の位置を認識する これは実現には至らなかった。 **アーム [#la23cb10] &ref(7.jpeg); &ref(can.png); このアームの最大の特徴は、アームに底が付いている点である。 アームの下の部分のパーツ(写真の赤い部分)が、缶の底の窪みに入ることで、缶を3個、4個と重ねても、落ちないような設計になっている(図)。 &ref(8.jpeg); &ref(can2.png); また、軽量化をはかるため、アームのガイドは缶のつなぎ目を中心に、最小限で済むように工夫してある。 **ギア [#y541f36c] &ref(5.jpeg); ロボットを組み立てる中で、最初の難関は、アームにつなぐモータのトルクをいかにして稼ぐかという事であった。 最初はサーボモータにアームを直接付けていたが、アームの重量を水平状態から持ち上げるためには、サーボモータの出力を最低でも80%は出さなければならなかった。しかし、缶を積んで持ち上げるという非常に繊細な作業を行うアームを、このような出力で動かすのは、現実的ではない。そこで使ったのがギアである(写真)。写真の様にギアを接続することで、モータの出力を50%前後でアームを動かすことができるようになった。後に駆動にもこの機構を使った。 **本体 [#ee57e202] &ref(3.jpeg); 2つのNXTを写真の様にL字にすることで、全体の大きさをできるだけ小さくした。また、操作、配線等もしやすくなっている。 *プログラム [#fea8fbc9] 今回の課題での最大の特徴は、2台のNXTをBluetoothによる通信で動作させるという事である。この通信をするにあたって、2台をそれぞれマスターとスレーブとする。 私たちのロボットでは、基本的な駆動をマスター、アーム等の動作をスレーブという風に割り振った。 **マスター [#waa312c6] ***定義 [#x5c4aa84] 定義は以下の通り #define RIGHT OUT_B #define LEFT OUT_C #define BOTH OUT_BC #define SPEED1 35 #define SPEED2 SPEED1*2 #define OnRL(speedR,speedL) OnRev(RIGHT,speedR);OnRev(LEFT,speedL); //ギアの都合で正転 と逆転が反対 #define go_forward OnRevSync(BOTH,SPEED2,0); //左右のタイヤの移動距離をシンクロさせて前進 #define go_back OnFwdSync(BOTH,SPEED2,0); //後退 #define turn_left1 OnFwdSync(BOTH,SPEED2,100); //時計回りに旋回。3つ目の引数100は旋回率(右のモータと左のモータの回転角度が100%異なる) #define turn_left0 OnRL(SPEED2,0); //左折 #define turn_right0 OnRL(0,SPEED2); //右折 #define turn_right1 OnFwdSync(BOTH,SPEED2,-100); //半時計回りに旋回 #define u_turnCW OnRL(-SPEED1,SPEED1); //時計回りに旋回。音波探知用にスピードを落としている。 #define u_turnRCW OnRL(SPEED1,-SPEED1); //半時計回りに旋回 #define THRESHOLD 45 #define RANGE1 7 //ライントレース中に缶をつかむ動作に移行するための距離 #define RANGE2 19.8 //アームを伸ばした時の缶までの距離 #define RANGE3 23 //回転しながら #define RANGE4 5 //前方に感知した缶をバンパーのホルダーに押しこむのに必要な距離 #define STEP 1 #define SR SENSOR_2 #define SL SENSOR_1 #define SS SensorUS(S3) #define CONN 1 #define arm_down "arm1.rxe" //アームを開き、つかむ準備をするプログラムを定義しなおした。特にアームをダウンさせるわけではない #define arm_up "arm2.rxe" //アームの降下→握り→上昇の一連の動作を定義しなおした #define BUILD_RAG 1000 //ゴールの交差点に止まったあと、ゴール中央を向くための回転に必要な時間。 #define DO 523 //音程C、E、Gを指定。hiCはCの二倍で対応 #define MI 659 #define SO 784 #define SIGNALON 11 //Mail受信用 #define SIGNALOFF 12 #define LATIO 3 //走行駆動系のギア比(24:8)。距離の算出に必要 int nFase,angleA,angleB,msg,nCross; float GetAngle(float r) //引数r[cm]だけ進むのに必要な回転角を導くマクロ。ギア比考慮済み { const float diameter=5.45; const float pi=3.1415; float ang=r/(diameter*pi)*360.0*LATIO; return ang; } ***サブルーチン [#k8a28f0a] sub find_line() //黒線を見つけるまで前進し、左右両方のセンサが線を検知するまで角度を調整するサブルーチン { until((SL<=THRESHOLD)&&(SR<=THRESHOLD)) { if((SL>THRESHOLD)&&(SR>THRESHOLD)) { go_forward; } else if((SL>THRESHOLD)&&(SR<=THRESHOLD)) { turn_right0; } else { turn_left0; } Wait(STEP); } } sub start_traceR() //左右の光センサが先に乗った状態から右方向にライントレースを開始するためのサブルーチン { until(SL>THRESHOLD) { turn_right0; } PlaySound(SOUND_CLICK); Off(BOTH); } sub start_traceL() //左右の光センサが先に乗った状態から左方向にライントレースを開始するためのサブルーチン { until(SR>THRESHOLD) { turn_left0; } PlaySound(SOUND_CLICK); Off(BOTH); } sub traceR() //右側の光センサを使ってライントレースを行うモード { if(SR<THRESHOLD-4) { turn_right1; } else if(SR<THRESHOLD) { turn_right0; } else if(SR<THRESHOLD+4) { go_forward; } else if(SR<THRESHOLD+7) { turn_left0; } else { turn_left1; } } sub traceL() //左側の光センサを使ってライントレースを行うモード { if(SL<THRESHOLD-4) { turn_left1; } else if(SL<THRESHOLD) { turn_left0; } else if(SL<THRESHOLD+4) { go_forward; } else if(SL<THRESHOLD+7) { turn_right0; } else { turn_right1; } } sub search_crossR(int MAXCross) //右側の光センサを使ってライントレースを行いつつ、引数MAXCrossの分だけ交差点を感知するサブルーチン。交差点の判定は左右の光センサが同時に閾値を下回ったことで行う。 { nCross=0; while(nCross<MAXCross) { until((SL<=THRESHOLD)&&(SR<=THRESHOLD)) { traceR(); Wait(STEP); } nCross++; PlaySound(SOUND_UP); until((SL>THRESHOLD)&&(SR>THRESHOLD)) { go_forward; Wait(STEP); } } } sub search_crossL(int MAXCross) //同じく左側の光センサによるライントレースと同時に交差点判定を行う { nCross=0; while(nCross<MAXCross) { until((SL<=THRESHOLD)&&(SR<=THRESHOLD)) { traceL(); Wait(STEP); } nCross++; PlaySound(SOUND_UP); until((SL>THRESHOLD)&&(SR>THRESHOLD)) { go_forward; Wait(STEP); } } } sub catch() //持っている缶を新しい缶の上に載せ、掴み、また持ち上げる一連の動作。 { angleB=GetAngle(RANGE4); RotateMotor(BOTH,SPEED1,-angleB); //缶をホルダーの中に押しこむために少し前進する PlayTone(262,400); //ネット上で見つけたメロディー。特に意味はない。 Wait(500); PlayTone(294,400); Wait(500); PlayTone(300,400); Wait(500); PlayTone(294,400); Wait(500); RemoteStartProgram(CONN,arm_down); //スレーブにアームを開かせる Wait(5000); ReceiveRemoteNumber(MAILBOX1,true,msg); if(msg==SIGNALON) { PlaySound(SOUND_CLICK); } else { PlaySound(SOUND_FAST_UP); } angleA=GetAngle(RANGE2); RotateMotor(BOTH,SPEED2,angleA); //アームの伸びに合わせ後退する RemoteStartProgram(CONN,arm_up); //スレーブに缶を掴ませる PlayTone(DO,120);Wait(150); //達成感のあるメロディー。一回ごとに半音ずつ上昇させたかった PlayTone(MI,120);Wait(150); PlayTone(SO,120);Wait(150); PlayTone(DO*2,240);Wait(300); PlayTone(SO,120);Wait(150); PlayTone(DO*2,360);Wait(450); Wait(10000); if(msg==SIGNALON) { PlaySound(SOUND_CLICK); } else { PlaySound(SOUND_FAST_UP); } RotateMotor(BOTH,SPEED2,-angleA+angleB); //つかむ動作の開始点まで戻る。 } sub build() //ゴールで缶を載せるだけのプログラム。最後まで使われなかった { PlayTone(262,400); Wait(500); PlayTone(294,400); Wait(500); PlayTone(524,400); Wait(500); PlayTone(294,400); Wait(500); RemoteStartProgram(CONN,arm_down); Wait(10000); go_back; Wait(2000); Off(BOTH); nFase++; } ***メイン [#kcffdc96] task main() //スタートからゴールまでの一連の流れを記述したプログラム { SetSensorLight(S1); SetSensorLight(S2); SetSensorLowspeed(S3); nFase=0; //第0段階開始(ラインに乗るまで) msg=SIGNALOFF; until(BluetoothStatus(CONN)==NO_ERR); find_line(); start_traceR(); nFase++; //第一段階開始 (一つ目の缶をつかむまで) while(SS>RANGE1) { traceR(); Wait(STEP); } Off(BOTH); catch(); nFase++; //第二段階開始(2つ目の缶をつかむまで) while(nFase<3) { until(SS<RANGE3) //缶を感知するまで回転し続ける { u_turnRCW; Wait(STEP); } while(SS>RANGE1) //感知したらホルダーに格納されるまで前進 { go_forward; Wait(STEP); } Off(BOTH); catch(); nFase++; } while(nFase<4) //第三段階開始(3つ目の缶を格納するまで) { until(SS<RANGE3+3) { u_turnRCW; Wait(STEP); } u_turnCW; Wait(200); Off(BOTH); while(SS>RANGE1) { go_forward; Wait(STEP); } Off(BOTH); //3つ目の缶は掴まず、ホルダーで抱えたままゴールで缶を2つ載せる nFase++; //第四段階開始(缶をゴールに運び、塔を建てるまで) } until((SR<THRESHOLD)||(SL<THRESHOLD)) //反転し、ライントレースに戻る { u_turnRCW; } find_line(); start_traceL(); search_crossL(2); //2つ目の交差点を過ぎたところで停止、缶を下ろす Off(BOTH); build(); go_back;Wait(3000); u_turnRCW;Wait(BUILD_RAG); } **スレーブ [#cb6d1b57] アームの動作の途中に、本体の後退、つまりマスター側の動作が入るため、2つに分けてある。 ***動作1 [#u0b5ef3f] 持っている缶を離して積む動作。 #define SIGNALON 11 #define SIGNALOFF 12 #define CONN 0 task main() { until(BluetoothStatus(CONN)==NO_ERR); RotateMotor(OUT_A,20,-48); Wait(1000); PlaySound(SOUND_FAST_UP); Wait(2000); SendResponseNumber(MAILBOX1,SIGNALON); } これが終わるとマスター側で後退をする。 ***動作2 [#od757035] アームを伸ばし、先ほど積んだ缶を持ち上げる動作 #define SIGNALON 11 #define SIGNALOFF 12 #define CONN 0 task main() { until(BluetoothStatus(CONN)==NO_ERR); RotateMotorEx(OUT_BC,15,450,0,true,true); Wait(1000); RotateMotor(OUT_A,30,48); Wait(1000); PlaySound(SOUND_FAST_UP); Wait(1000); RotateMotorEx(OUT_BC,40,-450,0,true,true); Wait(1000); SendResponseNumber(MAILBOX1,SIGNALON); } *感想 [#y586977e] つかれたねむいしぬ&heart; 今回の課題を通して学んだのは、単にプログラムが、組立が...というだけでなく、限られた時間で与えられた課題をチームで突破するようにプロジェクトの進行をする事の難しさである。 課題に対して、どれだけの時間がかかるか、というビジョンを持つことや、ある程度の取捨選択を行うという面で、足りない部分が少なからずあった。ただ、厳しい条件の中で、チーム全員が協力して作り上げることができて良かった。