#contents *1.課題 [#hc472796] 課題はピンポン玉とカップを動かし、別々の場所に置くことができるロボットを作ることだ。 #ref(2017b/Member/ats/Mission3/コース.JPG,コース全体) 上の図のようにピンポン玉とカップが配置されている。X地点には割りばしで正方形の枠を作り、置いておく。マゼンダ色の場所に玉の入ったカップを、黄色の場所に障害物としてのカップを置く。ロボットはAかD地点に配置する。ロボットは玉の入ったカップを取り、ピンポン玉をX地点の正方形の枠内に入れ、カップをY地点の黒線に囲まれた部分に入れなければならない。カップを重ねることができれば、さらに加点となる。障害物となっているカップをロボットが動かすと、減点される。 *2.ロボット [#a012d606] **ロボット全体 [#yb3111b9] カップとピンポン玉を分けつつ動かせるようなロボットを1台作ることを方針にして作業を進めた。2台に分けた場合と比べてロボットが大きくなるため、障害物に当たったり、ライントレースに支障がでたりしないようコンパクトにすることが求められた。 #ref(2017b/Member/ats/Mission3/ロボット全体.JPG,ロボット全体) 上の写真のように2つのロボット本体を横にして縦に重ねた。(下の方のロボット本体が見えにくいかもしれませんが...)こうすることで、車体の幅を小さくすることができた。また、アーム(カップを掴む機構)を上下に動かせるようにした。アームを上げておけば障害物に当らないようになっている。 左右の前輪はモーターで動かせるようにして、後ろには左右両方にキャスターを2つ付けた。 ロボットの大まかな動きは下のようになる。 1.カップを探し、アームで掴む。掴んだ後にカップを少しだけ上にあげておく。(この時にピンポン玉が出ない程度に上げることが大切だ。)カップを少し上げておけば、中身のピンポン玉をばらさずに割りばしの枠内に入れることができる。割りばしの上を通過するときに下の図のようになる。 #ref(2017b/Member/ats/Mission3/カップ移動.JPG,70%,カップ、割りばし通過) 2.カップをX地点の枠内まで持ってきて、アームを完全に上げる。 3.カップをY地点の枠内で降ろして離す。 4.2個目以降のときはY地点でセンサーを働かせて1個目のカップの位置を把握してその位置まで行き、カップを離す。 **アーム(カップを掴む機構)[#t91fb2a7] アームには2つのモーターを使った。1つを「つかむ」動作に、もう1つを「上げ下げする」動作に用いた。このため、下の写真のように、アームは「つかむ」動作と「上げ下げする」動作の2種類を別々に行うことができる。 #ref(2017b/Member/ats/Mission3/アーム.JPG,アーム可動方向) ***つかむ機構 [#x88d9d77] つかむ部分の構造はクワガタ方式を使った。歯車を4つ用いて、アームの先が開いたり閉まるような仕組みにした。下の写真はクワガタ方式の仕組みを説明するもの。 #ref(2017b/Member/ats/Mission3/歯車、ハサミ.JPG,クワガタ式の仕組み) ↑この時は手が開く方向に歯車が回っているが、モーターを逆回転させれば手が閉じる。 下の写真のように4つのタイヤを使って確実にカップを固定できるようにした。また、カップが少し離れていてもアームの手を締める動作をする際にタイヤが回転することで、カップが自然に内側に入ってくるようになっている。 ↓左がきつく締める前、右がきつく締めた後のもの。 &ref(2017b/Member/ats/Mission3/つかむ動作前.jpg,left,50%,つかむ動作前); &ref(2017b/Member/ats/Mission3/つかむ動作後.jpg,right,50%,つかむ動作後); ***アームの補強 [#wcdc4fb6] 腕の先に重いモーターが付いている構造となっているため、上げ下げの支点となる部分や腕の部分に強い負荷がかかる。この負荷が一点に集中しないようにするために、複数の棒を使ってアームを支えるようにした。この結果、アームの動作の安定性を高めることもできた。(下の写真のように支える部分の部品を増やした。) #ref(2017b/Member/ats/Mission3/アーム補強.JPG,アームを支える部分の補強) **センサー [#ce6a61eb] ライントレース用の光センサーとカップ探査用の距離センサーの2つを使った。光センサーは課題2の時の経験を生かして、高さに気をつけて装着した。(低すぎると誤作動が多くなる。)距離センサーはその大きさのため、装着が難しかった。アームを降ろしたとき、ロボットの前方の空間がとても狭くなってしまうからだ。下の写真のように最終的に、クワガタ構造のハサミの真ん中に取り付けることができた。 #ref(2017b/Member/ats/Mission3/センサー.JPG,センサー位置) *3.プログラム [#o29f8dd7] プログラムを考えるうえで重要になってくることは以下の2点だ。 1.ピンポン玉の入ったカップや障害物をなるべくずらさないようにロボットが走行すること。今回のロボットはコンパクトを目指して作成したが、それでもロボット2台を積んでいて車体が大きいから、接触しないように気を付けなければならない。 2.黒線を上手く利用してロボットが確実に走行できること。(〇cm移動,〇cm回転などを繰り返すと誤差でロボットが迷子になる可能性がある。) それを考慮に入れたルートが下の図の赤の矢印のようになる。(開始直後のルートだけを記載した) #ref(2017b/Member/ats/Mission3/ルートと番号.JPG,ルートと番号) 玉の入ったカップに上の図のように番号を付けた。(青背景の吹き出しで書いた。)まず、Aを出てBで交差点を認識したらカップ探査のプログラムを起動して1番のカップを取りに行く。そのとき進んだ距離だけ後退してBに戻る。次にPの方向を向き、ライントレースしていく。Pで交差点を認識したらQ-Sまで直進する。ここで前進する前に機体を線に対して左右どちらかに傾けることで直進した後の位置を把握することが容易になる。また、カップを少しあげてあるため割りばしに干渉せず運搬することができる。次にS地点でUターンし、X地点まで戻ってきてカップを上にあげる。その後Y地点へ行きカップを降ろす。そして2番、3番のカップの作業に続く。 この先は計画で終わってしまったため、ここには1番のカップを置くところまでしか記述しなかったた。この後はQ-S間からあまり離れずに作業ができるという計画だった。2番と3番のカップはロボットがQ-S間から距離センサーを作動させて取りに行けると考えたからだ。 ロボット本体の役割の分担が以下のようになる。 マスター;ライントレース、カップ探査など スレーブ;カップの上げ下げ、アームの手の開け閉めなど **ライントレース [#i9e38461] ロボットが走行などをするときのために止まるプログラムを書いた。 #define adjust Off(OUT_AB);Wait(50); //とまる ライントレースに必要なマクロが以下のようになる。 #define sensor_light SENSOR_2 //センサーの定義 #define black 30 //明るさのおおよその最低値 #define white 60 //明るさのおおよその最大値 #define threshold (black+white)*0.5 //明るさの中央付近の値 #define deflection (sensor_light-threshold) //比例制御のための式 ライントレースのためのサブルーチンが以下のようになる。 sub linetrase_right(int x) //右側のライントレース用の速度の式 { OnFwd(OUT_A,40+deflection*x); OnFwd(OUT_B,40-deflection*x); } sub linetrase_left(int x) //左側のライントレース用の速度の式 { OnFwd(OUT_A,40-deflection*x); OnFwd(OUT_B,40+deflection*x); } ライントレースを実行するサブルーチンが以下のようになる。このプログラムは黒線を一定時間認識し続けると交差点と判断して止まるものである。ライントレースは3段階に場合分けされていて、どれも明るさによって速度が変わる比例制御によって走行する。 sub intertify_right() //交差点識別、右側ライントレース { long t = CurrentTick(); while(CurrentTick()-t<100){ //0.1秒黒を認識し続けると止まる if(sensor_light>white-5){ linetrase_right(1.5); t = CurrentTick(); }else if(sensor_light>black+5){ linetrase_right(1.5); t = CurrentTick(); }else{ linetrase_right(1.5); } } adjust; } sub intertify_left() //交差点識別、左側ライントレース { long t = CurrentTick(); while(CurrentTick()-t<100){ //0.1秒黒を認識し続けると止まる if(sensor_light>white-5){ linetrase_left(1.5); t = CurrentTick(); }else if(sensor_light>black+5){ linetrase_left(1.5); t = CurrentTick(); }else{ linetrase_left(1.5); } } adjust; } 開始地点から出るためのサブルーチンが以下のようになる。 sub escape_A() //Aから抜け出す { while(sensor_light<white-5){ OnFwd(OUT_B,50); OnFwd(OUT_A,30); } adjust; } **コップの探査など [#e7826e9f] コップ探査に使った定義が以下のようになる。 #define ultrasonic SensorUS(S1) //超音波センサー const float diameter = 5.45; //タイヤの直径 const float track = 17.5; //タイヤのトレッド幅 const float pi = 3.1415; //円周率 コップ探査に必要な関数やそれに少し変更を加えた関数が以下のようになる。具体的にはdcm前進するための関数に対してdcm後退するものを、ang度時計回りに回る関数に対してand度反時計回りに回るものを書いた。 void approach(float d) //距離dcm前進 { long angle = d/(diameter*pi)*360.0; //必要なタイヤの回転数 RotateMotorEx(OUT_AB,50,angle,0,true,true); adjust; } void back(float d) //距離dcm後退 { long angle = d/(diameter*pi)*360.0; //必要なタイヤの回転数 RotateMotorEx(OUT_AB,-50,angle,0,true,true); adjust; } void spinAng(long ang) //角度ang度の旋回(時計回り) { long angle = track/diameter*ang; //必要なタイヤの回転数 RotateMotorEx(OUT_AB,50,angle,100,true,true); adjust; } void spinAng_opposite(long ang) //角度ang度の旋回(反時計回り) { long angle = track/diameter*ang; //必要なタイヤの回転数 RotateMotorEx(OUT_AB,-50,angle,100,true,true); adjust; } 以下は参考資料に乗っていた関数。 int search_distance(long ang) //現在の方向を中心にang度の範囲で探し物体までの距離を返す { long tacho_min; //最も近い距離を表現するタイヤの回転数 int d_min = 300; //最も近い距離の仮の最小値 long angle = (track/diameter)*ang; //旋回角度からタイヤの回転角を計算 spinAng(ang/2); //指定角度の半分旋回(時計回り) ResetTachoCount(OUT_AB); //角度計算をリセット OnFwdSync(OUT_AB,50,-100); while(MotorTachoCount(OUT_A)<=angle){ if(ultrasonic<d_min){ d_min = ultrasonic; tacho_min = MotorTachoCount(OUT_A); } } OnFwdSyncEx(OUT_AB,50,100,RESET_NONE); //時計回りに旋回 until(MotorTachoCount(OUT_A)<=tacho_min || ultrasonic<=d_min); adjust; //もとはWait(20); Off(OUT_AB); Wait(500); return d_min; } **通信のための定義(マスター側だけ) [#ocbde45f] マスター側の本体が通信で使う定義が以下のようになる。 #define ultrasonic SensorUS(S1) //超音波センサーの定義 #define master 0 #define slave 1 #define mail MAILBOX1 #define cup_age_little 20 //紙コップをつかめたときに少し上げる #define cup_age 30 //紙をコップ上げる #define cup_sage 40 //紙をコップ下げる #define cup_re 50 //紙をコップ持ち直す #define fin 9 //終わり **スレーブ側の定義とサブ関数 [#ve41796a] スレーブ側に用いた定義が以下のようになる。 #define master 0 #define slave 1 #define mail MAILBOX1 #define cup_age_little 20 //紙コップをつかんだときに少しあげる #define cup_age 30 //紙コップを完全に上げる #define cup_sage 40 //紙コップを下げる #define cup_re 50 //紙コップを持ち直す #define fin 9 //終わり スレーブ側のサブルーチンが以下のようになる。 sub tukamu() //紙コップをつかむ { RotateMotor(OUT_A,50,440); adjust; // Off(OUT_A); Wait(300); } sub hanasu() //紙コップをはなす { RotateMotor(OUT_A,50,-440); adjust; } sub up_little() //紙コップを少しあげる { RotateMotor(OUT_B,60,-180); adjust; } sub ageru() //紙コップを上げる { RotateMotor(OUT_B,60,-720); adjust; } sub down_little() //紙コップを少し下げる { RotateMotor(OUT_B,60,180); adjust; } sub sageru() //紙コップを下げる { RotateMotor(OUT_B,60,720); adjust; } **本文 [#k439fe69] マスター側のプログラムの本文が以下のようになる。 task main() { SetSensorLowspeed(S1); SetSensorLight(S2); escape_A(); //Aから抜け出す intertify_right(); //AからBまで intertify_right(); //AからBまでライントレース int d1 = search_distance(90); //1番の紙コップの方向を向き、またその距離を得る if(d1>10){ //1番の紙コップの5cm手前まで前進 approach(d1-5); } int msg1; //1番の紙コップを少し上げる while(msg1!=fin){ ReceiveRemoteNumber(mail,true,msg1); SendRemoteNumber(slave,mail,cup_age_little); } back(d1-5); //前進したのと同じだけ後退する spinAng(80); //Pの方向を向く while(sensor_light>black+5){ while(sensor_light>black+5){ //微調整 OnFwdSync(OUT_AB,50,0); } adjust; while(sensor_light<white-5){ while(sensor_light<white-5){ //微調整 OnFwd(OUT_B,50); } adjust; intertify_right(); //BからPまでライントレース spinAng(5); //場所を把握しやすいように少し右に向ける approach(26); //26cm前進→Q-S間に行く while(sensor_light>black+5){ while(sensor_light>black+5){ //向きの調整 OnFwd(OUT_A,50); } adjust; while(sensor_light<white-5){ while(sensor_light<white-5){ //向きの調整 OnFwd(OUT_B,50); } adjust; intertify_right(); //Sまでライントレース spinAng(90); //S地点でFの方を向く int msg2; //通信 カップを持ち直す while(msg2!=fin){ ReceiveRemoteNumber(mail,true,msg2); SendRemoteNumber(slave,mail,cup_re); } while(sensor_light>black+5){ //Xの方向に向く OnFwdSync(OUT_AB,50,100); } adjust; while(sensor_light<white-10){ OnFwd(OUT_A,50); } adjust; intertify_left(); intertify_left(); //Xまでライントレース int msg3; //通信紙コップを上げる while(msg3!=fin){ ReceiveRemoteNumber(mail,true,msg3); SendRemoteNumber(slave,mail,cup_age); } adjust; spinAng(160); //Yの方向を向く while(sensor_light>black+5){ OnFwdSync(OUT_AB,50,0); } adjust; スレーブ側のプログラムの本文が以下のようになる。 task main() { int msg1; //1番の紙コップを少し上げる until(msg1==cup_age_little) ReceiveRemoteNumber(mail,true,msg1); while(msg1==cup_age_little){ sageru(); tukamu(); up_little(); SendResponseNumber(mail,fin); Wait(500); msg1 = ""; } int msg2; //紙コップを持ち直す until(msg2==cup_re) ReceiveRemoteNumber(mail,true,msg2); while(msg2==cup_re){ down_little(); hanasu(); tukamu(); up_little(); SendResponseNumber(mail,fin); Wait(500); msg2 = ""; } int msg3; //紙コップをあげる until(msg3==cup_age) ReceiveRemoteNumber(mail,true,msg3); while(msg3==cup_age){ down_little(); ageru(); SendResponseNumber(mail,fin); Wait(500); msg3 = ""; } int msg4; //1番の紙コップを置く until(msg4==cup_sage) ReceiveRemoteNumber(mail,true,msg4); while(msg4==cup_sage){ sageru(); hanasu(); ageru(); SendResponseNumber(mail,fin); Wait(500); msg4 = ""; } *4.感想 [#qd6a9253] ロボットは利用する側の人間が適切に指示をしなければ、上手く動いてくれないということがよく分かった。今の時代ではロボットや機械を適切に操れるということの重要性が増してきているためこのような経験ができて良かったと思う。ロボット、機械を適切に操れるように知識を増やしていきたいと思う。ロボットコンテストの参加者の苦労も少し分かったような気がする。