箱の中にある2つのボールと床に置かれた2つのボールをすべてからの箱の中にいれる
移動に関してはミッション2で使用したステージを使用しているので、前回のライントレースのシステムを利用したい。また床に置く2つのボールも前回のプログラムが使える。しかし、箱に入れたボールに関しては、前回のロボットで取り出すことは難しそうである。箱の中からボールを取り出せるようなロボットを作らなければならない。
ミッション3ではEV3のセットを2セット使うことができる。そこで2台のEV3の使い方は2台のEV3を搭載した1つのロボットを作る方法と2台のロボットを作る方法が考えられる。私たちは2台のロボットを作ることにした。2台のロボットをつくることで片方が失敗してももう片方が成功できるというリスクの分散ができる。また、時間の短縮にもなる。しかし、デメリットとして、2台が別々に動くことでお互いにぶつかる危険性がある。また、モーターの数も2台それぞれに移動用に2つずつ使わなければいけないので、ボールをつかむ部分には結局1つずつしか付けられない。この二つの問題を解決しなければならない。
上図のとおりである。基本的には最短距離で、また、ボールに対して向きを合わせたいので、ボールに向きを合わせやすい場所を通っている。お互いのロボットがぶつからないようにできるだけ同じところは通らないようにしている。
今回はEV3を二つのロボットに分けて使用しているので、ボールをつかむ部分に使えるモーターはミディアムモーターだけだ。ボールをつかみ、箱に入れるという複雑な動きを工夫して作らなければならない。
こっちのロボットは箱ごと持ちあげて、中身だけをもう一つの箱に入れるという動きをする。まずは箱を持ち上げるアームを作らなければならない。
このような作りになった。アームの間に箱をはさみ、途中まで持ちあげ、ゴールの箱の手前でさらに持ち上げ、箱は固定したままボールだけを後ろに落とし込む構造である。なので、ボールを入れるときは後ろを向かなければならない。
こちらはボールが床に置かれているため、ミッション2と同じようなものを作ればよい。
このような作りになっている。仲間が作ったものなので詳しくは書かないが、前方のシャベル上のアームでボールをすくい上げ、途中までアームを上げて、ゴール前でさらに持ち上げボールを後ろへ落とし込む構造である。ボールを後ろへ投げるのが流行りのようである。
プログラムは分担して作ったため、それぞれのプログラムの詳細な意図については説明できない。プログラムを書いた人の説明を見るべきである。ここではプログラムの大まかな流れとその説明をする。また、ミッション1,2で同様に使われている基礎的な内容については説明を省く。わからなければ過去のミッションのページを見てほしい。プログラムを作成した本人のページを張っておく。
ミッション2と同じなので説明を省きたいところだが、前回は関数が連続で何回使われたかで交差点を認識してしまった。推奨されるのは時間を測る方法なので説明しておかないといけなさそうである。交差点に入ったロボットはラインから出ようとし続けるはずである。この動作はラインをトレースしている時よりも長いので、この連続で行われている時間がライントレースしている時よりも長いときにライントレースを終了するようにすればよい。そこでtime関数が使われるが、これは現在の絶対時間を表すものである。使いたいのは黒に入ってから白に出るまでの相対時間であるため、黒に入った時間と黒に入ってから連続して黒にいる現在の時間の差から相対時間を出す。現在の時間はtime.time()でわかるので、黒に入って修正し始めた時間が分かればよい。プログラムではこの時間をxに入れておき、白にいるときはxの値を常に現在の時間にし、黒にはいっているときには後進をやめればよい。プログラミングの例を書いておく。解説は省く。
def line_trace_right(t,c): x = time.time() while time.time() - x < t: if cs.value() > c: mL.run_forever(speed_sp=100) mR.run_forever(speed_sp=50) x = time.time() else: mL.run_forever(speed_sp=50) mR.run_forever(speed_sp=100) sleep(0.01) mL.stop() mR.stop()
アームの役割はアームを下す、持ち上げる、ボールを射出するの三つの役割がある。つまりアームは3段階の動きをしなければならない。プログラムではアームのスピードと時間で調節すればよいが、重すぎると調整が利かなくなる。微調整の利くようなロボットを作っておこう。
用紙間はラインで結ばれていない。移動するにはもう一方の紙のほうを向き適当に直進してラインを認識したところでとまればいい。注意すべき点は直進するときの向きとスピードだ。向きがずれると宇宙の彼方へ行ってしまうかもしれないし、早すぎるとラインを認識せずに通り越してしまうかもしれない。
例
mL.run_forever(speed_sp=-50) mR.run_forever(speed_sp=50) #反対側へ向きを合わせる sleep(2) while cs.value() > 15: #灰色または白を認識している間は直進を続ける mL.run_forever(speed_sp=70) mR.run_forever(speed_sp=70) mL.stop() #ラインに入ったので停止 mR.stop()
#!/usr/bin/env python3 from ev3dev.ev3 import * from time import sleep mL = LargeMotor('outB') mR = LargeMotor('outD') mZ = MediumMotor('outA') cs = ColorSensor('in1') cs.mode = 'COL-REFLECT'
def motor_init(): #全モーターをリセット mL.reset() mR.reset() mZ.reset() def down(): #アームを下げる mZ.run_timed(time_sp=600,speed_sp=150,stop_action='brake')
def up(): 箱ごとアームを途中まで上げる 箱は重いので値も変化させる mZ.run_timed(time_sp=685,speed_sp=-160,stop_action='hold') sleep(1) def up2(): #箱を更に持ち上げ、ボールをゴールに流し込む mZ.run_timed(time_sp=285,speed_sp=-569,stop_action='hold') sleep(1) def nage(): #素早くアームを上げて箱を放り投げる mZ.run_timed(time_sp=400,speed_sp=-1000,stop_action='brake') sleep(1) def go(): #デフォルトの前進 mL.run_timed(time_sp=80,speed_sp=150,stop_action='brake') mR.run_timed(time_sp=80,speed_sp=150,stop_action='brake') def over(t): #前進2 tの値で時間を調整 mL.run_timed(time_sp=t,speed_sp=200,stop_action='brake') mR.run_timed(time_sp=t,speed_sp=200,stop_action='brake') sleep(1) def back(t): #後進 tの値で時間を調整 mL.run_timed(time_sp=t,speed_sp=-200,stop_action='brake') mR.run_timed(time_sp=t,speed_sp=-200,stop_action='brake') def turnl2(): #ライントレースにおける右折用 mL.run_timed(time_sp=200,speed_sp=45,stop_action='brake') mR.run_timed(time_sp=200,speed_sp=-50,stop_action='brake') sleep(1) def turnr2(): #ライントレースにおける左折用 mR.run_timed(time_sp=200,speed_sp=75,stop_action='brake') mL.run_timed(time_sp=200,speed_sp=-45,stop_action='brake') def turnr2X(x): #左側ライントレースにおける交差点認識後の角度修正 mR.run_timed(time_sp=x,speed_sp=-75,stop_action='brake') mL.run_timed(time_sp=x,speed_sp=45,stop_action='brake') def turnr2Y(y): #右側ライントレースにおける交差点認識後の角度修正 mR.run_timed(time_sp=y,speed_sp=45,stop_action='brake') mL.run_timed(time_sp=y,speed_sp=-75,stop_action='brake') def turnC(a): #aの値でどれだけ左折するか調整 mR.run_timed(time_sp=a,speed_sp=-150,stop_action='brake') mL.run_timed(time_sp=a,speed_sp=150,stop_action='brake') sleep(1) def turnD(a): #aの値でどれだけ右折するか調整 mR.run_timed(time_sp=a,speed_sp=150,stop_action='brake') mL.run_timed(time_sp=a,speed_sp=-150,stop_action='brake') sleep(1) def linel(): #ラインの左側のトレース S = 1 while S > 0: #Sが1の間繰り返され0になると終了 go() if cs.value() > 15: #白側にそれた場合右に修正 while cs.value() > 15: turnl2() if cs.value() < 9: #黒側にそれた場合 tS = time.time() #時間計測開始 while cs.value() < 9: turnr2() #左に修正 if time.time()-tS > 0.48: #左へ修正が連続で0.48だけ続いた場合 turnr2X((time.time()-tS)*1400) #曲がりすぎた分を修正する S = 0 #繰り返しを終了する sleep(1) def liner(): #ラインの右側のトレース S = 1 while S > 0: #繰り返し開始 go() if cs.value() > 15: #白側にそれた場合左に修正 while cs.value() > 15: turnr2() if cs.value() < 9: #黒側にそれた場合 tS = time.time() #時間計測開始 while cs.value() < 9: turnl2() #右に修正 if time.time()-tS > 0.48: #右へ修正が連続で0.48だけ続いた場合 turnr2Y((time.time()-tS)*1400) #曲がりすぎた分を修正する S = 0 #繰り返しを終了する sleep(1) def running(): #用紙間を渡る while cs.value() > 15: #ラインにたどり着くまで実行 go() sleep(1)
motor_init() sleep(110) #もう一つのロボットとぶつからないように待機 liner() #Aからスタートし、右側を辿り交差点Cを認識 over(833) #ここからトレーまでの位置を調整する sleep(1) turnC(1760) back(2000) sleep(3) down() #アームを下げる over(2000) #トレーの下にアームを通す sleep(3) up() #アームを上げる sleep(1) turnC(800) #辺BCに方向転換 over(400) linel() #CからBへ左側を辿り、Bで交差点認識をする turnC(900) #線B'D'に向かって方向転換 over(500) #線をまたぐ running() #線B'D'に着くまで前進 sleep(3) turnC(1910) #180度方向転換 sleep(4) back(300) #トレーに近づく sleep(1) up2() #トレーへボールを流し込む sleep(2) down() #ここでアームを上げ下げをして、流しそびれたボールを確実に入れようとする sleep(1) up() over(150) #同じく前進と後進を行い、ボールを確実に処理する sleep(0.5) back(150) sleep(1) turnD(500) #線BDに向かって方向転換 running() #線BDに着くまで前進 linel() #線BDの左側を辿り、Dで交差点認識をする over(150) sleep(1) turnC(1100) #円GHIJに向かって方向転換 down() #ここの二つで箱をぶっ飛ばす nage()
#!/usr/bin/env python3 from ev3dev.ev3 import * from time import sleep mL = LargeMotor('outD') mR = LargeMotor('outC') mM = MediumMotor('outB') cs = ColorSensor('in3') cs.mode = 'COL-REFLECT'
def motor_init(): #ラージモーターをリセットする。 mL.reset() mR.reset() def arm_move(t,y): #アームを下げる t,yの値によってアームをどれだけ下げるか調整 x = time.time() #xに現在の時刻をいれる while time.time() - x < t: #t秒間下の動作を繰り返す mM.run_forever(speed_sp=y) #yの速さでアームを下げる mM.stop() sleep(1) def catch_up(x,t): #アームを上げる なぜ下げるときと違う書き方をしたのかは謎 mM.run_timed(speed_sp=x,time_sp=t,stop_action='hold') sleep(1) def hold_out(): #上の動作をするとモーターが固定されてしまう brakeにすることで固定を解除する mM.run_timed(speed_sp=100,time_sp=100,stop_action='brake') sleep(1) def move_forward(t): #t秒の間前進 x = time.time() #xに現在の時間を入れる while time.time() - x < t: #t秒経つまで下の動作を繰り返す mL.run_forever(speed_sp=-100) #マイナスの値が入っているが、前進する mR.run_forever(speed_sp=-100) #同じ mL.stop() mR.stop() def move_back(t): #上と同じ仕組みでバックする スピードの符号を逆にしただけ x = time.time() while time.time() - x < t: mL.run_forever(speed_sp=100) mR.run_forever(speed_sp=100) mL.stop() mR.stop() def turn_left(t): #t秒左に転回 左右のモーターを逆に動かした x = time.time() while time.time() - x < t: mL.run_forever(speed_sp=100) mR.run_forever(speed_sp=-100) mL.stop() mR.stop() def turn_right(t): #上の逆 x = time.time() while time.time() - x < t: mL.run_forever(speed_sp=-100) mR.run_forever(speed_sp=100) mL.reset() mR.reset() def line_trace_right(t,c): #交差点に入るまでラインの右側を辿る x = time.time() while time.time() - x < t: #t秒経つまで動作を繰り返す if cs.value() > c: #ラインの外側にそれたら左に転回 mL.run_forever(speed_sp=40) mR.run_forever(speed_sp=-80) x = time.time() #xの値を更新 else: #ラインの内側へそれたら右に転回 これがt秒連続で行われればとまる mL.run_forever(speed_sp=-80) mR.run_forever(speed_sp=40) if time.time() - x >t: #whileと重複しているのでいらないと思われる mL.stop() #上のifを使わない場合whileのなかから出せばよい mR.stop() def line_trace_left(t,c):交差点に入るまでラインの左側を辿る x = time.time() while time.time() - x < t: #t秒経つまで動作を繰り返す if cs.value() > c: #ラインの外側にそれたら右に転回 mL.run_forever(speed_sp=-80) mR.run_forever(speed_sp=40) x = time.time() #xの値を更新 else: #ラインの内側へそれたら左に転回 これがt秒連続で行われればとまる mL.run_forever(speed_sp=40) mR.run_forever(speed_sp=-80) if time.time() - x >t: mL.stop() mR.stop() def change_paper(): #2用紙間を移動するためのもの x = 0 #0からスタート while x < 2: #2回連続でストップしたら終了? if cs.value() > 50: mL.run_forever(speed_sp=100) mR.run_forever(speed_sp=100) if cs.value() < 50: mL.stop() mR.stop() x = x + 1
if __name__ == '__main__': #おまじない motor_init() #リセット sleep(1) hold_out() print(cs.value()) #確認用 sleep(1) #ここまでは準備 line_trace_left(1,40) #A'k'間を移動 sleep(1) turn_right(0.55) #ボールに対する向きの調整 sleep(1) move_back(4.2) #おろしたアームがボールに当たらないように後進 sleep(1) arm_move(1.5,-400) #アームを下す sleep(1) move_forward(1.6) #ボールをすくうため前進 sleep(1) catch_up(550,750) #アームを上げる sleep(1) move_forward(5) #前進 sleep(1) turn_right(1.25) #後方に箱が来るよう角度を調節 sleep(1) arm_move(1,800) #アームを上げボールを射出 sleep(1) hold_out() #ミディアムモーターの出力を解除 sleep(1) turn_left(1.7) sleep(1) move_forward(0.7) sleep(1) turn_left(1) #ここまで位置調整 sleep(1) line_trace_right(1,40) #L'K'間を移動 sleep(1) turn_left(0.5) #ラインに平行になるよう左に調節 sleep(1) move_forward(6) #用紙間を移動 change_paper()は使わない模様 sleep(1) move_forward(4.5) #ラインを通り越して前進 ここから別のメンバー作成 sleep(1) line_trace_left(1,40) #kのラインの左側を辿りkで止まる sleep(1) turn_right(0.6) #ボールに向けて角度を調整 sleep(1) move_back(4.2) #アームを下すためにバック sleep(1) arm_move(6,-200) # アームを下す sleep(1) move_forward(2.2) #ボールへ前進 sleep(1) catch_up(550,750) #ボールを持ち上げる sleep(1) turn_right(2.7) #箱の向きヘあわせる sleep(1) move_forward(3) #箱へ直進 sleep(1) turn_right(2.5) #180°回転 sleep(1) move_back(2) #ボールが箱に入るようにバックで位置調整 sleep(1) arm_move(1,300) #ボールを射出 sleep(1) turn_left(1.5) #L'のほうを向く sleep(1) move_forward(3) #直進してもう一方のロボットとの接触を防ぐ sleep(1) #関数の中にsleep()をいれておけばよりすっきりした
発表会では一回目にボールを一つ入れることができた。しかし、欲に負けて再トライしたところ、ボールは入らず、0点になってしまった。改善点としてはまず、バッテリーを充電しておくこと。バッテリーの持ちが悪いので、こまめな充電をしておくべきだった。第二にミディアムモーターのパワー不足を工夫して改善しなければならなかった。重いものを持ちあげようとすると、パワー不足で持ち上がらない、または一番上まで一気に持ち上がってしまうという極端な結果となってしまった。これはプログラムで改善できるものではなかったので、ロボットの構造を改良すべきだっただろう。例えば歯車をかませてモーターの回転数を増やしてあげるべきだった。