ボール回収の後に所定の位置に運ぶ、という課題だ
前回の課題2にボールを持ち上げる動作を追加したものである
プログラムもロボット制作も技量が足りないためボールを一つだけでも完璧に運ぶことを目標とした。よってロボットをニコイチにして確実にボールを持ち上げるアームを作成する。また、課題2のライントレースプログラムを活かす目的で以前使用したロボットに近い構造で足回りを整えた。運ぶボールはL'のボールである。
M'からスタートする。交差点を渡りL'のボールをつかむ。その場で回転して容器の上までアームを持ってくる。その場でアームを開く。
という計画を立てた
SLAVE側のアームを完全に降ろしたときにアームの先が地面すれすれの位置になるように高さを調節する。光センサーはできるだけアームの邪魔をしないように前輪車軸近辺に取り付けた。
アームの上下には多大な負荷がかかると予想されたのでモーターを二つ用いてパワーをあげた
高さが必要だったためバッテリーボックスを二段重ねて置いてある。下のマスター側のボタンを押すために無理やり四方を棒で固定して上のボックスとのあいだに空間を設けた
ただしディスレイが全く見えなかったので準備に手間取ることとなった
左:上から見たアーム 右:正面から見たアーム
このアームはモーターの回転をギアで垂直にしてある。これによりアームのロボット本体への固定が容易になる。
アームの先についているパーツはボールが落ちないように補助する役割を持っている。
基本的にはMission2のものと変わりないため、詳細な説明はMission2を参照
(http://yakushi.shinshu-u.ac.jp/robotics/?2019a%2FMember%2FHinata%2FMission2)
#define Baced 45 //中間となる明るさの定義 #define speed 40 #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL); #define Deep_turn_R OnRL(-speed,speed); //Deepで両車輪を回し、深く曲がる #define turn_R OnRL(0,speed); //片方の車輪のみを動かす #define Straight OnRL(speed,speed); //直進 #define Deep_turn_L OnRL(speed,-speed); #define turn_L OnRL(speed,0); #define turning_R OnRL(-speed,speeed);; //時計回りに回転 #define turning_L OnRL(speed,-speed); //反時計回りに回転 #define cross_line OnFwd(OUT_BC,15);Wait(400); //交差点を横切る
上記のものはすべて課題2で使用している
重量の増加に伴い足回りのパワーは40とした
#define CONN 1 //SLAVE (アーム)を表す #define meireiA 11 //これらは命令の番号を指定する #define meireiB 12 #define meireiC 13
SLAVEを動かすために追加したもので、これらの命令の内容は受信側(SLAVE)と統一する必要がある
サブルーチンは完全に課題2の物を使用した。マクロで一括してパワーを変更できるプログラムにすると細かな調整が利く
void R_trace(int Z){ //道の右側をトレースして交差点で停止する SetSensorLight(S1); long t0; t0=CurrentTick(); //タイマーをセット while (CurrentTick()-t0<Z) { //Z/1000秒以上連続して右に曲がるまで永続する if (SENSOR_1 < Baced-15) { //基準の明るさからー15の時の動作(黒線の上) Deep_turn_R; } else if (SENSOR_1 < Baced-5) {turn_R; //基準の明るさからー5のとき t0=CurrentTick();} else if (SENSOR_1 < Baced+5) {Straight; t0=CurrentTick();} else if (SENSOR_1 < Baced+10) {turn_L; t0=CurrentTick();} else {Deep_turn_L; t0=CurrentTick();} } turning_L; //左に旋回 Wait(400); cross_line; //直進して交差点を渡る }
右側をトレースして交差点をわたるサブ関数
Z/1000秒以上連続して右に曲がるまで続けると交差点と判断する。
void L_trace_A(int X){ //上記のR_traceの左バージョン SetSensorLight(S1); long t0; t0=CurrentTick(); while (CurrentTick()-t0<X) { if (SENSOR_1 < Baced-15) { Deep_turn_L; } else if (SENSOR_1 < Baced-5) {turn_L; t0=CurrentTick();} else if (SENSOR_1 < Baced+5) {Straight; t0=CurrentTick();} else if (SENSOR_1 < Baced+10) {turn_R; t0=CurrentTick();} else {Deep_turn_R; t0=CurrentTick();} } turning_R; //右旋回 Wait(400); cross_line; //交差点の横断 }
こちらはR_traceと違い左側をトレースしながら移動するようになっている
連続して左に曲がり続ける(黒が続く)と交差点と判断される
基本となる左側トレース。
void L_trace_B(int Y){ //左側トレース第二弾 SetSensorLight(S1); long t0; t0=CurrentTick(); while (CurrentTick()-t0<Y) { if (SENSOR_1 < Baced-15) {Deep_turn_R; t0=CurrentTick();} else if (SENSOR_1 < Baced-5) {turn_L; t0=CurrentTick();} else if (SENSOR_1 < Baced+5) {Straight; t0=CurrentTick();} else if (SENSOR_1 < Baced+10) {turn_R; t0=CurrentTick();} else {Deep_turn_R; //上記のものと違い、白を感知して右に連続して曲がり続けたときに止まるようになっている } } turning_L; //左旋回 Wait(200); }
task main() { int msg; //受け取った値を格納する変数とする L_trace_A(300); L_trace_B(200); SendRemorteNumber(CONN,MAILBOX1,meireiA); //トレースの後にSLAVEのメールボックスに命令Aを送信 While(true) { ReceiveRemorteNumber(CONN,MAILBOX1,true,msg); //命令Aの後にMAILBOX1の値を受け取りmsgに格納する //trueでメールボックスを空にする if(msg==meireiB) //命令Bがmsgにあれば以下の内容を実行 { turning_L; Wait(1500); Off(OUT_BC); SendRemorteNumber(CONN,MAILBOX1,meireiC); 命令Cを送信する } } }
#define speed 15 #define OnE(speed) OnFwd(OUT_B,speedE) //アームの開閉のために使用する #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL) //二つのモーターでアームを下げさせるのに使用 #define arm_up OnRev(OUT_B,24); //アームを持ち上げるのにはパワーが必要だったため、ここでけパワーを独立させた #define arm_down OnRL(speed,speed); #define arm_open OnE(speed); #define arm_close OnE(-speed); #define meireiA 11 //マスターと統一した命令 #define meireiB 12 #define meireiC 13
あまり動くスピードが速すぎてもボールが不安定になるのでアームの上昇をのぞいてパワーは低めに抑えた
void catch() { arm_down; //モーターACでモーターB(アーム)を可動させる Wait(950); arm_close; //モーターBがアーム Wait(2000); Off(OUT_B); arm_up; Wait(550); Off(OUT_AC); }
アームを下ろしてボールをつかんだ後に保持したままアームを上げるプログラム
void hanase() { arm_open; Wait(1200); Off(OUT_B); }
単純かつ単調なため説明は省略した
アームは開始時に大きく開けて、かつ最大まで上げておく
これにより走行に影響を与えず行動することが可能となる上に開始条件をほぼ一致させることが可能となる
task main { int msg; //// 受け取った値を格納する変数を作成 while(true) { ReceiveRemorteNumber(CONN,MAILBOX,true,msg); //msgに受け取った命令を格納してメールボックスを空にする if(msg==meireiA) //命令Aを受け取ったなら下記の内容を実行
{
catch(); SendRemorteNumber(CONN,MAILBOX1,meireiB); //行動を実行したのちに作業終了を告げる命令Bをマスターに送信する } if(msg==meireiC) //命令Cを受け取ったなら下記の内容を実行 { hanase; } } }
実験した教室では非常に正確に軽快に動作した。だが発表する教室が明るかったのか「黒を認識して曲がり続けると交差点と判断する」部分がうまく作動せず失敗した
図は失敗したK'の交差点である
色のついたラインはセンサーの位置を表している
理想は青のラインのようにキツく曲がって交差点と判断することだが、実際は赤のラインを走った。
これは黒線が明るく照らされた結果、濃い黒が薄い黒として判断されたのが原因であると考えられる。
薄い黒と判断すると片輪のみ動く仕様のため、あっさり線を越えてしまい次のサブルーチンに移行しなかった
時間との闘いの末にようやく完成したロボットとプログラムだったが、あえなく撃沈してしまい非常に空しくなった。反省と悔いの残る結果だった。