#contents *課題2(ライントレース) [#x23a59f6] 第二回目の課題は、ライントレース(&ボール運び)ロボット作成。課題内容は[[こちら:http://yakushi.shinshu-u.ac.jp/robotics/?2015a%2FMission2]]を参照。 **自分のコース [#jf1949a7] 今回選択したコースはC→A。Cを出発、P・Q間のボールを確保、環状を一周、Aへ向かう、Aのゴールへシュート というルートになっている。 下の画像は、ルートを簡略に説明したgif画像。 #ref(mission2course001.gif) *ロボットについて [#f1479046] 下はロボットの全体写真。真横から撮影したものである。 #ref(./mission2_001.jpg,50%) 前輪にモーター2つ、後輪にはキャスターを用いた。また、ボールの移動にアームを用いた。以下、詳細に説明する。 **車体部分について [#kf654dea] 前輪のタイヤの間隔を出来る限り狭くするよう心掛けた。そのようにしたことで、私のコースではないがC直前のような急カーブでもスムーズに対応できるようになった。 また、モーターの間隔を狭くするにあたって、前後に余分な空間ができてしまったが、前方は光センサーの取り付け部分、後方はキャスターの補強部分となり、小型化する上で有用なものとなった。 #ref(./mission2_006.jpg,50%) 光センサーは、床から高さ5mm程度に設定している。あまり距離が出来てしまうと、周囲から光の影響を受け易くなり、至近距離では明暗の差が減少してしまうと考えたからである。 #ref(./mission2_005.jpg,50%) また、前回同様、キャスターの高さ調節には苦労した。垂直にならなければキャスターとして機能しないからである。だが、班の中で意見を出し合えたおかげで比較的時間は掛からずに制作することが出来た。 本体とキャスターの取り付け部分に余裕をもたせているのは、方向転換の際の回転を滑らかに行わせる為である。 **アームについて [#efccc724] 今回作成したアームは、モーターの回転によって上下するというシンプルな構造にしてある。 アームには、前にボールが転がらないようにするという役割を担わせる一方、進行を左右されないようにボールを持ち上げるようにした。 #ref(./mission2_003.jpg,50%) ピンポン玉のような軽いものでは大差はないかもしれないが、ボールで前に突っ張ってしまうような心配はなくなった。 ***アームのギア比 [#h462160c] アームが早く上下してしまうと、こちらの意図しない負荷が部品にかかってしまう恐れ(アームの上下動による部品の故障)があった。それを無くすためにもギア比調節による減速は不可欠であった。 #ref(./mission2_007.jpg,50%) 今回用いたギア比は、 +モーター(1)に歯数8の歯車をつけ、2本目の棒(2)の歯数40の歯車に噛ませる。(ギア比8:40なので速度1/5) +2本目の棒には歯数40の歯車と歯数24の歯車がついている。(ギア比は40:24なので速度3/5) +歯数24の歯車と、3本目の棒(3)の歯数40の歯車に噛ませる。(ギア比は24:40なので速度3/5) 3本目の棒にアームがついている。 よって、ギア比は(1/5)*(3/5)*(3/5)=(9/125)となる。モーターが14回転すると、アームのついている棒が1回転する。つまり、モーターが4回転するとアームが約100°(アームが上下する幅)回転することになる。 *プログラムについて [#n62cfe2b] プログラム全体を記載する。 #define THRESHOLD 47 //基準となるしきい値 #define HIPOWER 7 //直進時 #define LOWPOWER 4 //カーブトレース時 #define setpower_H SetPower(OUT_AC,HIPOWER); #define setpower_L SetPower(OUT_AC,LOWPOWER); #define go_forward setpower_H; OnFwd(OUT_AC); #define go_back(t) setpower_H; OnRev(OUT_AC); Wait(t); #define turn_left1 setpower_L; OnFwd(OUT_C); OnRev(OUT_A); #define turn_right1 setpower_L; OnFwd(OUT_A); OnRev(OUT_C); #define turn_left2 setpower_L; OnFwd(OUT_C); Off(OUT_A); #define turn_right2 setpower_L;OnFwd(OUT_A); Off(OUT_C); #define STEP 1 #define nMAX 7 //黒をカウントする最大値 #define short_break Off(OUT_AC); Wait(200); //交差点判断時停止 #define cross_time 30 #define armtime 50 //アームの上下時間 #define arm_up OnFwd(OUT_B); Wait(armtime); Off(OUT_B); #define arm_down OnRev(OUT_B); Wait(armtime); Off(OUT_B); sub cross_line() { //交差点横断 OnFwd(OUT_AC); Wait(cross_time); Off(OUT_AC); } sub shoot() { //ゴールへシュート arm_up; go_forward; Wait(50); go_back(50); } task main() { SetSensor(SENSOR_1, SENSOR_LIGHT); int lc=0; //交差点の数をカウント int nc=0; //黒に連続でなった回数をカウント while (lc < 5) { //ゴール手前交差点でループ解除 while (nc < nMAX) { if (SENSOR_1 < THRESHOLD -8) { //39未満は黒 turn_left1; //左折 nc++; } else { if (SENSOR_1 < THRESHOLD -4) { //39〜42は黒灰色 turn_left2; //左旋回 } else if (SENSOR_1 < THRESHOLD +4) { //43〜50は灰色 go_forward; //直進 } else if (SENSOR_1 < THRESHOLD +8) { //51〜54は白灰色 turn_right2; //右旋回 } else { //55以上は白 turn_right1; //右折 } nc=0; //カウンタをリセット } Wait(STEP); } short_break; //交差点時 turn_right1; Wait(nMAX + 14); cross_line(); nc=0; lc++; //通過した交差点をカウント if (lc == 2) { //2つ目の交差点での動作 arm_down; //ボールを固定 go_back(20); turn_left1; Wait(55); Off(OUT_AC); } if (lc == 3) { //3つ目の交差点での動作 go_back(20); turn_left1; Wait(40); Off(OUT_AC); } } go_back(50); //ゴール手前交差点での動作 Off(OUT_AC); shoot(); Off(OUT_AC); } **プログラミングについて [#mf1882b0] 今回のプログラミングでは、3段階に分けてプログラムを組み立てていった。 +スムーズなトレースが出来るようにすること。 +交差点判断を明確にし、判断後の動作を調節すること。 +アームを使用し、今回の課題に添わせる内容にすること。 最初に上記のプログラムにあるライントレースの部分だけを完成させた。ラインをトレースさせることだけで試走させ、形になったところで、交差点動作をループの中に追加させた。 スタートからゴール手前の交差点まで一連のトレースが可能になったとき、ボール確保とシュートのプログラムを実行するように組み込んだ。 ***交差点判断について [#cc01eadd] 連続で黒と判断された回数をカウントすることによって、交差点を判断させた。7回連続で黒となった場合は、カーブではなく交差点であるという指示である。 また、交差点横断だけでは不十分な部分を、交差点に差し掛かった回数をカウントすることによって、その交差点に見合った条件を付け加えるようにした。その交差点時動作に付随する形でアームの挙動を設定し、今回の課題(ボールをゴールへシュート)を遂行した。 **トレースの精度を向上させる [#ua335086] スムーズなトレースをさせるために、黒と白の境界(灰色)を基準とした5段階判定(黒・黒灰色・灰色・白灰色・白)を用いた。内側に入りすぎ(黒)の時、または外側に出過ぎ(白)の時は、右左折する(直角に曲がる)ようにした。黒と灰色の中間(黒灰色)、白と灰色の中間(白灰色)は、右左旋回する(円を描くように緩やかに曲がる)ようにプログラミングを行った。 黒灰色・白灰色は共に光度差4を範囲としてあるが、灰色は光度差8を範囲としてある。直進(go_forward)の判定幅を多めにとることによって、速度の向上に繋げた。 setpowerを用いているのは、直進時とカーブ時に緩急をつけるためである。カーブ時のトレースがより正確になり、交差点付近等でのタイヤによる滑りが軽減される。ギア比での速度調整では、必然的に速度の遅い方に合わせる必要が出てくる。それでは直線時の移動速度に無駄が出来てしまう為、setpowerを用いる必要があった。また、前回の課題でsetpower3以下は好ましくないとわかっているため、低速時のsetpowerを4にしている。 **実際の映像 [#e3729495] 上記のプログラムを用いて実際に動作させた時の動画。 #ref(shisou03.mp4) *製作時間 [#l8b70f08] ロボット本体製作・・・4時間 プログラミング(試走行含む)・・・5時間 *今回の課題を通じての反省 [#e01af27f] 今回は、前回の書道ロボットに比べ、かなりスムーズに完成したと思う。ロボットのイメージがより具体的になってきたということと、パーツを覚え始めたということが時間短縮に繋がっていったのだと考える。しかし、プログラミングの理解力をもっと高める必要があると感じる場面があった。ifの入れ子構造の繋がりを理解することに時間がかかった上、確認作業にも手間取った。取り敢えず作り始める、というスタンスではなく、プログラムのイメージを一度書き起こしてから、プログラミングに取り掛かっていくべきだったと感じた。最終課題では、課題1、課題2で得た知識や反省を活かしながら取り組めるよう心掛けたい。 ※課題2・・・2015/07/16 (木) 最終更新 *ロボットコンテスト [#fb517c2d] ※課題2のページに追加更新という形をとる。 **ロボットコンテスト概要 [#v4fb8b34] ロボティクス入門ゼミ最終週(2015年7月31日)にロボティクス入門ゼミの集大成となるロボットコンテストが行われた。以後文中表記の際はロボットコンテストをロボコンと略称を用いる。 ロボットコンテストの詳細は[[こちら:http://yakushi.shinshu-u.ac.jp/robotics/?2015a%2F%A5%ED%A5%DC%A5%B3%A5%F3]]を参照。 **ロボットについて [#kcadc6b7] #ref(./robokon.jpg,50%) ロボコンのロボット本体の基本構造は上記の課題2で説明した構造同様である。 ロボコン用のロボットは、AB共にアームと光センサーの位置を除くほぼ全てのパーツ配置を同じにしてある(不足パーツは他のものを代用している)。プログラムの際に同じような数値を入れるためである。そのような構造にすることで、プログラミングの時間短縮を図った。 課題2のロボットは上下に動くアームでボール持ち上げ移動させていたが、ロボコンでは、ボールの受け渡し動作があるため、タイムロスが大きい上にパスの安定性に欠ける。今回のロボコン内容に沿わせるため、アームの部分だけを変更し、左右に開くアームを用いることにした。 アームが左右に開く場合、同じ高さでは互いにぶつかり合うため、上手くパスが通らない。そこで、アームの位置を上下にずらすことによって、アーム同士の干渉自体を避けることにした。また、2台ともボールを左右から同時に挟むようになっているため、パスの際生じる多少の位置ずれを補えるようになっている。 また、AとBのロボットのアームパーツ(黄色のボールを挟んでいるパーツ)のつけ方を逆にした。Aは本体から少し遠めの位置にボールを、Bは本体から近い位置にボールを固定することによって、ABのアームが双方の本体に接触せずにボールの受け渡しが可能になった。 **プログラム [#j5e42927] ***ロボットAのプログラム [#y80aeb9f] #define THRESHOLD 40 //基準となるしきい値 #define HIPOWER 7 #define LOWPOWER 4 #define setpower_H SetPower(OUT_AC,HIPOWER); #define setpower_L SetPower(OUT_AC,LOWPOWER); #define go_forward setpower_H; OnFwd(OUT_AC); #define go_back(t) setpower_H; OnRev(OUT_AC); Wait(t); #define turn_left1 setpower_L; OnFwd(OUT_C); OnRev(OUT_A); #define turn_right1 setpower_L; OnFwd(OUT_A); OnRev(OUT_C); #define turn_left2 setpower_L; OnFwd(OUT_C); Off(OUT_A); #define turn_right2 setpower_L; OnFwd(OUT_A); Off(OUT_C); #define STEP 1 #define nMAX 6 //黒をカウントする最大値 #define short_break Off(OUT_AC); Wait(200); #define cross_time 33 //交差点横断時間 #define armtime 15 //アーム開閉時間 #define arm_open setpower_L; OnFwd(OUT_B); Wait(armtime); Off(OUT_B); #define arm_close setpower_L; OnRev(OUT_B); Wait(armtime); Off(OUT_B); #define signal_go 25 #define first_ball 150 sub cross_line() { //交差点横断 OnFwd(OUT_AC); Wait(cross_time); Off(OUT_AC); } task main() { SetSensor(SENSOR_1, SENSOR_LIGHT); int lc=0; int nc=0; ClearTimer(0); ClearMessage(); while(FastTimer(0) < first_ball) { //Aゾーンからボールを掴むまでのライントレース if (SENSOR_1 < THRESHOLD -7) { turn_left1; } else { if (SENSOR_1 < THRESHOLD -3) { turn_left2; } else if (SENSOR_1 < THRESHOLD +3) { go_forward; } else if (SENSOR_1 < THRESHOLD +7) { turn_right2; } else { turn_right1; } } Wait(STEP); } arm_close(); while (lc < 3) { //Bゾーンまで向かうライントレース while (nc < nMAX) { if (SENSOR_1 < THRESHOLD -7) { turn_left1; nc++; } else { if (SENSOR_1 < THRESHOLD -3) { turn_left2; } else if (SENSOR_1 < THRESHOLD +3) { go_forward; } else if (SENSOR_1 < THRESHOLD +7) { turn_right2; } else { turn_right1; } nc=0; } Wait(STEP); } PlaySound(SOUND_CLICK); //交差点識別時のサウンド lc++; if (lc == 1) { //交差点一回目の動作 turn_right1; Wait(nMAX + 15); cross_line(); nc=0; } if (lc == 2) { //交差点二回目の動作 turn_right1; Wait(nMAX + 22); cross_line(); turn_right1; Wait(10); Off(OUT_AC); nc=0; } } nc=0; turn_right1; //Bゾーン手前の直角 Wait(nMAX + 14); Off(OUT_AC); SendMessage(signal_go); //ロボットB始動命令 Wait(40); arm_open(); //ロボットBにボール受け渡し完了 go_back(65); //ロボットAがCへ向かう turn_right1; Wait(50); go_forward; Wait(110); Off(OUT_AC); go_forward; until (SENSOR_1 < THRESHOLD -7); //ラインまで直進 turn_right1; Wait(50); Off(OUT_AC); if (true) { //Cゾーンまでトレース while (nc < nMAX) { if (SENSOR_1 < THRESHOLD -7) { turn_right1; nc++; } else { if (SENSOR_1 < THRESHOLD -3) { turn_right2; } else if (SENSOR_1 < THRESHOLD +3) { go_forward; } else if (SENSOR_1 < THRESHOLD +7) { turn_left2; } else { turn_left1; } nc=0; } Wait(STEP); } PlaySound(SOUND_CLICK); //交差点識別時のサウンド turn_left1; Wait(nMAX + 14); cross_line(); } nc=0; go_forward; //Cゾーンに入る Wait(75); turn_right1; //ボール受け取りのための位置修正 Wait(110); Off(OUT_AC); PlaySound(SOUND_DOWN); //ボール受け取り準備完了 until (Message() ==signal_go); //ロボットBより再始動命令 PlaySound(SOUND_UP); //ロボットA再始動 arm_close(); //ボール受け取り Wait(250); go_forward; //Cゾーンから離脱 Wait(120); int bc=0; while (bc < 3) { //CゾーンからAへ向かうライントレース while (nc < nMAX) { if (SENSOR_1 < THRESHOLD -7) { turn_left1; nc++; } else { if (SENSOR_1 < THRESHOLD -3) { turn_left2; } else if (SENSOR_1 < THRESHOLD +3) { go_forward; } else if (SENSOR_1 < THRESHOLD +7) { turn_right2; } else { turn_right1; } nc=0; } Wait(STEP); } PlaySound(SOUND_CLICK); //交差点認識時のサウンド turn_right1; Wait(nMAX + 14); cross_line(); nc=0; bc++; if (bc == 2) { //交差点二回目の追加動作 turn_right1; Wait(60); Off(OUT_AC); } } go_forward; //Aゾーンに帰還 Wait(60); turn_right1; //静止の方向修正 Wait(130); Off(OUT_AC); PlaySound(SOUND_DOWN); //ロボットA、Mission遂行完了 } ***ロボットBのプログラム [#ra19993f] #define THRESHOLD 44 //基準となるしきい値 #define HIPOWER 7 #define LOWPOWER 4 #define setpower_H SetPower(OUT_AC,HIPOWER); #define setpower_L SetPower(OUT_AC,LOWPOWER); #define go_forward setpower_H; OnFwd(OUT_AC); #define go_back(t) setpower_H; OnRev(OUT_AC); Wait(t); #define turn_left1 setpower_L; OnFwd(OUT_C); OnRev(OUT_A); #define turn_right1 setpower_L; OnFwd(OUT_A); OnRev(OUT_C); #define turn_left2 setpower_L; OnFwd(OUT_C); Off(OUT_A); #define turn_right2 setpower_L; OnFwd(OUT_A); Off(OUT_C); #define STEP 1 #define nMAX 5 //黒をカウントする最大値 #define short_break Off(OUT_AC); Wait(200); #define cross_time 40 //交差点横断時間 #define armtime 15 //アーム開閉時間 #define arm_open setpower_L; OnFwd(OUT_B); Wait(armtime); Off(OUT_B); #define arm_close setpower_L; OnRev(OUT_B); Wait(armtime); Off(OUT_B); #define signal_go 25 sub cross_line() { //交差点横断 OnFwd(OUT_AC); Wait(cross_time); Off(OUT_AC); } task main() { SetSensor(SENSOR_1, SENSOR_LIGHT); int lc=0; int nc=0; ClearMessage(); until (Message() == signal_go); //ロボットAがBゾーンに来るまで待機 PlaySound(SOUND_UP); //ロボットB始動 arm_close; //ボール受け取り Wait(250); go_forward; //Bゾーン離脱 Wait(140); Off(OUT_AC); while (lc < 4) { //BゾーンからCゾーンへ向かうライントレース while (nc < nMAX) { if (SENSOR_1 < THRESHOLD -7) { turn_right1; nc++; } else { if (SENSOR_1 < THRESHOLD -3) { turn_right2; } else if (SENSOR_1 < THRESHOLD +3) { go_forward; } else if (SENSOR_1 < THRESHOLD +7) { turn_left2; } else { turn_left1; } nc=0; } Wait(STEP); } PlaySound(SOUND_CLICK); //交差点認識時のサウンド lc++; if (lc == 1) { //交差点一回目の動作 turn_left1; Wait(nMAX + 14); cross_line(); nc=0; } if (lc == 2) { //交差点二回目の動作 turn_right2; Wait(25); turn_right1; Wait(35); Off(OUT_AC); nc=0; } if (lc ==3) { //交差点三回目の動作 turn_left1; Wait(nMAX + 14); cross_line(); nc=0; } } turn_left1; //Cゾーン手前の直角 Wait(nMAX + 20); Off(OUT_AC); SendMessage(signal_go); //ロボットA再始動命令 Wait(10); arm_open; //ボール受け渡し完了 go_back(80); //ライントレースのコース上から離脱 turn_right1; Wait(60); go_forward; Wait(200); Off(OUT_AC); PlaySound(SOUND_DOWN); //ロボットB、Mission遂行完了 }