#author("2020-02-14T12:30:24+09:00","ameame","ameame") #author("2020-02-14T12:32:19+09:00","ameame","ameame") [[2019b/Member]] #contents *課題3 ボール運び [#d6beec22] コースやルールは[[こちらのページ>2019b/Mission3]]にある.~ 4人で1つの課題に挑んだ.グループは3N4Nである. *成果 [#z4b650fd] 基礎点0点,技術点の平均14点で合計点14点であり,順位は3位であった. *ロボットについて [#r7c8b892] 2つのロボットを作った. **ロボット�� [#k0543428] #ref(3-ロボ1.jpeg) ボールを缶ごとコースの真ん中まで運んでくるロボット.モーター3つと光センサがついている.2つのモーターでタイヤを回し,もう1つはアームを動かす.アームは350mlのアルミ缶が3つ入る長さになっている.アームは2本の棒が平行につけられており片側の棒がロボット側の端を中心にして回転し上に上がるようになっている.缶を集める際時計回りで集めるため上げる棒は片側だけで構わない.光センサはライントレースに使う. **ロボット�� [#h9d0d78d] #ref(3-ロボ2.jpeg) 缶の上に乗ったボールを集め,缶の上に置くロボット.モーター3つと光センサ,超音波センサが1つずつついている.モーター2つはタイヤを回し,もう1つは上部の卍の形をしたパーツを回している.光センサはライントレースと缶の接近を感知し,超音波センサは缶との距離を測るのに使う.光センサで缶の接近を感知する方法は後述する. #ref(manzi.jpeg) 写真の卍の形をしたパーツの左下の部分にアルミ缶がはまり,卍の形をしたパーツが反時計回りに回転することで.ボールを集める.ボールを3つ集めた後は同方向にパーツを回転させることで缶にボールを置くことができる. #ref(ギヤ.jpeg) モーターから卍の形をしたパーツへは写真のようにギヤを組み合わせ動力を伝えている.この写真ではセンサの位置が変わっているが,本番ではロボット�△�1番上の写真の位置にセンサがついていた. #ref(touchLight.jpeg) 光センサで缶の接近を感知する仕組みは写真のようになっている.BのパーツとCのパーツは軸でつながっているためBが缶によってロボットの方に押されるとCが光センサの下に入り感知する.またBが押されたときギヤでつながっているAが上に持ち上がるため,Bを押している力が無くなるとB,Cは元の位置へ戻る.超音波センサは近距離のものや缶のように曲面になっているものとの距離を正確に測れなかったことと,タッチセンサでは反応せずそのまま缶を押してしまうことからこの仕組みを作った. *プログラムについて [#s4279a79] 私の班では4人それぞれプログラムを作り,動かして,1番うまくいった人のプログラムを本番で使うという方法を取った.本番で使ったのは私のプログラムでは無く,私のプログラムではうまくボールを缶の上に乗せることはできなかった.よって後に書くプログラムの説明は実際の動きではなく私の想定した動きである. **ロボット�,離廛蹈哀薀� [#cc7f68a8] #define TIME1 130 //この時間を超えて黒の部分にいると十字路と判断する #define SPEED 50 //ロボットのベースとなる速さ #define centor 40 //白と黒の境目の光センサの値 const float diameter=5.5; //タイヤの直径 const float distance=12.5; //タイヤ間の距離 const float pi=3.14; //円周率 #define limit 40 //ライントレース時の速さの制限 #define CONN 1 //通信するNXTの番号 #define SIGNALON 11 //通信先のNXTを動かすための番号 float CmToDeg(int cm) { int Tiredeg=360*cm/(diameter*pi); return Tiredeg; } float DegToDeg(int deg) { int TireDeg=deg*distance/diameter; return TireDeg; } void change_line(int curve){ while(SENSOR_4>centor-10){ OnRev(OUT_B,50*(-curve/abs(curve))); OnFwd(OUT_C,50*(-curve/abs(curve))); } } void crossroad(int d,int t) { if(d>0){RotateMotorEx(OUT_BC,20,DegToDeg(d),100,true,true);} if(d<0){RotateMotorEx(OUT_BC,20,DegToDeg(-d),-100,true,true);} OnFwd(OUT_BC,40); Wait(t); Off(OUT_BC); } void follow_line(int curve,int slowdown) { int a=(curve/25)*(SENSOR_4-centor); int vb=(SPEED-a)/(slowdown/10); int vc=(SPEED+a)/(slowdown/10); if(vb>limit){vb=limit;} if(vc>limit){vc=limit;} if(vb<-limit){vb=-limit;} if(vc<-limit){vc=-limit;} OnFwd(OUT_B,vb); OnFwd(OUT_C,vc); } void follow_linePlus(int CURVE,int SLOWDOWN,long tmin,long tmax) { long t0=CurrentTick(); long tb=CurrentTick(); while(CurrentTick()-tb<TIME1 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); if(SENSOR_4>centor-5 || CurrentTick()-t0<tmin){tb=CurrentTick();} } Off(OUT_BC); } task main() { SetSensorLight(S4); follow_linePlus(100,20,2000,10000); crossroad(10,0); RotateMotor(OUT_C,30,2*DegToDeg(85)); RotateMotor(OUT_A,30,50); follow_linePlus(100,20,100,10000); crossroad(10,100); follow_linePlus(100,20,10,10000); crossroad(-20,0); follow_linePlus(100,20,2000,2000); RotateMotorEx(OUT_BC,-40,CmToDeg(20),0,true,true); RotateMotor(OUT_A,-40,49); SendRemoteNumber(CONN,MAILBOX1,SIGNALON); RotateMotorEx(OUT_BC,-40,CmToDeg(20),0,true,true); } **ロボット�△離廛蹈哀薀� [#yd621489] #define TIME1 120 //この時間を超えて黒の部分にいると十字路と判断する #define SPEED 50 //ロボットのベースとなる速さ #define centor 40 //白と黒の境目の光センサの値 const float diameter=5.5; const float distance=12.5; const float pi=3.14; #define limit 60 #define SIGNALON 11 #define TIME1 130 //この時間を超えて黒の部分にいると十字路と判断する #define SPEED 50 //ロボットのベースとなる速さ #define centor 40 //白と黒の境目の光センサの値 const float diameter=5.5; //タイヤの直径 const float distance=12.5; //タイヤ間の距離 const float pi=3.14; //円周率 #define limit 40 //ライントレース時の速さの制限 #define SIGNALON 11 //通信先のNXTを動かすための番号 float CmToDeg(int cm) { int TireDeg=(360*cm)/(diameter*pi); return TireDeg; } float DegToDeg(int deg) { int Tiredeg=deg*distance/diameter; return Tiredeg; } void change_line(int curve) { while(SENSOR_2>centor-10){ OnRev(OUT_B,50*(-curve/abs(curve))); OnFwd(OUT_C,50*(-curve/abs(curve))); } } void follow_line(int curve,int slowdown) { int a=(curve/25)*(SENSOR_2-centor); int vb=(SPEED-a)/(slowdown/10); int vc=(SPEED+a)/(slowdown/10); if(vb>limit){vb=limit;} if(vc>limit){vc=limit;} if(vb<-limit){vb=-limit;} if(vc<-limit){vc=-limit;} OnFwd(OUT_B,vb); OnFwd(OUT_C,vc); } void follow_linePlus(int CURVE,int SLOWDOWN,long tmin,long tmax) { long t0=CurrentTick(); long tb=CurrentTick(); while(CurrentTick()-tb<TIME1 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); if(SENSOR_2>centor-5 || CurrentTick()-t0<tmin){tb=CurrentTick();} } } void fetch_ball(int CURVE,int SLOWDOWN) { int c=0; while(c<3){ while(SensorUS(S3)>6){ follow_line(CURVE,SLOWDOWN); } RotateMotorEx(OUT_BC,30,CmToDeg(6),0,true,true); RotateMotor(OUT_A,30,85); RotateMotor(OUT_B,-30,180); RotateMotor(OUT_B,30,180); c=c++; } RotateMotorEx(OUT_BC,-40,DegToDeg(180),100,true,true); } void find_can(int deg) { long tacho_min=0; int d_min=300; RotateMotorEx(OUT_BC,20,DegToDeg(deg)/2,100,true,true); ResetTachoCount(OUT_BC); OnFwdSync(OUT_BC,20,-100); while(MotorTachoCount(OUT_B)<=DegToDeg(deg)){ if(SensorUS(S3)<d_min&&SensorUS(S3)>10){ d_min=SensorUS(S3); tacho_min=MotorTachoCount(OUT_B); } } OnFwdSync(OUT_BC,20,100); until(MotorTachoCount(OUT_B)<=tacho_min); Wait(14); Off(OUT_BC); } void put_ball(int tmax) { long T0=CurrentTick(); OnFwdSync(OUT_BC,20,0); until(SENSOR_2<35||CurrentTick()-T0>tmax); Off(OUT_BC); long T=CurrentTick()-T0; RotateMotor(OUT_A,20,87); OnRevSync(OUT_BC,20,0); Wait(T); Off(OUT_BC); } task main() { SetSensorLight(S2); SetSensorLowspeed(S3); follow_linePlus(-100,20,6000,6000); change_line(50); int msg; while(msg!=SIGNALON){ ReceiveRemoteNumber(MAILBOX1,true,msg); } fetch_ball(100,20); change_line(-50); follow_linePlus(-100,20,0,10000); find_can(60); put_ball(5000); RotateMotorEx(OUT_BC,40,DegToDeg(90),-100,true,true); find_can(60); put_ball(5000); } **関数の説明 [#jb848e96] �´△剖δ未隆愎瑤砲弔い得睫世垢襦� ***CmToDeg [#gfe0d885] float CmToDeg(int cm) { int TireDeg=(360*cm)/(diameter*pi); return TireDeg; } 進みたい距離(cm)からタイヤの回転角度を計算する関数. ***DegToDeg [#c1982aee] float DegToDeg(int deg) { int TireDeg=deg*distance/diameter; return TireDeg; } ロボットの回転したい角度からタイヤの回転角度を計算する関数. ***change_line [#lc9bbbec] void change_line(int curve){ while(SENSOR_4>centor-10){ OnRev(OUT_B,50*(-curve/abs(curve))); OnFwd(OUT_C,50*(-curve/abs(curve))); } } ライントレースの沿う側を変更する関数.センサーがラインの外にあるときに沿う側を変えてしまうとコースアウトしてしまうため一度ラインの上に戻さなくてはならない.そのため,センサーが白の部分の値を取る間その場で旋回する.~ 引数は1つあり,左から右に移る時は負の値,右から左の時は正の値を入れる. ***follow_line [#z31a8548] void follow_line(int curve,int slowdown) { int a=(curve/25)*(SENSOR_1-centor); //光センサの値と白黒の境目の値(centor)の差に比例する値 int vb=(SPEED-a)/(slowdown/10); //モータBの速さ int vc=(SPEED+a)/(slowdown/10); //モータCの速さ if(vb>limit){vb=limit;} //モータの速さを[-limit,limit]に制限 if(vc>limit){vc=limit;} if(vb<-limit){vb=-limit;} if(vc<-limit){vc=-limit;} OnFwd(OUT_B,vb); OnFwd(OUT_C,vc); } SPEED:ロボットのベースとなる速さ~ 光センサの値によって左右のモータの速さをそれぞれ-limit〜limitの間で1次関数的に変化させる関数.~ 引数は2種類あり,~ curve:ロボットの曲がりやすさを決める.''値の正負で沿うラインの左右が決まる''.~ slowdown:ロボットを全体的に減速させる.~ ***follow_linePlus [#e1c0e49f] void follow_linePlus(int CURVE,int SLOWDOWN,long tmin,long tmax) { long t0=CurrentTick(); long tb=CurrentTick(); while(CurrentTick()-tb<TIME1 && CurrentTick()-t0<tmax){ follow_line(CURVE,SLOWDOWN); if(SENSOR_2>centor-5 || CurrentTick()-t0<tmin){tb=CurrentTick();} } } ライントレースをする.経過時間がtmin以降で十字路につく,またはtmaxを超えると関数が終了する.~ 引数は4つあり,前2つはfollow_line内の値を決め,後2つは前文で説明したtminとtmaxを指定する. **ロボット�,隆愎� [#z9f31889] ロボット�,離廛蹈哀薀爐砲世瓜箸辰心愎瑤鮴睫世垢襦� ***crossroad [#s411b092] void crossroad(int d,int t) { if(d>0){RotateMotorEx(OUT_BC,20,DegToDeg(d),100,true,true);} if(d<0){RotateMotorEx(OUT_BC,20,DegToDeg(-d),-100,true,true);} OnFwd(OUT_BC,40); Wait(t); Off(OUT_BC); } 交差点などで停止した後ロボットの位置を調整するのに使う関数.指定した角度dだけ回転し,その後40%のスピードでt秒前進する.dは正負で回転方向を変えることができる. **ロボット�△隆愎� [#x496d63f] ロボット�△離廛蹈哀薀爐砲世瓜箸辰心愎瑤鮴睫世垢襦� ***fech_ball [#r97ab3b7] void fetch_ball(int CURVE,int SLOWDOWN) { int c=0; while(c<3){ //以下の動作を3回繰り返す while(SensorUS(S3)>6){ //超音波センサの値が6以上の間ライントレース follow_line(CURVE,SLOWDOWN); } RotateMotorEx(OUT_BC,30,CmToDeg(6),0,true,true); //6cm前進 RotateMotor(OUT_A,30,85); //アームを90度回転 RotateMotor(OUT_B,-30,180); //ロボットを左に向ける RotateMotor(OUT_B,30,180); //正面を向く c=c++; } RotateMotorEx(OUT_BC,-40,DegToDeg(180),100,true,true); //ロボットが180度反転 } 線上に縦一列に並んでいるボールを載せた缶からボールを集める関数.缶に近づくまでライントレースを行い,缶に近づいたらボールを回収する.その後ロボットを左に向けることで缶をどかし,つぎーの缶へ向かう.缶とボールは3つなので3回繰り返す.最後にボールを置きに行くために後ろを向く.ロボットの説明で紹介した近づいた缶を感知する仕組みはこのプログラムを書いた後に取り付けたためここでは使用しない. ***find_can [#o1a2aeb0] void find_can(int deg) { long tacho_min=0; //タイヤの回転角度を記憶するための変数 int d_min=300; //仮の最小値 RotateMotorEx(OUT_BC,20,DegToDeg(deg)/2,100,true,true); //指定した角度の半分回る ResetTachoCount(OUT_BC); OnFwdSync(OUT_BC,20,-100); //指定した角度回る while(MotorTachoCount(OUT_B)<=DegToDeg(deg)){ // if(SensorUS(S3)<d_min&&SensorUS(S3)>10){ //最小値の更新と角度の記憶 d_min=SensorUS(S3); // tacho_min=MotorTachoCount(OUT_B); // } } OnFwdSync(OUT_BC,20,100); //記憶した角度まで戻る until(MotorTachoCount(OUT_B)<=tacho_min); // Wait(14); Off(OUT_BC); } 最も近い缶を探す関数.仕組みは講義内で紹介されたものとほとんど同じである.引数の角度分ロボットの正面を探す.しかし超音波センサの不具合で稀に突然小さな値が出ることがあったため.最小値を更新する条件に超音波センサの値が10以上であることを加えた. ***put_ball [#nff4fbc4] void put_ball(int tmax) { long T0=CurrentTick(); //経過時間を計るための変数 OnFwdSync(OUT_BC,20,0); //缶が接近するまで前進する until(SENSOR_2<35||CurrentTick()-T0>tmax); // Off(OUT_BC); long T=CurrentTick()-T0; //前進した時間を記録 RotateMotor(OUT_A,20,87); //ボールを置く OnRevSync(OUT_BC,20,0); //前進した時間だけ後退 Wait(T); // Off(OUT_BC); // } find_canで缶を見つけた後,正面にある缶にボールをのせに行き,同じ位置に戻る関数.缶の接近は,ロボットの説明で紹介した,光センサを利用したもの,を使って判別している. **mainプログラムの説明 [#l7036e47] 全体の流れは~ ロボット�´�起動~ ↓~ ロボット�ヾ未鮟犬瓩襦ぅ蹈椒奪鉢�I´に移動する.~ ↓~ ロボット�ヾ未鮟犬畚�わり,缶から離れる~ ↓~ ロボット��缶からボールを回収する~ ↓~ ロボット��H´に移動する~ ↓~ ロボット��缶を探してボールを置く~ となり,具体的な説明はプログラムに直接記載する. #ref(roats.jpeg) task main() //ロボット1 { SetSensorLight(S4); follow_linePlus(100,20,2000,10000); //左側ライントレース(E,C,D) crossroad(10,0); //交差点Dで停止 RotateMotor(OUT_C,30,2*DegToDeg(85)); //右タイヤ中心に時計回りに90度回転 RotateMotor(OUT_A,30,50); //アームを閉じる follow_linePlus(100,20,100,10000); //左側ライントレース(D,G) crossroad(10,100); //交差点Gを直進 follow_linePlus(100,20,10,10000); //左側ライントレース(G,H,I) crossroad(-20,0); //交差点Hを左折 follow_linePlus(100,20,2000,2000); //左側ライントレース RotateMotorEx(OUT_BC,-40,CmToDeg(20),0,true,true); //位置調整で後退 RotateMotor(OUT_A,-40,49); //アームを上げる SendRemoteNumber(CONN,MAILBOX1,SIGNALON); //ロボット2起動 RotateMotorEx(OUT_BC,-40,CmToDeg(20),0,true,true); //回収の邪魔になるので後退 } アームは350mlアルミ缶3本分の約200mmあるため,Dで回転することでE,Fの缶を集めることができる.右タイヤを中心に回ることで集めながら缶がロボット側に寄って来る.位置調整の後退はロボット�△�F´の缶が触れないようにするためである. task main() //ロボット2 { SetSensorLight(S2); SetSensorLowspeed(S3); follow_linePlus(-100,20,6000,6000); //右側ライントレース(A´,J´,H´,I´) change_line(50); //左側に車線変更 int msg; while(msg!=SIGNALON){ //ロボット1からメッセージが来るのを待つ ReceiveRemoteNumber(MAILBOX1,true,msg); } fetch_ball(100,20); //ボールを回収し反転 change_line(-50); //右側に沿う follow_linePlus(-100,20,0,10000); //右側ライントレース(I´,H´) find_can(60); //C´の位置にある缶を探す put_ball(5000); //ボールを置きに行って戻る RotateMotorEx(OUT_BC,40,DegToDeg(90),-100,true,true); //F´の方向の辺りを向く find_can(60); //F´の位置にある缶を探す put_ball(5000); //ボールを置きに行って戻る } ロボット�△魯魁璽垢涼羶瓦妊棔璽襪魏鷦�した後H´に向かいそのときコースの写真の右側を向いているため初めにC´の缶を探す.その後またH´に戻り,90度時計回りに回りF´の缶を探す.ロボット本体が大きく交差点G´D´に進むことは缶を倒してしまう危険があったためE´の缶は諦めている.もし満点を狙うのならばE´の缶の左側から近づく必要がある.また最初のライントレースはI´付近で停止する予定だが,I´付近は目印が無く時間tmaxで停止する必要がある.プログラム内では6秒で設定しているがこの数字ではロボット�,抜馨弔靴討靴泙Δ里把汗阿垢詆�要がある. *まとめ [#cf1a0a02] ロボットに関してはかなり満足のいくものを作ることができた.前年度等の映像で見たものとは違ったものを作りたいという考えから横に回転するアームを作ったが缶との距離によって挙動が大きく変わり制御が大変であった.ロボットを組み立てながら同時にプログラムも作り,細かく挙動を確認しながらロボット,プログラムの修正を行うという形をとれればよかった.~ 自分のプログラムは未完成であった.ロボット�,亡悗靴討呂垢戮撞麁阿�確認できていたがロボット�△魯棔璽襪魏鷦�する部分のみであった.互いの連携やボールを置くプログラムは挙動を確認できていない.プログラムを作っている途中でNXTにプログラムが入らなくなってしまった.原因は容量不足でNXT内のプログラムを消すことで解決したが,大きく時間を取られてしまった.そういったアクシデントも想定して予定を立てるのが重要なのだと学んだ.