目次
今回の課題の目的は課題2で使用した紙(下の図)を使用し、ピンポン玉と紙コップを別々に回収して所定の場所に運ぶ。ただし、マジェンタの円に逆さまにした紙コップを置き、その中にピンポン玉を2つ つ入れておく。また、黄色い円に紙コップを通常の向きに置き、障害物とする。課題の基本得点の計算方法やルールは課題3(ロボコン)のところに見てください。
今回では2台のロボット(MasterとSlave)を作った。Masterの方はA地点からスタートし、左側の紙コップを取る目的とする。また、Slaveの方はD地点からスタートし、右側の紙コップを取る目的とする。詳しくはプログラムのところに説明する。2台のロボットは同じ構造になっているので1台だけの構造を説明する。ロボットの構造では上の図のように2つの部分(車体の部分とアームの部分)によって説明していく。
上の図のようにロボットの車体の部分は課題2のように作った。詳しくは課題2(tono)のところに見てください。まず、2つモータを左側と右側に付けてタイヤ―を回す。また、LineTraceを正確に動くため光センサーをつける場所はできるだけ床に近いである。ケーブル線を邪魔にならないように車体の中にしっかり絞っていた。
ロボットのアームでは今回の課題の一番重要なものである。ロボット全体のところのように1つのモータだけを使って紙コップを掴む動作や上げる動作をする。そのことは先生の指導と2016a/課題3(ngtrz)のものを参考にしてロボットのアームの部分を作った。また、紙コップとピンポン玉を探し取りにいくまでの動作をできるようにアームの下で超音波センサーを付けた。
紙コップを掴む構造をわかりやすく説明できるため、上の図のように番号の順番に沿って説明していく。ただし、前後・左右の方向については図の右下に従う。
モータが前の方向に回ると隣の黒い歯車も同様にまわる。そのため、歯車灰色の歯車が右の方向に回る。
灰色の歯車が右の方向に回ると歯車の真ん中の穴にあるウォームギアも右の方向に回転する。
ウォームギアが右の方向に回転すると両隣の灰色の歯車が回転する。(左側の歯車は左へ、右側の歯車は右へ)
この仕組みは長い黒い棒の部品を使って下の歯車に動きを伝える。上の図のように下の歯車は上の歯車に動きを使って紙コップを掴む
紙コップを掴んでから紙コップを持ち上げることを行う。上の左側の図のようにアームが完全に紙コップを掴むとモータの隣の黒い歯車(No.1)しか回らない。(No.2からNo.5の部品は回らないこと)そのため、上の右側の図のように紙コップが上げられるようになった。ただし、モータの回力が重力より大きくなければならない、そうしないと紙コップが落としてしまうからである。だから、プログラムのときこのことも考える必要がある。
ロボットのピンポン玉を運ぶ動作は最初に部品No.1とNo.2を垂直にした。その結果、ピンポン玉を運ぶことはできたがピンポン玉を目的に入れることはできなかった。だから、部品No.1とNo.2を上の図のように斜めをするとピンポン玉を目的に入れることはできるようになった。また、紙コップの高さが割り箸の高さより高いからと途中でピンポン玉が円の外に出さない。
超音波センサーは縦をつけた方がいいといわれた。しかし、今回の課題では私たちが超音波センサーを最後に付けた残ったスペースのせいでよこになってしまった。上の図のように改善前のときかなり後ろのところに付けたからセンサーがうまく働かなかった。そのため、改善後のとき付けるところをちょっと前にすると改善前よりセンサーがうまく働いた。
私たちのロボットの構造がかなり大きいのでせいでスタート線に越えてしまった。それはルール違反だから、上の図のようにスタンドをつける。このスタンドが動きときに邪魔になることはなかった。
上の図のように2つのロボット(LeftRobotとRightRobot)をコースの中で移動する。
今回の課題ではまだ課題2のコースを使っている。課題2の結果を見るとriko のプログラムがグループの中で一番優秀のでrikoの課題2のプログラムを利用した。Sub関数などはrikoのLineTraceと全く同じものを扱っていたから課題2(riko)または課題3(riko)のところを見てください。
先生が授業で配ったプリントと授業の参考資料の37ページを参考してプログラムを書いたが、void の代わりにsubを使った。
#define SLOW 30 // 速度を遅くして30にする。 const float diameter=5.45; // タイヤの直径(cm) const float tread=11.3; // タイヤのトレッド幅(cm) const float pi=3.1415; // 円周率 int d; // 整数dとおく sub fwdDist(float d) // 距離d cm 前進 { long angle=d/(diameter*pi)*360.0; // 必要なタイヤの回転角 RotateMotorEx(OUT_AB,SLOW,angle,0,true,true); } sub turnAng(long ang) // 距離ang度の時間回り旋回 { long angle=tread/diameter*ang; // 必要なタイヤの回転角 RotateMotorEx(OUT_AB,SLOW,angle,100,true,true); } int searchDirection(long ang) //現在の方向を中心にang度の範囲で探し、障害物までの距離を返す。 { long tacho_min; // もっとも近い距離を実現するタイヤの回転数 int d_min=300; // もっとも近い距離の仮の最小値(十分大きくとっておく) long angle=(tread/diameter)*ang; // 旋回角度からタイヤの回転の計算 turnAng(ang/2); // 指定された角度の半径の半分を旋回 ResetTachoCount(OUT_AB); // 角度計算をリセット OnFwdSync(OUT_AB,SLOW,-100); // 反時計回りに旋回 while(MotorTachoCount(OUT_A)<=angle){ if(SensorUS(S3)<d_min){ // 現在の距離が仮の最小値より小さい場合 d_min=SensorUS(S3); // 仮の最小値の更新 tacho_min=MotorTachoCount(OUT_A); // この時のタイヤの回転数を記録 } } OnFwdSyncEx(OUT_AB,SLOW,100,RESET_NONE); until(MotorTachoCount(OUT_A)<=tacho_min||SensorUS(S3)<=d_min); Wait(14); // 微調整 Off(OUT_AB); Wait(500); // 0.5秒に休止 return d_min; }
ピンポン玉がなかにある紙コップを取るプログラムは以下のようなものである:
sub catch_cop(long ang1) { ResetTachoCount(OUT_C); // モータCの回転角をリセットする RotateMotor(OUT_C,-SLOW,ang1); // モータCをSLOW%のスピードでang1度反転 }
紙コップを離すプログラムは以下のようなものである:
sub release_cop(long ang2) { ResetTachoCount(OUT_C); // モータCの回転角をリセットする RotateMotor(OUT_C,SLOW,ang2); // モータCをSLOW%のスピードでang1度前転 }
紙コップを持ち上げるときのプログラムは以下のようなものである
sub keep_cop() { ResetTachoCount(OUT_C); // モータCの回転角をリセットする RotateMotor(OUT_C,-SLOW,ang3); // モータCをSLOW%のスピードでang3度反転 OnFwd(OUT_C,-20); // モータCを後ろに20%のスピードをする }
紙コップを重ねるプログラムをわかりやすく説明できるため、上の図のように番号の順番に沿って説明していく。
Master側が紙コップを持って、Slave側はMaster側から離れる。次、超音波センサーを邪魔にならないようにSlave側一回アームを閉める。また、Master側も超音波センサーの邪魔にならないようにアームを持ち上げる。
Slave側が紙コップを掴む。Master側はアームを持ち上げる状態でSlave側に近づく。
Master側は紙コップをSlave側にかぶせる。Slave側はアームを開き、紙コップを離す。
Slave側が離れる。最後にMasterがアームを広げ、紙コップを離すと紙コップを重なることができる。
LeftRobot(Slave側)のプログラムは以下のようなものである。ただし、番号順番については紙コップを重ねる順番のところに見てください。また、counterのところはarsoが担当したので詳しくはarsoのコップ関連プログラムのところに見てください。
#define SIGNALON 11 // モータ回転のためのメッセージ #define SIGNALON2 12 // モータ停止のためのメッセージ sub tagforleft() { int msg,counter; // 受け取った値を格納する変数 ResetTachoCount(OUT_ABC); //タイヤとアームの回転をリセットする RotateMotor(OUT_C,SLOW,1080); // アームをSLOWスピードで1080度前転 RotateMotorEx(OUT_AB,-SLOW,360,0,true,true); //タイヤのスピードを同期させ360度後転 RotateMotor(OUT_C,-SLOW,1080); // アームをSLOWスピードで1080度後転 while(counter==0){ ////カウンターが0の間繰り返す(番号2の状態) ReceiveRemoteNumber(MAILBOX1,true,msg); if(msg==SIGNALON) { ResetTachoCount(OUT_ABC); RotateMotor(OUT_C,SLOW,1080); RotateMotorEx(OUT_AB,SLOW,120,0,true,true); RotateMotor(OUT_C,-SLOW,1080); counter++; //(番号3の状態) } }counter=0; //(番号4の状態) while(counter==0){ ReceiveRemoteNumber(MAILBOX1,true,msg); if(msg==SIGNALON2) { ResetTachoCount(OUT_ABC); RotateMotor(OUT_C,SLOW,360); RotateMotorEx(OUT_AB,SLOW,40,0,true,true); //(番号5の状態) counter++; } }counter=0; ResetTachoCount(OUT_ABC); RotateMotor(OUT_C,SLOW,720); //(番号6の状態) RotateMotorEx(OUT_AB,-SLOW,120,0,true,true); }
RightRobot(Master側)のプログラムは以下のようなものである。ただし、番号順番については紙コップを重ねる順番のところに見てください。
#define SIGNALON1 11 // モータ回転のためのメッセージ #define SIGNALON2 12 // モータ停止のためのメッセージ sub tagforright() { keep_cop(); // 番号2の状態 d=searchDirection(15); // Slave側のロボットを探す if(d>5.0){ // 距離が5cmより遠いとき SendRemoteNumber(CONN,MAILBOX1,SIGNALON1); // Slave側のMAILBOX1にモータ回転を送る Wait(5000); //5秒停止 fwdDist(d-8.0); // 近づいてd-8.0cm手前停止(番号3の状態) ResetTachoCount(OUT_ABC); //タイヤとアームの回転をリセットする RotateMotorEx(OUT_AB,-SLOW,40,0,true,true); //タイヤのスピードを同期させ40度前転 RotateMotor(OUT_C,SLOW,50); //アームをSLOWスピードで50度前転(番号4の状態) SendRemoteNumber(CONN,MAILBOX1,SIGNALON2); // Slave側のMAILBOX1にモータ停止を送る Wait(5000); //5秒停止 RotateMotor(OUT_C,SLOW,100); // アームをSLOWスピードで100度前転 ResetTachoCount(OUT_ABC); // タイヤとアームの回転をリセットする RotateMotor(OUT_C,SLOW,720); //アームをSLOWスピードで720度前転 RotateMotorEx(OUT_AB,-SLOW,120,0,true,true); //タイヤのスピードを同期させ120度 前転(番号5の状態) } }
LeftRobot の全体プログラムは以下のようなものである。ただし、移動順番についてはコース移動説明のところに見てください。
task main() { SetSensorLight(S2); // 光センサーの起動 SetSensorLowspeed(S3); // 超音波センサーセンサーの起動 Start_Set(); // Start Lineの移動 R_line(300,150); // Line Trace (番号1の移動) d=searchDirection(15); // 紙コップを探した後、 if(d>5.0){ // 距離が5cmより遠いとき catch_cop(700); // ここから紙コップを取る(番号2) OnFwd(OUT_AB,SLOW); // ここからはロボットをX地点まで移動する動作 Wait(1400); Off(OUT_AB); catch_cop(300); OnFwd(OUT_AB,-SLOW); Wait(100); Off(OUT_AB); // ここまではロボットをX地点の近くまで移動する動作 } ResetTachoCount(OUT_A); RotateMotor(OUT_A,SLOW,10); OnFwd(OUT_AB,SLOW); Wait(600); Off(OUT_AB); // ピンポン玉をX地点に入れる(番号3) catch_cop(40); // 紙コップを取ってX地点の外に出る OnFwd(OUT_C,-SLOW); // ここからはロボットを紙コップを重ねるところまで移動する動作 Wait(1000); Off(OUT_C); OnFwd(OUT_AB,-SLOW); Wait(1000); Off(OUT_AB); White_to_Black(33,35,15); Black_to_White(48,35,15); White_to_Black(33,40,25); Black_to_White(48,-30,0); White_to_Black(33,-30,0); Black_to_White(50,0,-30); White_to_Black(33,35,30); Black_to_White(48,35,30); // ここまではロボットを紙コップを重ねるところまで移動する動作 release_cop(50); // 1回目の紙コップを重ねる動作を行う tagforleft(); // 同上 keep_cop(); // 同上 tagforleft(); // 2回目の紙コップを重ねる動作を行う OnFwd(OUT_AB,-SLOW); Wait(1000); Off(OUT_AB); // LeftRobotの仕事終了 }
RightRobot の全体プログラムは以下のようなものである。ただし、移動順番についてはコース移動説明のところに見てください。
task main() { SetSensorLight(S2); // 光センサーの起動 SetSensorLowspeed(S3); // 超音波センサーセンサーの起動 reverse_Start_Set(); // Start Lineの移動(ロボットの後ろからスタート) L_line(1300,110); // Line Trace (番号1の移動) OnFwd(OUT_AB,-SLOW); // ここから番号2の移動 Wait(500); Off(OUT_AB); // ここまで番号2の移動 White_to_Black(43,20,25); // Line Trace (番号3の移動) White_to_Black(33,20,25); // 同上 ResetTachoCount(OUT_A); RotateMotor(OUT_A,-SLOW,150); d=searchDirection(15); // 紙コップを探した後、 if(d>5.0){ // 距離が5cmより遠いとき catch_cop(600); // ここで紙コップを取る(番号4) OnFwd(OUT_AB,SLOW); // ここからはロボットをX地点まで移動する動作 Wait(1000); Off(OUT_AB); catch_cop(500); OnFwd(OUT_AB,-SLOW); Wait(400); Off(OUT_AB); // ここまではロボットをX地点の近くまで移動する動作 } ResetTachoCount(OUT_B); RotateMotor(OUT_B,-SLOW,110); release_ball(); // ピンポン玉を目的に入れる(番号5) ResetTachoCount(OUT_A); RotateMotor(OUT_A,-SLOW,130); tagforright(); // 1回目の紙コップを重ねる動作を行う OnFwd(OUT_AB,-SLOW); // ここからは番号6の移動 Wait(1000); Off(OUT_AB); White_to_Black(48,35,25); L_line(300,130); Black_to_White(48,0,-30); White_to_Black(33,0,-30); // ここまでは番号6の移動 d=searchDirection(15); // 紙コップを探した後、 if(d>5.0){ // 近づいて5cm手前 catch_cop(600); // ここで紙コップを取る(番号6) OnFwd(OUT_AB,SLOW); // ここからはロボットをX地点まで移動する動作 Wait(1000); Off(OUT_AB); catch_cop(500); OnFwd(OUT_AB,-SLOW); Wait(400); Off(OUT_AB); // ここまではロボットをX地点の近くまで移動する動作 } White_to_Black(33,-30,-30); // ここからは番号7の移動 L_line(150,130); Black_to_White(48,30,30); // ここまでは番号7の移動 release_ball(); // ピンポン玉を目的に入れる(番号7) ResetTachoCount(OUT_A); RotateMotor(OUT_A,-SLOW,130); tagforright(); // 2回目の紙コップを重ねる動作を行う(すべての紙コップ持っている) ResetTachoCount(OUT_C); RotateMotor(OUT_C,-SLOW,1100); White_to_Black(33,-30,-30); // ここからはロボットをY地点まで移動する動作 Black_to_White(48,-30,-30); White_to_Black(33,-30,-30); OnFwd(OUT_AB,SLOW); // ここまではロボットをY地点まで移動する動作 Wait(300); Off(OUT_AB); ResetTachoCount(OUT_C); RotateMotor(OUT_C,SLOW,1200); // Y地点で紙コップを入れる動作 }
発表のとき、1回目LeftRobotが1個ピンポン玉をX地点に入れたので2点を取った。2回目はRightRobotが2個ピンポン玉をX地点に入れたが、地点の障害物のを動かしたから減点されて4点であった。 また、技術点としては平均8.8点となり、全体合計は12.8点。順位は9チーム中6位という結果になった。詳しくはロボコン結果のところに見てください。
最後に、この授業を取ってプログラム入門を触ることができた。プログラムの一ヶ所間違いたら起動できないことや人間はどうやってロボットを動かせるなどがわかった。わたしは今物理コースの学生からきっとロボティクスゼミでやったことを使う機会があると思う。
松本成司(2013)「自律型ロボットを作ろう!(2013年度版)」(マインドストームを使ったロボット製作の体験教室資料), [online] http://yakushi.shinshu-u.ac.jp/robotics-jr/robotics-jr-2013.pdf(参照2018-2-12).