今回の課題はライントレースで決められた場所を走らせ、その途中で空き缶を認知し缶に向かってボールを投げさせるロボットを作成するというものであった。コースは2つあり、その内容や経路は経路は以下に表す通りである。
第1コース
ロボットを長方形X内におき、Aをスタート
1,Bを右折
2,Kで一時停止して左折
3,Jを直進
4,Iを直進
5,Hを左折
6,Gで一時停止して左折
7,Eで一時停止して右折
8,Lを経て正方形Y内に入って停止
(一時停止の指定がある場所は、1秒間停止すること)
ボールはロボットが弧KJIH上にある時にQ地点の空き缶に当てる。
第2コース
ロボットを正方形Y内におき、Lをスタート
1,Eを一時停止して直進
2,Iを一時停止して左折
3,Hを直進
4,Kを直進
5,Jを左折
6,Cを一時停止して右折
7,Eを一時停止して直進
8,Gを一時停止して直進
長方形X内に入って停止 (一時停止の指定がある場所は、1秒間停止すること)
ボールはロボットが弧IHKJ上にある時にP地点の空き缶に当てる。
基本的には説明書で作った車体を利用して作成してある。
今回はライントレースということで色を読み取って機体を黒線に合わせながら進んでいくこと、さらに缶の位置を検知してボールを投げることが課題に挙げられているため超音波センサーとカラーセンサーをロボットに組み込まなければならない。ここでいう超音波センサは以下のようなものである。
電源が付くと...
今回の課題ではカラーセンサーが読み取る値が非常に重要になってくる。それを考慮し私たちの機体はなるべく前輪とカラーセンサーの位置が近くなるようにした。こうすることでカラーセンサーが把握する色と前輪の色が近くなり、黒線に沿って動いてくれるようになる。 今回大きな課題となったのは配線である。モーターが3つ、センサも3つずつあったので工夫してつなげないと長さが足りなくなったり絡まって車輪に巻き込んだりということが起こる恐れがあった。私たちはセンサの入力が数字、モータの出力がアルファベットのコンセントにつながなければならないので、導線を外してev3を前後に変えることで導線が絡まりにくくなりうまく動かせるようになった。
このロボットではボールを転がす形で投げるような設計にした。動きは下図の表すとおりである。
この状態から・・・
このように動く。
ここの部分は車体より上に作る必要があったのだが、ある程度頑丈に作っておかないと車体を動かしている途中でこの部分が外れてしまったり、ボールが大きく揺れて転がって落ちてしまう可能性があった。そのため残り少ない部品を使い、なるべく衝撃に強くなるように補強しながらつくるようにした。そのこともあり,上部分のおかげで重心が大きく変わってしまうので上部分だけを先に作って取り返しのつかないことにならないように、放出部分の作成と車体部分のマイナーチェンジは並行して行われた。
#!/usr/bin/env python3 from ev3dev.ev3 import * from time import sleep mL = LargeMotor('outA') #左タイヤの出力の定義 mR = LargeMotor('outB') #右タイヤの出力の定義 mi = MediumMotor('outD') #放出部分の出力の定義 s1 = TouchSensor('in3') #タッチセンサの出力の定義 s2 = ColorSensor('in4') #カラーセンサの出力の定義 U1 = UltrasonicSensor('in2') #超音波センサの出力の定義 def laf(t): #left and forwardの略。ライントレースの際に線の右にはみ出しかけたときに使う mL.run_timed(time_sp=30,speed_sp=120+20*t,stop_action='brake') mR.run_timed(time_sp=30,speed_sp=120-20*t,stop_action='brake')
def raf(t): #right and forwardの略。lafの逆向き mL.run_timed(time_sp=30,speed_sp=120-20*t,stop_action='brake') mR.run_timed(time_sp=30,speed_sp=120+20*t,stop_action='brake')
def tl(t): #turn leftの略 lafと似ているがより大きく回転させたいときに使う mL.run_timed(time_sp=20,speed_sp=-150*t,stop_action='brake') mR.run_timed(time_sp=20,speed_sp=150*t,stop_action='brake')
def tr(t): #turn lightの略 tlの逆向きに表す mL.run_timed(time_sp=20,speed_sp=100*t,stop_action='brake') mR.run_timed(time_sp=20,speed_sp=-100*t,stop_action='brake')
def fd(): #まっすぐに進ませる定義。左右で同じだけ進ませるときに使う。 mL.run_timed(time_sp=20,speed_sp=150,stop_action='brake') mR.run_timed(time_sp=20,speed_sp=150,stop_action='brake')
def roll(t,s): #車体を回転させる。スピードを固定して回る大きさを調整したいときに使う。 mL.run_to_rel_pos(position_sp=t,speed_sp=150,stop_action='brake') mR.run_to_rel_pos(position_sp=s,speed_sp=150,stop_action='brake')
基本的なロボットの動かし方は以下のとおりである。
以下はボールを投げる際に利用した定義である。
def throwing(s,t): #ボールを投げる際につかわれる mi.run_timed(time_sp=s,speed_sp=t,stop_action='brake')
def turn(s,t): #缶を探すときに車を回す mL.run_timed(time_sp=s,speed_sp=t,stop_action='brake') mR.run_timed(time_sp=s,speed_sp=-t,stop_action='brake')
ここからは実際にロボットを動かすプログラミングである。
roll(150,150) #前にロボットを動かす sleep(3) #3秒止まる w=0 #変数wの定義 b=0 #変数bの定義 i=0 #変数iの定義
while s1.value() == 0: #タッチセンサーが押されていないときに命令を実行させる。 t0 = time.time() if i == 0: #i=0の時の数える時間 s=0.2 elif i == 3: #i=3の時の数える時間 s=1.0 elif i == 6: #i=6の時の数える時間 s=3 elif i == 7: #i=7の時の数える時間 s=1.1 elif i == 8: #i=8の時の数える時間 s=6.5 elif i == 9: #i=9の時の数える時間 s=0.7 else: #その他での数える時間 s=0.8 while i <= 9: if time.time()-t0 <= 7*s: #交差点を出発してから7*s秒経つまで交差点を認識させないようにする b=0 if 65 < s2.value(): #道から大きく外れたときに使う tl(-1) w=w+1 b=0 if 55 < s2.value() <= 65: #道から少し外れたときに使う laf(1) w=w+1 b=0 if 50 < s2.value() <= 55: #黒と白のちょうど境界線を走るときに使う fd() w=0 b=0 if 40 < s2.value() <= 50: #道の中に少しだけ入り込んだときに実行する raf(1) b=b+0.6 if s2.value() <= 40: #道に深く入り込んだ時に使う tr(-1) b=b+1 #変数bに1を足す w=0 #変数wを0に戻す elif b>33: #交差点を認識した時の命令 i=i+1 #iに1を足す print(i) #コマンドプロンプトにiの値を表示させる break #命令を終わらせる
ルート2では左沿いでのみトレースを行ったので1つだけしか定義を作らなかったが、ルート1では右沿いと左沿いを両方使う必要があったので、定義を2つ書く必要があった。
if i == 1: #iが1の時の命令 sleep(2) #2秒休み roll(30,-30) sleep(1) #1秒休み roll(50,50) sleep(2) #2秒休み if i == 3: #iが3の時の命令 ここは缶を探すコマンドも含まれる roll(200,0) sleep(2) #2秒休み I=0 while s1.value()==0: #ここから缶を見つけてボールを投げる命令が始まる。 u = 100000 t0=time.time() print(I) turn(12000,70) while time.time()-t0<= 12: #命令を実行する際の条件。この時間を満たすときロボットが缶を探す。 if u >=U1.value(): #この行から缶の距離を測る u = U1.value() t1 = time.time() print(u) else: continue #この行まで間の位置を測る(最小値の利用) sleep(0.5) #0.5秒休み t2=time.time() #缶を探してから止まるまでの時間を計る sleep(1) #1秒休み print(t1) print(t2) t3=t2-t1 print(t3) turn(t3*(1040+10*i),-70) sleep(t3*1.2) #t3の1.2倍だけ秒休み if u+20>U1.value(): #缶の方向を向いたときのみ球を投げさせる throwing(1000,50) sleep(3) #3秒休み throwing(1000,-50) break elif I == 3: #2回探せなかったときは強制的にボールを投げる throwing(1000,50) sleep(3) #3秒休み throwing(1000,-50) break else: #缶が見つからなかったときもう一回ボールを探させる I=I+2 sleep(2) #2秒休み roll(-30,-30) sleep(3) #3秒休み if i == 4: #iが4の時の命令 roll(200,0) sleep(2) #2秒休み if i == 6: #iが6の時の命令 roll(-10,-10) sleep(2) #2秒休み roll(420,-20) sleep(6) #6秒休み if 8 <= i <= 9: #iが8,9の時の命令 roll(30,-30) sleep(1) #1秒休み roll(70,70) sleep(2) #2秒休み if i == 10: #iが10の時の命令 roll(50,-50) sleep(1) #1秒休み roll(250,250) break #実行を終了させる
ここは主に交差点を認知してからどう動くか、また止まるタイミングなどを指定させるために作った部分である。 ここで変数w,b,iというものが存在しているが、これは順に白の検知数、黒の検知数、交差点の検知数を表すものであり、白と黒をを認識した数を数えさせることで今ロボットが動いている場所が交差点であるのかということや、今いる交差点は何個目のものであるのかを数え、それにより交差点を検知した後に実行する命令を検知した回数によって変えさせるために変数iを用意した。
このwとbというのは交差点をみわけるために使われた。wは今回あまり効果を発揮していないが、黒の値が33、つまり連続してロボットが黒を認識したときのみロボットが停止する仕様になっている。しかしどのように調整しても交差点以外のところでロボットが停止してしまった。
このことから新しい変数s,iを作成した。 sという変数は時間を表す。このsは交差点を認知した瞬間から計られ、この時間の範囲内はbの値が常に0となり普通の道を間違えて交差点だと検知しないように設定されている。
iは前述のとおり交差点の個数を数えるもの。交差点から交差点までの距離が一律でないので交差点を認識しないように交差点の場所によってどのように進んでもある程度の時間は交差点と認識しないようになった。
今回ライントレースをやってみて一番難しいと感じたところは交差点を認識する部分だった。この交差点を認識する条件が厳しいと認識せずに進んでいってしまうし、逆に条件が易しすぎると交差点でない普通の直線やカーブを認識してしまったのでそこの調整は結構長い時間をかけてやったと思う。
反省点としてまず挙げられるのは、ロボットの進ませ方である。発表の際にほかのグループと比べ右や左にそれたり、途中で止まりながら動いたりとぎこちない動きになってしまった。もっと条件分岐やrun_foreverを多くつけてまっすぐ動くところを増やしていけばよかったと思う。
今回ボールを投げるところでは一回転して缶を見つけてからその位置に逆回転してボールを投げるという形で投擲を行ったが、ボールを投げる際に缶が見つからなかったらもう一回最初から探し直していた。確かにこれで確実当たるようにはなるのだが、一回探すごとにいちいち最初からやり直してしまっていたため無駄に時間がかかってしまっていた。それを防ぐために二回目から範囲を限定して探すようにしてから投げればもっと時間がかからずなおかつ精度もあげられるかもしれないと思った。
総計:500 今日:1 昨日:0