・saborijini
・bosoo
・hodaka
・snowdrop
2台のロボットで紙コップを収集し、同じ番号の書かれている紙コップに重ねる
フィールドは課題1で使用した紙を2枚つなぎ合わせる (詳細な図面は課題1を参照)。
図の右半分の黄色い地点には、その数字と同じ数字が書いてある紙コップが置かれている。 また左半分のA,B,Cの地点にも1から3までの数字が書かれている紙コップが置かれているが、 どのコップをどの地点に置くかは直前にサイコロを振って次のように決める。
サイコロの目 | A | B | C |
1 | 1 | 2 | 3 |
2 | 1 | 3 | 2 |
3 | 2 | 1 | 3 |
4 | 2 | 3 | 1 |
5 | 3 | 1 | 2 |
6 | 3 | 2 | 1 |
その他ルールなど詳細は課題2
全体の写真。NXTを縦に重ねることで、横幅の軽減に努めた。アームは上部に取り付けた。
超音波センサーを前につけた。これにより、紙コップの位置を正確に測ることが出来る。縦につけたほうがより精度が増したと考えられる。またラインとセンサーを下向きにつけ、フィールドのラインを識別した。これにより、ロボット自身が今どこにいるのか確認できる。
前からの写真。
アームの写真。ラインセンサーを横につけることで、紙コップの色の識別を可能にした。アームを持ち上げる部分、紙コップを持ち上げる部分の二つのモーターを分けた。モーター一つの重さが予想以上にあったため、アシストにを輪ゴムを使うことで、アームを上げる動作をスムーズにした。
アームの横からの写真。
紙コップを挟む動作。
ここでは、同じようなプログラム[PlaySound(SOUND_CLICK);やOff(OUT_BC);Wait(500);]が使われている。これはある動作が完了したことを示すために音を鳴らし、各動作をはっきりさせるために一時停止をしている。また今回の課題は余り時間をかけられなかったので1個のコップの運搬までしかプログラミングが出来ていない。コップは茶色のコップを使用し、白黒の着色で区別した。
まず、スタート位置から、赤色のルートを通り、紙コップAまで移動する。紙コップAをアームでつかみ、アームについているライントセンサーで紙コップの側面の色を判別(白・茶・黒)それに対応する1〜3の紙コップへ移動(青・緑・ピンクのルート)。後、B、Cも同様にして運ぶ予定であった。基本的に、曲がるタイミングはフィールド上のライン(赤丸の部分)に依存した。
#define black 36 #define speed 35 #define A 38 #define B 65
定数の定義。電池の種類・消耗次第でスピードが、照明次第でセンサーの感度が変わってくるのでよく調整する数値は代わりとなる文字に置き換えて作業を効率良くした。 上から順に黒線と白紙の境界値・ロボットの移動速度(モーターの速度)・黒コップと茶コップの境界値・茶コップと白コップの境界値を定義している。
task main() { SetSensorLight(S1); SetSensorLight(S4); SetSensorLowspeed(S3);
センサーの定義。1にはライントレース用の光センサー、3にはコップ認識用の光センサー、4にはコップとの距離認識用の光センサーをつないだことを示している。2には配線するのが困難であったため使っていない。
int msg;
msgという変数を用いることを示している。これは後で使う。
until(BluetoothStatus(1) == NO_ERR); RemotePlayTone(1,440,1000);
子機と通信が出来ているか確認して、上手くいっている時はピーっと音を鳴らすようにしている。もし通信が切れているなら、ロボットはこれ以降の動作を実行しない。
Off(OUT_BC); Wait(1200);
音を鳴らしている間はロボットは停止して、すぐには動き出さない。これは、急に動き出すと、ロボットを触る時、指が引っかかる可能性があるため(起動ボタンは下の親機にある)。
OnFwdSync(OUT_BC,speed,0); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
ここからロボットが動き出す。OnFwdSyncを用いて直進している。モーター2つの力が等しいとは限らないため、2つを同期させることで真っ直ぐ進めるようにした。 今回ロボットの移動はWait();を使うのを控えた。これは先に述べたように、電池の状態次第で移動時間を調節する必要があったからだ。そのため、untilを用いて移動時間を決めた。「黒線に当たるまで」というようにした。これにより、どんなスピードでも決まった距離を進める。
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); Wait(400); PlaySound(SOUND_CLICK);
スタート位置の黒線を横断。
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
次の黒線に当たるまで直進。
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); Wait(500); PlaySound(SOUND_CLICK);
黒線の枠を横断する。ここでロボットは枠内に半分ほど入る。
Off(OUT_BC);Wait(500);
OnFwd(OUT_B,speed); OnRev(OUT_C,speed); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
ロボットが黒線上にいる状態で方向転換(左旋回)する。この旋回を始めWait();を使い調節して動かそうとしたが、電池の消耗等の理由で安定しなくなかった。そこでここにもuntilを用いた。黒線に当たるまで旋回させると、ほぼ90度旋回に成功した。しかし、ここでも、枠内から少しでもはみ出ると、直角に曲がらなかったので、いかに枠内までロボットを移動させるかが課題だった。
Off(OUT_BC);Wait(700);
OnFwd(OUT_B,speed); OnRev(OUT_C,speed); Wait(180); PlaySound(SOUND_CLICK);
前の動作でほぼ旋回できているのでここでは微調整として少し旋回させ る。1秒もしない時間ならWait();を使っても誤差は生じなかった。
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); Wait(1100); PlaySound(SOUND_CLICK);
旋回が終わると、ロボットは黒線の枠上にいるので光センサーを再び使うために黒線から出るくらいの距離を前進する。ここをuntilにしよう(黒線上から白の部分まで前進)かと考えたが、必ずしもロボットが黒線上にいるとは限らないので使わなかった。
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); Wait(500); PlaySound(SOUND_CLICK);
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
Off(OUT_BC);Wait(500);
OnFwdSync(OUT_BC,speed,0); Wait(600); PlaySound(SOUND_CLICK);
Off(OUT_BC);Wait(500);
一度目に旋回した地点からすると、コップは黒線2本目のところにいる。そのため黒線まで直進、横断をどちらも2回ずつする。
OnFwd(OUT_B,speed); OnRev(OUT_C,speed); until(SENSOR_1 < black); PlaySound(SOUND_CLICK);
Off(OUT_BC);Wait(500);
前述と同じように、方向転換(90度)する。これでコップAの前まで移動できたことになる。
OnRevSync(OUT_BC,speed,0); Wait(2000);
旋回地点ではあまりにもコップとの距離が近いため、ここで一旦バックする。
OnFwdSync(OUT_BC,speed,0); until(SensorUS(S3) <= 12);
コップとの距離が12cm以下になるまで前進する。この12cmは別に練習プログラムを組んでベストな位置を測った。
Off(OUT_BC); Wait(100);
RemoteStartProgram(1,"z_slave.rxe");
親機からプログラム「z_slave.rxe」を子機に実行するように命令する。プログラムを指定して、子機にそれを起動させる通信方法はプログラミングする作業が簡単になるため、今回はその方法を採用した(数字を送る場合、子機はifを用いてそれぞれの数字ごとの行動をプログラムしなければならない)。
while(msg != 11){ ReceiveRemoteNumber(MAILBOX1,true,msg); } PlaySound(SOUND_CLICK);
子機から「11」というメッセージが来るまで待つ。このメッセージは、子機が親機の命令を終えたことを知らせるために送る。親機はMAILBOX1にメッセージを受け取り、それをmsgに置き換える。trueは受け取ったらMAILBOX1を空にすることを示している。この後にも、親機はメッセージを受け取るため、空にさせるようにした。初めは、untilを使って「11」が来るまで、というプログラムを用いた。しかしこれは上手くいかなかった。until()内の条件文が出来ていなかったためだと思われる。ここではwhile(msg != 11)を採用した。これはmsgが11でないならば受け取り続ける(待ち続ける)ことを示している。whileまたはuntilで繰り返さない時、メッセージを受け取らなくてもプログラムが進んでしまうようにした。
if(SENSOR_4 < A){ OnFwdSync(OUT_BC,speed,0); until(SensorUS(S3) <= 18); Off(OUT_BC); Wait(100); RemoteStartProgram(1,"z_slave-2.rxe");
while(msg != 12){ ReceiveRemoteNumber(MAILBOX1,true,msg); } PlaySound(SOUND_CLICK);
コップがAだった場合、コップ1との距離が18cm以下になるまで前進して、親機がプログラム「z_slave2.rxe」を子機に起動させる。子機からのメッセージを受け取り次第ピッと鳴らして終了。2個目以降は戻って掴んで認識、重ねるを繰り返すつもりだった。
}else if(SENSOR_4 < B){ OnFwdSync(OUT_BC,speed,0); Wait(2000); PlaySound(SOUND_CLICK); Off(OUT_BC);Wait(500); OnFwd(OUT_B,speed); OnRev(OUT_C,speed); until(SensorUS(S3) < 18); PlaySound(SOUND_CLICK); Off(OUT_BC); Wait(100); RemoteStartProgram(1,"z_slave-2.rxe");
while(msg != 12){ ReceiveRemoteNumber(MAILBOX1,true,msg); } PlaySound(SOUND_CLICK);
コップがBだった場合、超音波センサーでコップを認識するまで左に旋回、コップまで前進、以降はAの場合と同じ。
}else{ OnFwdSync(OUT_BC,speed,0); Wait(6000); OnFwd(OUT_B,speed); OnRev(OUT_C,speed); until(SensorUS(S3) < 30); PlaySound(SOUND_CLICK); Off(OUT_BC); Wait(100); OnFwdSync(OUT_BC,speed,0); until(SensorUS(S3) <= 18); Off(OUT_BC); Wait(100); RemoteStartProgram(1,"z_slave-2.rxe"); Off(OUT_BC); Wait(100); RemoteStartProgram(1,"z_slave-2.rxe"); while(msg != 12){ ReceiveRemoteNumber(MAILBOX1,true,msg); } PlaySound(SOUND_CLICK); } }
コップがCだった場合、コップ3の真横に来るように前進、右に旋回(90度)、コップまで前進、以降はAの場合と同じ。
task main() { PlaySound(SOUND_CLICK); OnFwd(OUT_A,70);Wait(2800);Off(OUT_A); OnRev(OUT_C,20);Wait(800);Off(OUT_C); OnRev(OUT_A,60);Wait(2800);Off(OUT_A); SendResponseNumber(MAILBOX1,11); Wait(1000); PlaySound(SOUND_CLICK); }
アームを下げて、コップを掴んでアームを上げ、親機にメッセージを送る。
task main() { PlaySound(SOUND_CLICK); OnFwd(OUT_A,60);Wait(900);Off(OUT_A); OnFwd(OUT_C,20);Wait(700);Off(OUT_C); OnRev(OUT_A,80);Wait(1800);Off(OUT_A); SendResponseNumber(MAILBOX1,12); Wait(1000); PlaySound(SOUND_CLICK); }
アームを下げ、コップを離してアームを上げ、親機にメッセージを送る。
今回私は、プログラムについてより一層、考えが深まった。今回の課題は、ただ単純に一連の動作をプログラムにするのではなく、ロボット自身にその状況を理解させる必要があった。そのためには、何通りもの動作を前もってプログラムする必要がある。センサーによる知覚をどうすればより正確に、安定してプログラムとかみ合わせることが出来るのか。それはロボット本体、ハードを変えるよりも、プログラムを変えることに重点を置くべきなのだと改めて感じた。