- 追加された行はこの色です。
- 削除された行はこの色です。
[[2013b/Member]]
#contents
*コースと攻略方法 [#y2bdf8ae]
●コースについて
コースは写真の通りである。今回は空き缶をコース上に配置して、それをどかしてもとに位置に戻しつつゴールを目指さなくてはならない。このコースの特徴を以下に示す。
&ref(2013b/Member/Redcicudu/Mission1/らいん1.jpg,39.9KB,何とも苦労したコースである);
コースは写真の通りである。今回は空き缶をコース上に配置し、それをどかしてもとに位置に戻しつつゴールを目指さなくてはならない。このコースの特徴を以下に示す。
+交差点は合計4回通過しなくてはならない。
+急なカーブや直角なカーブがあり、プログラムによっては交差点と誤認をしてしまう。
+空き缶は交差点から10センチ以上離して設置しなくてはならない。
以上が主な特徴である。
●攻略方法
まず、私のプログラムはテキストの例を参考に作られているため、急なカーブや直角のカーブを見分けることができない。そこで、交差点を渡った回数を数え、その回数ごとにトレースする方法を切り替えることでこのコースを攻略することにした。
*ロボットについて [#vb01f325]
&ref(2013b/Member/Redcicudu/Mission1/ろぼ.jpg,40.7KB,いいロボだろう?);
ロボットは写真の通り少し背高ノッポになってしまった。それはできる限り縦と横に広がってしまわないように配慮したからだ。縦と横に広いとセンサーを上手くつけられなかったり、旋回するのに余計タイヤを回さなくてはならなくなってしまった、という経験を生かしているつもりである。また、重心を中心にするという目的もある。
&ref(2013b/Member/Redcicudu/Mission1/ろぼ2.jpg,38.8KB,みどころですよ!);
アームは両側からはさむ形にした。広げきっているときはできるだけ邪魔にならないように胴体に近くなるように調整した。アーム用のモーターの回転軸が後方にあるため、長いリンケージロッドを利用しているのが特徴である。ロッドの接合部分はなかなか工夫したポイントの一つだ。
*プログラムについて [#q17d2328]
このプログラムはテキストのプログラムを参考にしている。私がプログラムに不慣れであったためにとんでもなく長いプログラムになってしまった。そこでいくつかに区切って説明をする。
●各種定義
#define BB 30 //閾値
#define BW 38
#define WB 52
#define WW 45
#define HA 40 //モーターの出力
#define HB 43
#define LA 20
#define LB 23
#define OnRL(speedR,speedL) OnFwd(OUT_A,speedR);OnFwd(OUT_B,speedL);
#define FWD OnRL(HA,HB); //前進
#define T_LEFT OnRL(LA,0); //左折
#define T_RIGHT OnRL(0,LB); //右折
#define Q_LEFT OnRL(LA,-LB); //左旋回
#define Q_RIGHT OnRL(-LA,LB); //右旋回
#define STEP 5 //一回の判断で行う動作の長さ
#define nMAX 40 //カウンタの最大値
#define SB Off(OUT_AB);Wait(1000); //小休止
#define CT 350 //交差点通過にかける時間
#define cross_line OnFwd(OUT_A,HA);OnFwd(OUT_B,HB);Wait(CT);SB; //交差点通過の動作
#define RMC(speed_c,-rad_c) RotateMotor(OUT_C,speed_c,rad_c);
#define speed_c 25 //アームの出力
#define rad_c 150 //アームの動作範囲
#define ARM1 RMC(speed_c,rad_c); //空き缶をつかむ
#define ARM2 RMC(speed_c,-rad_c); //空き缶をはなす
#define speed_ab 30 //モーターの出力
int nOnline=0; //カウンタの変数を設定、0とする。
int Online2=0;
int Online3=0;
int Online4=0;
int Online5=0;
int Online6=0;
int Online7=0;
ここでは各種動作や値の定義を行っている。ついでなのでカウンタ変数の設定も含めておいた。アームの動作は精密に行いたかったのでサーボモータとしての機能を利用した。おかげで無理な動作でアームが破損するというとは無かった。
●サブルーチンその1
sub CROSS_1()
{
while(nOnline<nMAX){
if(SENSOR_1<BB){
Q_RIGHT;
nOnline++;
} else {
if(SENSOR_1<BW){
T_RIGHT;
} else if(SENSOR_1<WB){
FWD;
} else if(SENSOR_1<WW){
T_LEFT;
} else {
Q_LEFT;
}
nOnline=0;
}
Wait(STEP);
}
}
このサブルーチンは進行方向から見て黒線の右側のふちに沿って走るプログラムである。簡単に説明すると、センサーの値が黒線の真っただ中を示したなら右へ、黒線の外なら左へ、その中間なら前進するというプログラムである。
ちなみに「nOnline」は、「右へ行く動作を何回行ったか」をカウントしている。その数が初めに定義した「nMAX」回より多くなったらこのサブルーチンを一旦停止するように設定してある。
●サブルーチンその2
sub CROSS_2() //黒線の左の縁に沿って動く、前のものとは逆になってる。
{
while(Online3<40){
if(SENSOR_1<BB){
Q_LEFT;
Online3++;
} else {
if(SENSOR_1<BW){
T_LEFT;
} else if(SENSOR_1<WB){
FWD;
} else if(SENSOR_1<WW){
T_RIGHT;
} else {
Q_RIGHT;
}
nOnline=0;
}
Wait(STEP);
}
}
こちらのサブルーチンは左側のふちに沿って進むようにしてある。つまりその1とは左右が逆になっているのだ。あとはカウンターの変数を混同してしまわないように別のものにしてある。
●サブルーチンその3
sub CROSS_3()
{
while(Online6<50){
if(SENSOR_1<BB){
Q_RIGHT;
} else if(SENSOR_1<BW){
T_RIGHT;
} else if(SENSOR_1<WB){
FWD;
Online6++;
} else {
if(SENSOR_1<WW){
T_LEFT;
} else {
Q_LEFT;
}
Online6=0;
}
Wait(STEP);
}
}
こちらは空き缶を掴む直前のライントレースである。その1と違うのはカウントするのは「何回前進したか」をカウントしている。このサブルーチンが終わった後にさらに前進し、空き缶を掴むのである。
●サブルーチンその4
sub CATCH_CAN()
{
RotateMotorEx(OUT_AB,speed_ab,90,0,true,true); //掴む位置まで移動
ARM1; //缶を掴む
Off(OUT_C); //小休止
Wait(500);
RotateMotorEx(OUT_AB,speed_ab,240,0,true,true); //回頭位置まで移動
RotateMotorEx(OUT_AB,speed_ab,180,-100,true,true); //90度回頭
Off(OUT_AB);
Wait(500);
RotateMotorEx(OUT_AB,speed_ab,500,0,true,true); //回頭位置まで移動(その2)
Off(OUT_AB);
Wait(500);
RotateMotorEx(OUT_AB,speed_ab,410,100,true,true); //180度回頭
ARM2; //缶を離す
Off(OUT_C);
Wait(500);
RotateMotorEx(OUT_AB,-speed_ab,100,0,true,true); //缶から離れる
RotateMotorEx(OUT_AB,speed_ab,410,100,true,true); //回頭して前を向く
}
これは空き缶を掴むための一連の動作なのだが、空き缶を直角カーブ上に配置したので方向転換が少し面倒である。今回は電池の消費具合に左右されにくいようにサーボモーターの機能を利用した。
●メインタスク
task main()
{
SetSensorLight(S1); //光センサーを設定
SetSensorLowspeed(S2); //超音波センサーを設定
long t0=CurrentTick();
while(CurrentTick()-t0<=8000){ //最初の8秒間は交差点を認識させない、スタートのT字路を交差点と誤認するため
CROSS_1();
}
while(Online2<1){ //交差点を一回渡ったらトレースする縁を切り替える
CROSS_1();
SB;
T_LEFT;Wait(nMAX*STEP); //方向修正
cross_line; //交差点を渡る
Online2++; //交差点を渡った回数を追加
nOnline=0; //カウンタをリセット
}
OnFwd(OUT_A,50); //反対の縁をトレースするので反対側へ移動
Wait(800);
while(Online4<1){ //交差点を一回渡ったらトレースする縁を切り替える(その2)
CROSS_2();
SB;
T_LEFT;Wait(nMAX*STEP); //方向修正(その2)
cross_line;
Online4++; //交差点を渡った回数を追加(その2)
Online3=0; //カウンタをリセット(その2)
}
OnFwd(OUT_B,50); //またトレースする縁が変わるので反対側へ移動
Wait(900);
while(Online5<2){ //交差点を二回渡ったら、交差点を認識しないようにする
CROSS_1();
SB;
T_LEFT;Wait(nMAX*STEP);
cross_line;
Online5++;
nOnline=0;
}
while(Online7<1){ //空き缶の目前までライントレース
CROSS_3();
SB;
FWD; //空き缶を感知するまで前進
until(SensorUS(S2)<=10);
CATCH_CAN(); //空き缶をつかむ一連の動作
Online7++;
nOnline=0;
}
CROSS_1(); //再びライントレースを行いゴールを目指す
}
これがメインタスクである。多分文字ばかり読んでいても分かりにくいと思うので写真を加工して簡単な図を作成してみた。
&ref(2013b/Member/Redcicudu/Mission1/らいん2.jpg,49.9KB,おわかりいただけただろうか?);
上の図において赤い線はサブルーチンその1を、青はその2を、緑はその3を、黄色はその4を実行する領域を表している。この図において注目していただきたい点はどの領域でどのサブルーチンを使用しているかではない。すべてコースの曲線に対して常に外回りに進むようになっているのだ。こうすることによって、交差点以外ではカウンターが作動してしまうことを防ぐことができたのである。また、直角の上に空き缶を置いたのは直線が続いているところが多く、スムーズに空き缶を掴む動作に移行できるからである。
*考察 [#m2fc770a]
プログラムに関しては考え方自体はシンプルで、自分なりに「交差点」という難敵を克服したしつもりである。しかし、プログラムに不慣れであったためにとてつもなく長くなってしまった。もっと定義の仕方やサブルーチンの使い方を考え直そうと思った。
また、超音波センサーが思った以上の曲者で、視野が狭いうえに動きながら検知を行うとスムーズに次の動作へ移れなったりとなかなか手を焼かされてしまった。こちらの改善もまだまだ必要だと感じた。
ロボットの製作はほとんど自分が自由勝手にやらせてもらった。講義に出席するたびにカタチの違うロボットのプログラムを行う羽目になった相方のpianoman君にはとても申し訳ないと思っている。それでもロボット自体に関しては自分なりにベストを尽くせたと考えている。
改善点を挙げるとするならば、
+プログラムを見やすくシンプルにする。
+超音波センサーによる検知の精度を向上させる、もしくはそれ以外の方法で空き缶を検知する。
+ロボットをよりコンパクトに、低重心に、かつ部品点数を少なくする。
以上の三つを改善すればよりよいロボットに生まれ変わると思う。