目次 #contents *[[課題の説明>2014a/Mission1]] [#ofbcff9d] *メンバー [#tbb423e0] -bgpat: スタート→ゴール -[[只の某>2014a/Member/tadanobo/Mission1]]: ゴール→スタート *コースの作成 [#b0a98824] &ref(2014a/Member/bgpat/Mission1/course.png,50%); 正確なコースを作り、作業時間を短縮するために、 このような画像ファイルを作成しました。 これを何枚かのA4用紙に分けて印刷して台紙に貼り付け、 インクの薄い部分は黒いペンで塗りました。 しかし、貼り付ける作業に時間がかかってしまい、 あまいいい方法とは言えませんでした。 また、セロハンテープを使って貼り付けたため、テープの部分が少しだけ明るくなってしまいました。 しきい値の調整は難しかったですが、トレースにはほとんど問題ありませんでした。 *ロボット本体 [#i40141cf] **ドライブベース [#i6612d7b] 説明書の見本をもとに組み立てました。 見本よりもタイヤの幅を狭くしたことで小回りが効き、 光センサーを前輪に近づけることでトレースがしやすくなりました。 パーツがたくさん余ったので補強をしっかりしておきました。 &ref(2014a/Member/bgpat/Mission1/body.JPG,100%); **アーム [#i7d72672] アームはNXT本体の右、車体の右肩にあたる部分に取り付けました。 モーターが車体からはみ出してしまうため、多めの補強を施しました。 アームを上から下ろすことでピンポン玉を確保し、坂に押し上げます。 このとき、ピンポン玉の位置がずれても確保できるように坂を3箇所作りました。 ピンポン玉を確保したままゴールまで運びます。 シュートするときは、アームを上げるとピンポン玉が坂を転がります。 &ref(2014a/Member/bgpat/Mission1/DSC01846.jpg,20%); *プログラム [#xe5408e4] **定数 [#i0ee2036] 車体の構成に関する部分です // 入出力系 #define LEFT_MOTER OUT_C #define RIGHT_MOTER OUT_A #define LIGHT_SENSOR S3 // スピード #define SPEED 40 // 回転センサーの係数 #define MOTER_ROTATE (1 / 14.55) // 車体の幅 #define BODY_WIDTH 12.2 **マクロ [#sf63adb2] コードを簡略化するためのマクロです // 回転センサーの値を取得する #define LeftRotation() MotorRotationCount(OUT_C) #define RightRotation() MotorRotationCount(OUT_A) **移動用モーター制御用関数 [#t3845988] OnFwdとOnRevとOffをまとめた関数です。 スピードによって動作が変わるようになっています。 /* * モーターを回転させる * スピードが負のときは逆回転 * 0のときは停止 */ void On(int leftSpeed, int rightSpeed){ if(leftSpeed > 0){ OnFwd(LEFT_MOTER, leftSpeed); }else if(leftSpeed){ OnRev(LEFT_MOTER, -leftSpeed); }else{ Off(LEFT_MOTER); } if(rightSpeed > 0){ OnFwd(RIGHT_MOTER, rightSpeed); }else if(rightSpeed){ OnRev(RIGHT_MOTER, -rightSpeed); }else{ Off(RIGHT_MOTER); } } **明るさ判定関数 [#eb86e092] ライントレースに使います。 授業では触れていませんが、[["switch"という構文>http://bricxcc.sourceforge.net/nbc/nxcdoc/nxcapi/switch.html]]を使うことを想定しています。 光センサーのしきい値はここに記入します。 /* * 明るさを 0 〜 4 で返す * 暗い < 0 1 2 3 4 > 明るい */ bool Light(){ if(SENSOR_3 < 40){ return 0; }else if(SENSOR_3 < 45){ return 1; }else if(SENSOR_3 < 50){ return 2; }else if(SENSOR_3 < 55){ return 3; }else{ return 4; } } **メイン関数のコード [#sa2e140e] [[解説はこちら>#k0f87237]] コード全体が見たい人以外は飛ばしてください。 解説には部分ごとにコードを載せています。 task main(){ //通過ポイントを数えるための変数 int corner = 0; // 車体の向き float rotation = 0; // 光センサー初期化 SetSensorLight(LIGHT_SENSOR); // スタートから出る On(SPEED, SPEED); until(Light() == 0); until(Light() == 4); // トレース開始 while(corner < 8){ Wait(1); switch(Light()){ case 0: //左回転 On(-SPEED, SPEED); break; case 1: //左折 On(0, SPEED); break; case 2: //直進 On(SPEED, SPEED); break; case 3: //右折 On(SPEED, 0); break; case 4: //右回転 On(SPEED, -SPEED); break; } // 回転センサーの値を取得する int _tl = LeftRotation(); int _tr = RightRotation(); // 差から角度を求める rotation += 360 * (_tr - tr - _tl + tl) * MOTER_ROTATE / (2 * BODY_WIDTH * PI); tl = _tl; tr = _tr; // 通過ポイント switch(corner){ case 0: // 1回目のヘアピンカーブ if(rotation > 90){ PlayTone(TONE_C4, 100); corner++; } break; case 1: // 1回目の交差点 if(rotation < 10){ PlayTone(TONE_D4, 100); On(SPEED, SPEED); until(Light() == 0); until(Light() >= 2); corner++; } break; case 2: // ピンポン玉を取る位置 if(rotation < -40){ PlayTone(TONE_E4, 100); Wait(500); On(SPEED, SPEED); RotateMotor(OUT_B, 10, 90); On(-SPEED, -SPEED); Wait(500); On(SPEED, 0); until(Light() < 4); corner++; } break; case 3: // 大きいカーブ終了 if(rotation < -180){ PlayTone(TONE_F4, 100); corner++; } break; case 4: // 2回目の交差点 if(rotation > -180){ PlayTone(TONE_G4, 100); On(SPEED, 0); until(Light() == 4); On(SPEED, SPEED); until(Light() == 3); corner++; } break; case 5: // 3回目の交差点 if(rotation > 10){ PlayTone(TONE_A4, 100); On(SPEED, 0); until(Light() >= 3); corner++; } break; case 6: // 2回目のヘアピンカーブ if(rotation > 90){ PlayTone(TONE_B4, 100); corner++; } break; case 7: // ゴール if(rotation < -15){ PlayTone(TONE_C5, 100); On(0, 0); RotateMotor(OUT_B, 100, -90); corner++; } break; } } Wait(2000); } **メイン関数の解説 [#k0f87237] ***変数とセンサーの初期化 [#k6bd95c1] corner変数には通過ポイントの番号を、 rotation変数には車体の向きを格納します。 トレースに使用する光センサーを初期化します。 //通過ポイントを数えるための変数 int corner = 0; // 車体の向き float rotation = 0; // 光センサー初期化 SetSensorLight(LIGHT_SENSOR); ***スタートから出る [#j7b5760d] 黒い線を跨ぐまで直進してスタートエリアから出ます。 On(SPEED, SPEED); until(Light() == 0); until(Light() == 4); ***ライントレースのループ処理 [#j5421e60] スタートエリアから出るとライントレースを開始します。 ゴールに到達してシュートするまで繰り返すループ関数です。 1ミリ秒で1ステップ進みます。 シュートをする前にプログラムが終了しないようにするために ループのあとにWaitを入れました。 while(corner < 8){ Wait(1); /* * ライントレース */ /* * 車体の向きを求める */ /* * 通過ポイントごとの処理(交差点を渡るなど) */ } Wait(2000); ***ライントレース [#f99fefa4] 明るさを0から4の5段階に分けて、0と4のときはその場で回転、1と3のときは曲がりながら進む、2のときは直進します。 [[Light関数についてはこちら>#eb86e092]]をご覧ください。 switch(Light()){ case 0: //左回転 On(-SPEED, SPEED); break; case 1: //左折 On(0, SPEED); break; case 2: //直進 On(SPEED, SPEED); break; case 3: //右折 On(SPEED, 0); break; case 4: //右回転 On(SPEED, -SPEED); break; } ***車体の向きを求める [#s4d5e350] モーターの回転センサーを使って車体の向きを計算します。 "内側と外側の回転数の差"を"車体と幅から求めた車輪の円周"と"モーターが1回転したときの回転センサーの値"で割ることで計算しています。 0°は車体を置いた向きとなります。 // 回転センサーの値を取得する int _tl = LeftRotation(); int _tr = RightRotation(); // 差から角度を求める rotation += 360 * (_tr - tr - _tl + tl) * MOTER_ROTATE / (2 * BODY_WIDTH * PI); tl = _tl; tr = _tr; ***通過ポイントごとの処理 [#nc87e345] 通過ポイントを設定して、車体の向きからコースのどのあたりにいるかを求めています。 デバッグ用に音を鳴らしています。 詳細は後述します。 &ref(2014a/Member/bgpat/Mission1/point.png,50%); switch(corner){ case 0: /* * あとで解説 */ break; } ---- +1回目のヘアピンカーブ ~ ~車体の向きが90°を超えると通過ポイント1を超えたことにします。 if(rotation > 90){ PlayTone(TONE_C4, 100); corner++; } break; ~ +1回目の交差点 ~ ~車体の向きが10°未満になると通過ポイント2を超えたことにして ~交差点を超えます。 ~ ~スタートと同じように線を超えるまで直進して交差点を超えます。 ~ if(rotation < 10){ PlayTone(TONE_D4, 100); On(SPEED, SPEED); until(Light() == 0); until(Light() >= 2); corner++; } } break; ~ +ピンポン玉を取る位置 ~ ~1回目の交差点を超えて ~車体の向きが-40°(320°)より小さくなるとると ~ピンポン玉を拾います。 ~ ~直進しながらアームを下げ、 ~ボールを確保すると元の位置までバックします。 ~右に進みながらライントレースに復帰します。 ~ if(rotation < -40){ PlayTone(TONE_E4, 100); Wait(500); On(SPEED, SPEED); RotateMotor(OUT_B, 10, 90); On(-SPEED, -SPEED); Wait(500); On(SPEED, 0); until(Light() < 4); corner++; } break; ~ +大きいカーブ終了 ~ ~車体の向きがスタートしたときと反対になると大きいカーブが終了したとみなします。 ~ if(rotation < -180){ PlayTone(TONE_F4, 100); corner++; } break; ~ +2回目の交差点 ~ ~大きいカーブが終了して、車体が左に向くと2回目の交差点を越えます。 ~ ~進み方や光センサーの条件を変えていますが、1回目の交差点を超えるときとほとんど同じです。 ~ if(rotation > -180){ PlayTone(TONE_G4, 100); On(SPEED, 0); until(Light() == 4); On(SPEED, SPEED); until(Light() == 3); corner++; } break; ~ +3回目の交差点 ~ ~車体の向きが10°を超えると3回目の交差点です。 ~本来は0°ですが、実際動かすと10°くらいが丁度よくなります。 ~ ~この手前に直角カーブがありますが、ライントレースの処理だけで通過することができます。 ~ ~方向を修正するように右に進み、交差点を越えていきます。 ~線から出るとライントレースに戻ります。 ~ if(rotation > 10){ PlayTone(TONE_A4, 100); On(SPEED, 0); until(Light() >= 3); corner++; } break; ~ +2回目のヘアピンカーブ ~ ~1回目のヘアピンカーブと同じように車体の向きが90°を超えると7個目の通過ポイントです。 ~ if(rotation > 90){ PlayTone(TONE_B4, 100); corner++; } break; ~ +ゴール ~ ~車体の向きが15°より小さくなるとゴールです。 ~ ~車体はゴールの方を向いているので、アームを上げてシュートします。 ~ ~変数cornerが8になるのでループから抜けて、プログラムが終了します。 ~ if(rotation < -15){ PlayTone(TONE_C5, 100); On(0, 0); RotateMotor(OUT_B, 100, -90); corner++; } break; ~ *工夫した点 [#s41c4e3f] &ref(2014a/Member/bgpat/Mission1/rotation.png,50%); &ref(2014a/Member/bgpat/Mission1/saka.png,50%); *感想 [#e41a8e39] 編集中です、 時間をください・・・ *コメント [#e006bb1b] #comment