- 追加された行はこの色です。
- 削除された行はこの色です。
目次
#contents
*[[課題の説明>2014a/Mission1]] [#ofbcff9d]
*メンバー [#tbb423e0]
-bgpat: スタート→ゴール
-[[只の某>2014a/Member/tadanobo/Mission1]]: ゴール→スタート
*コースの作成 [#b0a98824]
&ref(2014a/Member/bgpat/Mission1/course.png);
&ref(2014a/Member/bgpat/Mission1/course.png,50%);
このような画像ファイルを作成して何枚かのA4用紙に分けて印刷し、台紙に貼り付けました。
薄い部分は黒いペンで塗りつぶしました。
正確なコースを作り、作業時間を短縮するために、
このような画像ファイルを作成しました。
セロハンテープを使って貼り付けたため、テープの部分が少しだけ明るくなってしまいましたが、トレースには問題ありませんでした。
これを何枚かのA4用紙に分けて印刷して台紙に貼り付け、
インクの薄い部分は黒いペンで塗りました。
しかし、貼り付ける作業に時間がかかってしまい、
あまいいい方法とは言えませんでした。
また、セロハンテープを使って貼り付けたため、テープの部分が少しだけ明るくなってしまいました。
しきい値の調整は難しかったですが、トレースにはほとんど問題ありませんでした。
*ロボット本体 [#i40141cf]
**ドライブベース [#i6612d7b]
説明書の見本をもとに組み立てました。
光センサーを近づけることで小回りが効くようにしています。
&ref(2014a/Member/bgpat/Mission1/body.JPG);
見本よりもタイヤの幅を狭くしたことで小回りが効き、
光センサーを前輪に近づけることでトレースがしやすくなりました。
パーツがたくさん余ったので補強をしっかりしておきました。
&ref(2014a/Member/bgpat/Mission1/body.JPG,100%);
**アーム [#i7d72672]
アームを上から下ろすことでボールを確保し、ボールを坂に押し上げます。
アームはNXT本体の右、車体の右肩にあたる部分に取り付けました。
モーターが車体からはみ出してしまうため、多めの補強を施しました。
坂のおかげでアームを上げるだけでシュートができます。
アームを上から下ろすことでピンポン玉を確保し、坂に押し上げます。
このとき、ピンポン玉の位置がずれても確保できるように坂を3箇所作りました。
坂は3箇所あり、位置がずれてもボールを取れる仕組みになっています。
&ref(2014a/Member/bgpat/Mission1/DSC01846.jpg);
ピンポン玉を確保したままゴールまで運びます。
シュートするときは、アームを上げるとピンポン玉が坂を転がります。
&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)
コードを簡略化するためのマクロです
// 回転センサーの値を取得する
#define LeftRotation() MotorRotationCount(OUT_C)
#define RightRotation() MotorRotationCount(OUT_A)
**移動用モーター制御用関数 [#t3845988]
OnFwdとOnRevとOffをまとめて、スピードによって動作が変わるようになっています。
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;
}
}
**メイン関数 [#t10751d9]
**メイン関数のコード [#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 < 20){
PlayTone(TONE_D4, 100);
On(SPEED, SPEED);
until(Light() == 0);
until(Light() >= 2);
corner++;
}
break;
case 2: // ボールを取る位置
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]
***トレース [#f99fefa4]
明るさを0から4の5段階に分けて、0と4のときはその場で回転、1と3のときは曲がりながら進む、2のときは直進するようします。
**メイン関数の解説 [#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回転したときの回転センサーの値"で割ることで計算しています。
正確な計算ではありませんが、だいたいあっていたのでこの方法を使いました。
通過ポイントを設定して、コースのどのあたりにいるかを求めています。
&ref(2014a/Member/bgpat/Mission1/point.png);
***ボール [#ec9fadc9]
1つ目の交差点を過ぎると角度を調整してアームを下ろします、このときボールが坂をのぼります。
0°は車体を置いた向きとなります。
ゴールにたどり着くとシュートはアームを上げるとボールが坂を転がります。
*感想 [#n5075af2]
なかなか思っている通りに動かず苦労しました。特に値の調整が大変でした。
// 回転センサーの値を取得する
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回目の交差点
~
~車体の向きが20°未満になると通過ポイント2を超えたことにして
~交差点を超えます。
~
~スタートと同じように線を超えるまで直進して交差点を超えます。
~
if(rotation < 20){
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;
~
*執筆中 [#e41a8e39]
時間が欲しい・・・
*コメント [#e006bb1b]
#comment