目次 #contents *課題3 [#va54b46a] 細かいルールは省略するが、端的にまとめると &size(30){ボールを拾って缶に乗せる!}; *課題達成のためのルート [#j10deba6] &ref(2018b/Member/miyashi/Mission3/s_課題3コース.png,100%,設定したルート); *ロボットの説明 [#of1e3a29] **本体について [#hfdefae6] 車型ロボットにボールを扱う機構とセンサ類を設置した。以下で詳細を説明する。 &ref(2018b/Member/miyashi/Mission3/s_DSC_0700.JPG,100%,外観); **ボールを扱う機構について [#jc19a875] 缶にボールを載せるためには、まずボールをロボットが保持する必要があった。一つの方法としてベルトコンベアでボールを掬い上げることにした。写真のような動きでボールを掬うことができる。また、三枚目の画像にあるように、コンベアはボールを二つ同時に保持することが可能であるので、ライントレースする距離を減らしてコースをショートカットできる。 &ref(2018b/Member/miyashi/Mission3/s_1549727873635.jpg,100%,ステップ1); → &size(30){→}; &ref(2018b/Member/miyashi/Mission3/s_1549727874867.jpg,100%,ステップ2); → &size(30){→}; &ref(2018b/Member/miyashi/Mission3/s_1549727876084.jpg,100%,ステップ3); 次に、ボールを缶に載せる部分を紹介する。画像にあるように、車体上部に設置した坂道で転がしてボールを入れることにした。コンベアで上がってきたボールはこの坂道を滑り、缶に向かう。 &ref(2018b/Member/miyashi/Mission3/s_坂道.png,100%,坂道); しかし、坂道で下ってきたボールには勢いがあり、当初は缶の上で弾かれる等、安定してボールを缶の上に載せることができなかった。そこでボールの勢いを殺して安定させる機構を作った。それが以下の画像である。 &ref(2018b/Member/miyashi/Mission3/s_ストッパー.png,100%,ストッパー); この機構(可動な壁)があることで、加速されたボールを減速させ、ボールを逃さないことが可能である。 **没案 [#gfcf025a] ボールを掴んで載せる機構の没案が以下の画像である。ボールを掴んだ後にコンベアでアーム自体を缶の上の高さまで昇降する。その後、缶の場所まで進んで、アームを開く。 &ref(2018b/Member/miyashi/Mission3/s_没案.png,100%,没案の機構); この機構は大きく、アームの構造上車体の前に設置する必要がある。しかし、車体前部に設置すると、ライントレースをする時に缶を邪魔してしまう。また、アームを開閉するために設置するMediumMotorも、アームを支える部分に設置する必要があり、コンベアの上下とともにモータも動くことになり、非常に不安定で確実性がない。よってこの機構は没にした。 **センサ類について [#v8a9ebbc] 使用したのはライントレースに使用するカラーセンサ、缶を発見する超音波センサ、車体の回転を制御するジャイロセンサを一つづつ。カラーセンサと超音波センサに関しては、課題2のレポートで詳細したのでそちらを参照。(URLは後述) &ref(2018b/Member/miyashi/Mission3/s_センサ類.png,100%,カラーセンサと超音波センサ); ***ジャイロセンサ [#zaa3d421] 課題とロボットの特性上、車体を何回も正確に回転させる必要があった。前回までのミッションのときはモーターを決まった値で回して車体を回転させていたが、充電等の影響により回転角度が変わってしまうという失敗が多かった。この反省を踏まえ、充電に影響されにくいセンサ類、を採用した。採用したジャイロセンサは、角度を測ることが可能であり、計測開始と計測終了時の値を得ることにより、何度移動したか、その変位を知ることができる。しかし、完全に正確な値を得ることは不可能であり、数度だけ誤差が生じる。ジャイロセンサは画像のように、車体の上部に設置した。 &ref(2018b/Member/miyashi/Mission3/s_1549810118777.jpg,100%,ジャイロセンサ); *定義したプログラム その1 [#s9b3182f] **基本的なプログラム [#o934100a] 車輪を止めるプログラム。適宜、利用した。 def stop(): ml.stop() #左車輪を停止 mr.stop() #右車輪を停止 決められた角度だけ車輪のモーターを回す。前進、後進、回転、斜め方向etc…多彩な動きを変数により制御する。適宜、利用した。 def angle(l,r,t): ml.run_to_rel_pos(position_sp=l,speed_sp=115,stop_action='hold') #左車輪をlだけ回転させる mr.run_to_rel_pos(position_sp=r,speed_sp=115,stop_action='hold') #右車輪をrだけ回転させる time.sleep(t) 車体後部のベルトコンベアを回すプログラム。ボールを抱える時や発射する時にコンベアを回す。maはベルトコンベアに連動するLargeMotorのこと。 def roll(s): ma.run_to_rel_pos(position_sp=s,speed_sp=80,stop_action='hold') #モーターをsだけ回し、コンベアを回転させる。 time.sleep(3) ジャイロセンサによる制御で、車体を決められた角度回転させるプログラム。センサが検出する角度と車輪の速度を変数で扱う。右回りと左回りの二種類作った。 右回り def kaiten_r(s,t): g1=gs.value() #回転前のジャイロセンサの値g1を測る g2=gs.value() #g2を測る while g2-g1<s: #角度s(変数)回転するまで車体を回す ml.run_forever(speed_sp=t,stop_action='hold') #左車輪を回転させ続ける mr.run_forever(speed_sp=-t,stop_action='hold') #右車輪を回転させ続ける g2=gs.value() #g2を更新 stop() #止める 左回り def kaiten_l(s,t): g1=gs.value() #回転前のジャイロセンサの値g1を測る g2=gs.value() #g2を測る while g1-g2<s: #角度s(変数)回転するまで車体を回す ml.run_forever(speed_sp=-t,stop_action='hold') #左車輪を回転させ続ける mr.run_forever(speed_sp=t,stop_action='hold') #右車輪を回転させ続ける g2=gs.value() #g2を更新 stop() #止める **ライントレースのプログラム [#y6003622] 移動時にライントレースを利用する。課題2のレポートで内容は詳細してあるのでプログラムの説明はそちらを参照。 http://yakushi.shinshu-u.ac.jp/robotics/?2018b%2FMember%2Fmiyashi%2FMission2 def linetrace_rin(): c1=cs.value() while c1>13: #カラーセンサの値が14以上なら次の動きを繰り返す。 c1=cs.value() x=(abs(c1-10)/70)*100 #xの値を決める。 if x>100: #xの値の最大を100とする。 x=100 l=(x/100)*210-30 #左車輪の速度lを定める。 r=150-l #右車輪の速度rを定める。 ml.run_forever(speed_sp=l,stop_action='brake') #与えられた速度lで進む。 mr.run_forever(speed_sp=r,stop_action='brake') #与えられた速度rで進む。 stop() def linetrace_rout(): c1=cs.value() #計測値をc1とする。 while c1<40: #c1<40ならば以下を繰り返す。 c1=cs.value() #c1を計測。 x=(abs(c1-10)/70)*100 #xの値を求める。 if x>100: #xの値の最大値を100とする。 x=100 l=(x/100)*120-40 #lの値を決める。 r=40-l #rの値を決める。 ml.run_forever(speed_sp=l,stop_action='brake') #lの速度で左車輪を動かす。 mr.run_forever(speed_sp=r,stop_action='brake') #rの速度で右車輪を動かす。 stop() #止める。 def linetrace_lin(): c1=cs.value() while c1>13: #カラーセンサの値が14以上なら次の動きを繰り返す。 c1=cs.value() x=abs((c1-10)/70)*100 #xの値を決める。 if x>100: #xの値の最大を100とする。 x=100 r=(x/100)*210-30 #右車輪の速度rを定める。 l=150-r #左車輪の速度lを定める。 ml.run_forever(speed_sp=l,stop_action='brake') #与えられた速度lで進む。 mr.run_forever(speed_sp=r,stop_action='brake') #与えられた速度rで進む。 stop() #カラーセンサの値が13以下なら動きをやめる。 def linetrace_lout(): c1=cs.value() #計測値をc1とする。 while c1<40: #c1<40ならば以下を繰り返す。 c1=cs.value() #c1を計測。 x=(abs(c1-10)/70)*100 #xの値を求める。 if x>100: #xの値の最大値を100とする。 x=100 r=(x/100)*120-40 #rの値を求める。 l=40-r #lの値を求める。 ml.run_forever(speed_sp=l,stop_action='brake')#lの速度で左車輪を動かす。 mr.run_forever(speed_sp=r,stop_action='brake')#rの速度で右車輪を動かす。 stop() #止める。 以上4つのライントレースのプログラムに加え、今回は変数の値(秒数)経過したら、ライントレースを終わるプログラムを作った。 def linetrace_ltime(j): c1=cs.value() #計測値をc1とする t1=time.time() #計測開始時刻をt1とする t2=time.time() #t2を計測 while t2-t1<j: #j秒間経過するまで以下を繰り返す c1=cs.value() #c1を計測 t2=time.time() #t2を更新 x=abs((c1-10)/70)*100 #xの値を求める if x>100: #xの最大値を100とする x=100 r=(x/100)*210-30 #rの値を求める l=150-r #lの値を求める ml.run_forever(speed_sp=l,stop_action='brake') #左車輪をlの速度で回す。 mr.run_forever(speed_sp=r,stop_action='brake') #右車輪をrの速度で回す。 stop() #j秒後にライントレースを停止。 **缶の発見とボール落下させるプログラム [#w1430d97] 缶の場所を探査したい場所に到着した時に使用する。まず任意の変数だけ回転し、超音波センサが最も近い物体を検知した時に回転し始めてからどれほどの角度を回ったかジャイロセンサで把握しておき、変数分回転したあとに最も近い物体の方向を先程とは逆回りで回転しながら向く。 その後、缶に向かって前進する。課題の特性上、缶の配置によって前進する距離が異なるので、缶に超音波センサがぶつかる(センサの値が2550になる)まで前進を続ける。 缶にぶつかったら車体は止まり、ベルトコンベアを回転させ、ボールを落下させる。 缶まで進んだ分後退し、探査開始時に戻るまで車体角度を調節する。 この定義は左回りの探査と右回りの探査の二種類がある。 左回り def discover_l(s,t): u1=us.value() #u1を計測 g1=gs.value() #g1を計測 g2=gs.value() #g2を計測 while g1-g2<s: #角度sになるまで左に回る u2=us.value() #u2を更新 ml.run_forever(speed_sp=-70,stop_action='hold') mr.run_forever(speed_sp=70,stop_action='hold') g2=gs.value() #g2を更新 if u2<u1: #最も近い物体がある方向を向いた時の距離と角度を保存 u1=u2 #u1を更新 g3=gs.value() #g3を計測 g4=gs.value() #g4を計測 g5=gs.value() #g5を計測 if u1<=t: #t(距離)の中に感があった時 while g5-g4<g3-g2-6: #缶のある方向を向くまで車輪を動かす mr.run_forever(speed_sp=-70,stop_action='hold') ml.run_forever(speed_sp=70,stop_action='hold') g5=gs.value() #g5を更新 stop() #車体を止める u3=us.value() #u3を計測 t1=time.time() #t1缶に向かう前の時間を計測 while u3<2550: #缶に当たる(センサの値が2550になる)まで車輪を回し続ける ml.run_forever(speed_sp=100,stop_action='hold') mr.run_forever(speed_sp=100,stop_action='hold') u3=us.value() #u3を更新 t2=time.time() #缶に当たった時をt2とする stop() #止める roll(220) #コンベアを回転させ、ボールを落とす ml.run_timed(speed_sp=-100,time_sp=(t2-t1)*1000,stop_action='hold') #缶に向かい始めた位置まで戻る mr.run_timed(speed_sp=-100,time_sp=(t2-t1)*1000,stop_action='hold') #缶に向かい始めた位置まで戻る time.sleep(t2-t1) #戻るまで停止 g6=gs.value() #g6を計測 g7=gs.value() #g7を計測 while g7-g6<=g1-g3-6: #本プログラム開始時の位置に戻る mr.run_forever(speed_sp=-70,stop_action='hold') ml.run_forever(speed_sp=70,stop_action='hold') g7=gs.value() #g7を更新 stop() #止める else: #指定したt(距離)に缶がなかった場合 while g5-g4<90: #直角に戻る mr.run_forever(speed_sp=-70,stop_action='hold') ml.run_forever(speed_sp=70,stop_action='hold') g5=gs.value() #g5を更新 stop() #止める 右回り 左回りと同様のプログラムのため説明は割愛 def discover_r(s,t): u1=us.value() g1=gs.value() g2=gs.value() while g2-g1<s: u2=us.value() ml.run_forever(speed_sp=70,stop_action='hold') mr.run_forever(speed_sp=-70,stop_action='hold') g2=gs.value() if u2<u1: u1=u2 g3=gs.value() g4=gs.value() g5=gs.value() if u1<=t: while g4-g5<g2-g3-6: mr.run_forever(speed_sp=70,stop_action='hold') ml.run_forever(speed_sp=-70,stop_action='hold') g5=gs.value() stop() u3=us.value() t1=time.time() while u3<2550: ml.run_forever(speed_sp=100,stop_action='hold') mr.run_forever(speed_sp=100,stop_action='hold') u3=us.value() t2=time.time() stop() roll(220) ml.run_timed(speed_sp=-100,time_sp=(t2-t1)*1000,stop_action='hold') mr.run_timed(speed_sp=-100,time_sp=(t2-t1)*1000,stop_action='hold') time.sleep(t2-t1) g6=gs.value() g7=gs.value() while g6-g7<=g3-g1-6: mr.run_forever(speed_sp=70,stop_action='hold') ml.run_forever(speed_sp=-70,stop_action='hold') g7=gs.value() stop() else: while g4-g5<90: mr.run_forever(speed_sp=70,stop_action='hold') ml.run_forever(speed_sp=-70,stop_action='hold') g5=gs.value() stop() discoverは以下の画像のような動きをする。 &ref(2018b/Member/miyashi/Mission3/s_discover1.png,100%,ステップ1); &ref(2018b/Member/miyashi/Mission3/s_discover2.png,100%,ステップ2); &ref(2018b/Member/miyashi/Mission3/s_discover3.png,100%,ステップ3); &ref(2018b/Member/miyashi/Mission3/s_discover4.png,100%,ステップ4); *定義したプログラムその2 [#gfc686da] **道中を細かく分けた定義 [#pf903340] まずはスタートからGに行き、赤ボールを拾う。 def Redball(): angle(250,250,3) #赤ボールが掬える場所まで進む kaiten_r(180,110) #180度回転する roll(220) #コンベアを回転させ、赤ボールを保持する kaiten_r(195,110) #195度回転する この後、ライントレースを行うので回転後にカラーセンサが確実に白を検知するように15度余分に回る linetrace_lin() #交差点Gに到達 次に、青ボールを拾う。 def Blueball(): angle(-20,160,2) #Gの交差点を斜めに越える linetrace_lin() #ライントレースでHまで到達 angle(-35,-35,1.5) #少し後退する。 kaiten_r(175,110) #175度回転してボールが捕獲可能な場所にコンベアを設置 roll(220) #ボールを掬う その後、Eまで到達する。 def move(): kaiten_r(190,110) #車体を戻す angle(-50,-50,1) #後退してライントレースが確実にできるようにする linetrace_lin() #以下のライントレースの繰り返しでEまで到達する linetrace_lout() linetrace_lin() linetrace_lout() linetrace_lin() E到達後、ボールを載せに向かう。ここではinputを使用し、サイコロを振って出た目の番号(rとする)を入力することでそれに対応した動きをする。 r=int(input("insert numbera")) if r<=2: #サイコロが1または2のとき kaiten_l(90,110) #左に90度向く discover_l(90,250) #左に90度缶を探査し、発見時はボールを載せて探査開始時の位置まで戻る kaiten_l(95,100) #元の位置に戻る(5度は微調整) if r==3: #サイコロが3のとき angle(-40,180,2) #交差点Eを斜めに越える linetrace_lin() #ライントレースでDまで到達 discover_r(60,250) #右に60度缶を探査し、発見時はボールを載せて探査開始時の位置まで戻る angle(-150,-150,2) # 後退する kaiten_l(195,110) #195度回転 linetrace_rin() #ライントレースでEに到達 angle(230,-40,2) #交差点Eの黒線を越えてマップ視点で左向きに車体を向かせる if r==4: #サイコロが4のとき angle(-40,180,2) #交差点Eを斜めに越える linetrace_lin() #ライントレースでDまで到達 discover_l(60,250) #左に60度缶を探査し、発見時はボールを載せて探査開始時の位置まで戻る kaiten_l(195,110) #195度回転する linetrace_rin() #ライントレースでEに到達 angle(230,-40,2) #交差点Eの黒線を越えてマップ視点で左向きに車体を向かせる if 5<=r: #サイコロが5または6のとき angle(-40,180,2) #交差点Eを斜めに越える linetrace_lin() #ライントレースでDまで到達 linetrace_lout() #マップ視点で右を向く linetrace_ltime(3) #3秒間ライントレースする discover_l(60,190) #左に60度缶を探査し、発見時はボールを載せて探査開始時の位置まで戻る angle(-130,-130,3) #後退する kaiten_l(180,110) #180度回転する linetrace_rin() #以下のライントレースでEまで到達する linetrace_rout() linetrace_rin() angle(230,-40,2) #交差点Eの黒線を越えてマップ視点で左向きに車体を向かせる 以上の4パターンの動きはいづれも同じ場所(マップ視点で黒線EIの下部)で終了する。ここから青の缶を探査する場所Kまで移動する。 def BlueZone(): linetrace_rin() #以下のライントレースでHまで到達 linetrace_rout() linetrace_rin() angle(70,0,1) #交差点Hを超える linetrace_rin() #ライントレースでKまで到達 この後に青のボールを放つ。ここでは、赤の缶の時のように場合分けをしてダミーかどうかを判別することが困難であったので、ダミー缶の想定はせずに、単純に最も近い缶にボールを載せるプログラムを作った。 def discover2(): kaiten_l(60,100) #60度左向きになり、探査準備をする。 discover_r(90,500) #右に90度缶を探査し、発見時はボールを載せて探査開始時の位置まで戻る **課題3のロボットの動作をまとめたプログラム [#c8aad90f] 以上のプログラムをまとめたプログラムを定義してある。予め説明してあるので改めて説明するのは割愛する。 def start(): r=int(input("insert numbera")) Redball() Blueball() move() if r<=2: kaiten_l(90,110) discover_l(90,250) kaiten_l(95,100) if r==3: angle(-40,180,2) linetrace_lin() discover_r(60,250) angle(-150,-150,2) kaiten_l(195,110) linetrace_rin() angle(230,-40,2) if r==4: angle(-40,180,2) linetrace_lin() discover_l(60,250) kaiten_l(195,110) linetrace_rin() angle(230,-40,2) if 5<=r: angle(-40,180,2) linetrace_lin() linetrace_lout() linetrace_ltime(3) discover_l(60,190) angle(-130,-130,3) kaiten_l(180,110) linetrace_rin() linetrace_rout() linetrace_rin() angle(230,-40,2) BlueZone() discover2() 実際の作動プログラム start() プログラムを作動させると、 insert numbera と表示され、赤ボールを載せる缶がある位置の番号を入力すると、任意の缶に載せる。今回の課題の目的の一つとしてダミー缶が判別できるかどうかが重要であった。サイコロの出目に応じてinputでロボットの動きを選択するのは正直かなりグレーであったが、一応start()という一つのプログラムで稼働させたのでギリギリセーフであると主張したい。 *課題3を振り返って [#afa9ea10] **ロボコンについて [#v462ca9f] ロボコンの結果は優勝だった。とはいえ、正しい缶にボールを設置することができなかった。本番以外では3~4割で高いとは言えないが、成功する可能性も十分にあっただけにこの結果が悔やまれる。 **反省 [#ief8bd9f] 課題3では、課題1や2に比べて成功率が不安定だった。問題の要因はジャイロセンサであった。EV3を起動させた後にジャイロセンサが正常に作動しているかばらつきがある。正常な作動ができないと、今回の重要なプログラムであるkaitenが使えなくなる等、課題の達成を困難にさせる。なるべくEV3の調子に左右されることのない制御をするつもりだったが、ジャイロセンサを始めて使ったこともあり、ジャイロセンサの脆弱性に気付くことができなかった。ジャイロセンサの性能は我々にはどうしようもないので仕方がないにしても、結果的に低い成功率だったので残念だった。ジャイロセンサよりもEV3の調子に左右されないプログラムを作ることが成功率を上げるためには必要だったと思う。 ロボット本体について。ライントレースとコンベアにモータを3つとセンサを3つだけ使用した。他の班に比べてモータやセンサの数が少なく、EV3に大きな負担をかけずに済んだ。ロボット自体は縦方向に大きくなったものの、どの動きも円滑にできていたおり課題達成に十分であったので良かった。 また、今回は通信を行っていない。本来ならば通信を行うべきだったが、mosquittoの扱いが上手くいかずに断念した。とはいえ一つのEV3で十分に課題完全達成の兆しは見えていたので結果的に通信ができなかったことによる損害は少ないように思う。 *ゼミを振り返って [#g901fc53] 受講前、私は機械・ロボット学科でありながら、ロボットに関する知識が非常に乏しかった。しかし、この授業を通してロボットの製作や制御を学び、実践して自分の糧とすることが出来て良かった。一番大変だったのは制御の不安定性であった。同じプログラムで動かしても毎回結果が異なり、如何に安定した動きをさせるかに集中してプログラムを組んだ。もちろんそのためにはロボットの形も重要であった。動作の確実性を高くするための設計を心掛けた。プログラムかロボット製作のどちらが大変だったかと言えばプログラムだが、両者の関係は相補的であると痛感した。ロボットを疎かにしてはプログラムで苦労し、ロボットをできるだけ満足のいく出来にすればプログラミングの負担が減る。特に一回目の課題では顕著にこの関係性が表れた。プリンター型のロボットを作っていた周りの班は安定して成功していたが、車型で文字を書いた私の班は制御に手間取った。それをきっかけに課題2以降はロボットの設計にこだわるようになり、それが安定したプログラムの実行に繋がり、発表日に間に合わせることができていたので良かった。長く書いたが、結論として言いたいのは当たり前かもしれないが、ロボットもプログラムも両方手を抜かないことが成功の近道だった。学科のカリキュラム上、これからもロボットとは必ず関わるが、この講義で学んだことを存分に活かしたいと思う。 *その他 [#sf430009] 閲覧回数 総計:&counter(total); 今日:&counter(today); 昨日:&counter(yesterday);