目次
#contents
* メンバー [#ibb85475]
kiyomizu : スタート→ゴールを担当
suiden : ゴール→スタートを担当
* 課題の内容 [#w222b816]
図のようなコースを各自で作成した上で、次のような動作をするロボットを作成せよ。 コースの詳細については授業中説明する(下の図において、黒い線の太さは20mmで、線の中央までの距離がcm表示されている)。
+ スタート地点から出発し、黒い線にそって動く。
+ 一つ目の十字の交差点と二つめの十字の交差点の間に置かれたピンポン玉をつかむ。
+ さらに黒線に沿って動いてゴール地点を目指す。
+ ゴールに向けてピンポン玉をシュートする。
ただしT字路では、直進、右折、左折を正しく判断させること。また二人一組で同じロボットを使用し、互いにスタート地点とゴール地点を入れ替えること。ロボット本体は共通のものを使うとする。
さらに以下のような動作に挑戦することが望ましい。
+ ゴールエリア外からピンポン玉をシュートする。
+ 曲線部中央の障害物に接触しないようにすること。
&ref(2014a-mission1.png);
* ロボットの紹介 [#qd0e48a4]
** ロボットの外観 [#e9cd888b]
&ref(robo2.jpg);
&ref(robo1.jpg);
&ref(robo3.jpg);
アームを上げた状態のロボット アームを下げた状態のロボット 後ろから見たロボット
** 光センサー・バンパー[#ld0a546a]
&ref(robo4.jpg);
中央にあるのが光センサーであり、ラインをトレースするときに使用する。
両サイドにあるのがバンパーであり、より確実にボールを確保できるようにしている。
** アーム [#d4f186e7]
&ref(robo8.jpg);
&ref(robo9.jpg);
アームは輪のような構造になっており、モーターを回転させ、ロボットから見て手前の棒とバンパーでボールを挟み、ボールの下に棒を通し、確保する仕組みになっている。
輪の中にボールが入れば、確実にゴールまで持っていきシュートを打つことができるので、この仕組みを採用した。
しかし、輪の中にボールが入らないことがあったので、L字形のパーツで返しをつけ、ボールを取りこぼさないように工夫した。
** ボールの捕り方・シュートの打ち方 [#s1fd127d]
&ref(robo5.jpg);
&ref(robo6.jpg);
&ref(robo7.jpg);
アームを下げてボールを捕る ボールを確保 アームを上げてシュートを打つ
* プログラムの内容 [#n91931a1]
&ref(2014a-mission1-kouryaku.png);
プログラムの内容を上図で示されたPartごとに分けて説明する。
** Part0:定義 [#c2c9cc66]
#define THRESHOLD 50 // しきい値を50に設定
#define turn_left0 OnFwd(OUT_B,30); // 左折
#define turn_left1 OnFwd(OUT_B,20);OnFwd(OUT_C,-20); // 左旋回
#define turn_right0 OnFwd(OUT_C,30); // 右折
#define turn_right1 OnFwd(OUT_C,20);OnFwd(OUT_B,-20); // 右旋回
#define STEP 1 // 1回の判断で動作させる時間
#define nMAX 250 // 連続してラインを認識する回数の限度
#define short_break Off(OUT_BC);Wait(1000); // 小休止
#define cross_line OnFwd(OUT_BC,30);Wait(300);short_break;PlaySound(SOUND_UP); // 交差点を渡る
** Part1 [#u87a2841]
task main()
{
SetSensorLight(S1); // 光センサーをポート1に接続
long t0 = CurrentTick(); // タイマー�,鰺儖�
int nOnline = 0; // ライン上に存在した回数をカウントする変数を用意
int nPoint = 0; // 次のPartに移行するための変数を用意
OnFwd(OUT_BC,30); // 0.7秒間直進
Wait(700);
Part1は、スタートの枠内から出るためのプログラムである。
** Part2 [#q3248486]
while (CurrentTick() - t0 < 10000) { // 開始から10秒未満のとき以下の動作を実行
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
} else {
if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +2) { // 黒と白の境界では直進
OnFwd(OUT_BC,30);
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
Wait(STEP); // 1/1000秒ごとに判断を行う
}
}
Part2は、開始から10秒未満のとき、ラインの左側をトレースするプログラムである。
** Part3 [#c5040110]
while (nPoint == 0) { // nPoint=0(初期状態)のとき以下の動作を実行
while (nOnline < nMAX){ // 交差点と判断しない限り以下の動作を実行
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
nOnline++; // nOnlineに+1する
} else {
if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +2) { // 黒と白の境界の上では直進
OnFwd(OUT_BC,30);
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
nOnline=0; // nOnlineを0にリセット
}
Wait(STEP); // 1/1000秒ごとに判断を行う
} // 交差点と判断したとき以下の動作を実行
short_break; // 小休止
turn_right1; Wait(nMAX*STEP); // 方向修正
cross_line; // 交差点を渡る
nOnline=0; // nOnlineを0にリセット
nPoint++; // nPointに+1する(nPoint=1にする)
}
Part3は、"nPoint=0"のとき(初期状態)、ラインの左側をトレースしつつ、交差点を渡るプログラムである。
また、交差点を渡ったとき、nPointを+1してPart4に移行する。
** Part4 [#x2bdc966]
long t1 = CurrentTick(); // タイマー�△鰺儖�
while (nPoint == 1) { // nPoint=1のとき以下の動作を実行
if (CurrentTick() - t1 == 5000) { // 5秒経過したとき以下の動作を実行
Off(OUT_BC); // ロボットを止める
RotateMotor(OUT_A,85,210); // アームを下げる
nPoint++; // nPointに+1する
PlaySound(SOUND_UP); // 音を鳴らす
} else {
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
} else if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
Wait(STEP); // 1/1000秒ごとに判断を行う
}
}
Part4は、"nPoint=1"のとき、ラインの左側をトレースしつつ、Part4開始から5秒が経過したとき、アームを下ろしボールを確保するプログラムである。
タイマーを利用してボールを確保するために安定した動きが必要であるため、このプログラムに限り直進"OnFwd(OUT_BC,50)"を入れていない。
また、ボールを確保したとき、nPointを+1してPart5に移行する。
** Part5 [#m89b5795]
while (nPoint == 2) { // nPoint=2のとき以下の動作を実行
while (nOnline < nMAX -100){ // 交差点と判断しない限り以下の動作を実行
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
nOnline++; // nOnlineに+1する
} else {
if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +2) { // 黒と白の境界では直進
OnFwd(OUT_BC,30);
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
nOnline=0; // nOnlineを0にリセット
}
Wait(STEP); // 1/1000秒ごとに判断を行う
} // 交差点と判断したとき以下の動作を実行
short_break; // 小休止
turn_right1; Wait(nMAX*STEP); // 方向修正
cross_line; // 交差点を渡る
nOnline=0; // nOnlineを0にリセット
nPoint++; // nPointに+1する(nPoint=3にする)
}
Part5は、"nPoint=2"のとき、ラインの左側をトレースしつつ、交差点を渡るプログラムである。
中央の交差点を渡るとき、交差点ではなくカーブと誤認識してしまうことが多かったため、"nMAX"を"nMAX -100"に変更することで、交差点と判断しやすくしており、誤認識を減らしている。
また、交差点を渡ったとき、nPointを+1してPart6に移行する。
** Part6 [#oa31f38f]
while (nPoint == 3) { // nPoint=3のとき以下の動作を実行
while (nOnline < nMAX){ // 交差点と判断しない限り以下の動作を実行
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
nOnline++; // nOnlineに+1する
} else {
if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +2) { // 黒と白の境界では直進
OnFwd(OUT_BC,50);
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
nOnline=0; // nOnlineを0にリセット
}
Wait(STEP); // 1/1000秒ごとに判断を行う
} // 交差点と判断したとき以下の動作を実行
short_break; // 小休止
turn_left1; Wait(nMAX*STEP -50); // 方向修正
cross_line; // 交差点を渡る
nOnline=0; // nOnlineを0にリセット
nPoint++; // nPoint+1する(nPoint=4にする)
}
Part6は、"nPoint=3"のとき、ラインの左側をトレースしつつ、交差点を渡るプログラムである。
上部の交差点を渡るとき、斜めに横断してしまいラインの右側に出てしまうことが多かったため、方向修正の"turn_right1"を"turn_left1"に、"nMAX*STEP"を"nMAX*STEP -50"変更することで、適当な時間で左に曲がりながら交差点を渡り、ラインの左側に出られるようにしている。
また、交差点を渡ったとき、nPointを+1してPart7に移行する。
** Part7 [#ibc0bdc7]
while (nPoint == 4) { // nPoint=4のとき以下の動作を実行
while (nOnline < nMAX){ // 交差点と判断しない限り以下の動作を実行
if (SENSOR_1 < THRESHOLD -10) { // 黒い部分の上では左旋回
turn_left1;
nOnline++; // nOnlineに+1する
} else {
if (SENSOR_1 < THRESHOLD -5) { // やや黒い部分の上では左折
turn_left0;
} else if (SENSOR_1 < THRESHOLD +2) { // 黒と白の境界では直進
OnFwd(OUT_BC,30);
} else if (SENSOR_1 < THRESHOLD +15) { // やや白い部分の上では右折
turn_right0;
} else { // 白い部分の上では右旋回
turn_right1;
}
nOnline=0; // nOnlineを0にリセット
}
Wait(STEP); // 1/1000秒ごとに判断を行う
} // 交差点と判断したとき以下の動作を実行
short_break; // 小休止
turn_right1; Wait(nMAX*STEP +100); // 方向修正
OnFwd(OUT_BC,-50); Wait(500); // 0.5秒間後退する
nOnline=0; // nOnlineを0にリセット
nPoint++; // nPoint+1する(nPoint=5にする)
}
Part7は、"nPoint=4"のとき、ラインの左側をトレースするプログラムである。
ゴール前の直角カーブを交差点と認識するが、"cross_line"を後退"OnFwd(OUT_BC,-50); Wait(500)"に、nMAX*STEP"を"nMAX*STEP +100"に変更することで、正確にゴールに正対した後、後退する。
また、nPointを+1してPart8に移行する。
** Part8 [#d79427ce]
while (nPoint == 5) { // nPoint=5のとき以下の動作を実行
Off(OUT_BC); // ロボットを止める
RotateMotor(OUT_A,-100,90); // アームを上げる
PlaySound(SOUND_UP); // 音を鳴らす
Off(OUT_A); // アームを止める
nPoint++; // nPointに+1する(nPoint=6にする)
}
}
Part8は、"nPoint=5"のとき、アームを上げ、シュートを打つプログラムである。
また、シュートを打った後、nPointを+1して"nPoint=6"にすることで条件から外し、プログラムを終了する。
** 実際のプログラム [#g3c01e9c]
理解しやすくするためにPartごとに区切って説明したが、実際のプログラムはPart0:定義〜Part8までをつなげたものである。
また、次のことに注意が必要である。
+ 電池残量が100%のときを想定してプログラムを組んだため、電池残量が少ないと正常に動作しない場合がある。
+ しきい値が日や時間によって変わることがあるため、しきい値の変更が必要な場合がある。
* 感想・反省 [#y966b4ba]
ロボットは、試行錯誤を繰り返しただけあってベストに近いものを作り上げられたと思う。理想を言うならば、もっと小回りの利くより小型なロボットを作りたかった。
プログラムは、成功率10割を目指す中で同じようなプログラムでも細かい数値調整や変更が必要だったため、全体で173行になってしまった。173行というのが多いのか少ないのかは分からないが、もっと容量が少なく済むような、効率的なプログラムが作れたと思うので、これからも勉強を続けていきたい。
また、当初は超音波センサーを利用してボールを確保する予定であったが、うまくプログラムを組むことができず、簡単に組むことができるタイマーを利用するプログラムに変更したため、成功率が下がってしまった。
最終的な成功率は、正確に数えて計算したわけではないが、8〜9割はあったと思う。目標である10割には届かなかったが満足のできる結果となった。
次の課題では、妥協することなく、ベストなプログラムを組めるようにしたい。