#author("2020-02-15T03:35:06+09:00","Nishi","Nishi")
#author("2020-02-15T03:36:41+09:00","Nishi","Nishi")
[[2019b/Member]]

目次
#contents

*課題 [#se26b6b2]
**ルート [#u475520c]
#ref(./2019b-mission2.png,65%);
+ A地点から出発
+ B
+ C (直進)
+ D (一時停止の後、直進)
+ E, F 通過
+ G (一時停止の後、左折)
+ H (一時停止の後、左折)
+ I (ボール or キューボイドをつかんでUターン)
+ H (直進)
+ J (一時停止)
+ A地点に入る(ゴール)

**コース作り [#c443b33e]
- 黒い線をテープで作ると光が反射し、光センサーの感知に影響を与える可能性があったため、油性マジックを使用した。
- 交差点や曲がり角を示すアルファベットを黒色で太く書いたため、場所によってはロボットに誤作動を招くことがあった。特に「H」の妨害が目立ち、手を加える羽目になった。

*ロボット [#rdb86612]

**ロボットの説明 [#a82bdc20]

#ref(./IMG_3690.jpg,12%);
 今回のロボットにはモーター3つ、超音波センサー、光センサーが搭載されている。2つのモーターはタイヤに、残り1つのモーターはボールを捉える囲いを上下させるために使用されている。

#ref(./IMG_3692.jpg,10%);
 製作の初期段階では2つのアームに挟む形でボールを掴もうとしていた。しかし構想が浮かばず断念し、囲いを上下することで捉えるシンプルな方法を選択した。ボールの位置は超音波センサーで確認し、見つけるようになっている。
#ref(./IMG_3696.jpg,10%);
 光センサーは前輪の前に設置されており、ラインを識別するために使用されている。

**ロボットの特徴 [#nf54064f]
***ロボットが転倒しにくいようした [#qeddd225]
 ボールを捉えるための囲いを下ろすと、前側に使用するパーツが多いこともあって重心がかなり前に寄り、ロボットが進行するときにふらつくことがあった。そのため、重心をできる限り後ろに寄せた。また、ロボットが高さもつことも不安定になる要因であるため、重心を下げるようにもした。
***光センサーをなるべく下に近づけた [#q8d721ad]
 光センサーが紙面から離れていると、白と黒での明るさの値の差が小さくなり、細かく指定することが困難になった。そのため、密着しすぎない程度に近づけるようにした。

*プログラム [#za36c1d9]
**プログラム全体 [#b511c31e]
 #define WHITE 54    // 白色
 #define GWHITE 48   // 白灰色
 #define GBLACK 44   // 黒灰色
 #define BLACK 34    // 黒色
 #define SPEED_H 45  // ハイスピード
 #define SPEED 35    // ノーマルスピード
 #define SPEED_L 25  // ロースピード
 #define STEP 1
 #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL);
 #define go OnFwdSync(OUT_BC,SPEED_H,0);               // 直進
 #define gofwd OnFwdSync(OUT_BC,SPEED_L,0);            // 直進(スロー)
 #define left_rotation_s OnRL(SPEED,-SPEED);           // 高速左回転
 #define left_rotation OnRL(SPEED_L,-SPEED_L);         // 左回転
 #define turn_left Off(OUT_C);OnFwd(OUT_B,SPEED);      // 左旋回
 #define turn_right Off(OUT_B);OnFwd(OUT_C,SPEED);     // 右旋回
 #define right_rotation OnRL(-SPEED_L,SPEED_L);        // 右回転
 
 float GetAngle(float d)
 {
     const float diameter = 5.45;
     const float pi=3.1415;
     const float distance=12.5;
     float ang = (distance*d)/diameter;
     return ang;
 }
 
 void u_turn()
 {
     int angle = GetAngle(90.0);
     RotateMotorEx(OUT_BC,SPEED,angle,100,true,true);
 }
 
 void back(long b)
 {
     RotateMotorEx(OUT_BC,-SPEED,b,0,true,true);
 }
 
 void follow_line()
 {
     SetSensorLight(S3);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < 200){
         if(SENSOR_3 <= BLACK){
             left_rotation
         }else if((BLACK < SENSOR_3) && (SENSOR_3 < GBLACK)){
             turn_left
         }else if((GBLACK <= SENSOR_3) && (SENSOR_3 <= GWHITE)){
             go
             t0 = CurrentTick();
         }else if((GWHITE < SENSOR_3) && (SENSOR_3 < WHITE)){
             turn_right
             t0 = CurrentTick();
         }else if(WHITE <= SENSOR_3){
             right_rotation
             t0 = CurrentTick();
         }
         Wait(STEP);
     }
     Off(OUT_BC);
 }
 
 void follow_line_for_intersection(long t)
 {
     SetSensorLight(S3);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < t){
         if(SENSOR_3 <= BLACK){
             left_rotation_s
         }else if((BLACK < SENSOR_3) && (SENSOR_3 < GBLACK)){
             turn_left
         }else if((GBLACK <= SENSOR_3) && (SENSOR_3 <= GWHITE)){
             go
         }else if((GWHITE < SENSOR_3) && (SENSOR_3 < WHITE)){
             turn_right
         }else if(WHITE <= SENSOR_3){
             right_rotation
         }
         Wait(STEP);
     }
     Off(OUT_BC);
 }
 
 void fetch_ball()
 {
     SetSensorLowspeed(S1);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < 100){
         if(SensorUS(S1) <= 10){
             RotateMotor(OUT_A,-SPEED,70);
         }else{
             gofwd
             t0=CurrentTick();
         }
     }
     Off(OUT_BC);
 }
 
 task main()
 {
     RotateMotorEx(OUT_BC,SPEED_H,180,0,true,true);
     follow_line();
     Wait(1000);
 
     RotateMotor(OUT_C,SPEED,60);
     follow_line();
     Wait(1000);
 
     follow_line_for_intersection(2000);
     follow_line();
     Wait(1000);
 
     follow_line_for_intersection(1000);
     back(120);
     fetch_ball();
     u_turn();
 
     follow_line_for_intersection(15000);
     follow_line();
     Wait(1000);
 
     turn_right
     Wait(200);
     Off(OUT_BC);
     go
     Wait(1000);
     Off(OUT_BC);
 }

**プログラムの説明(定義,関数など) [#l67fe0b2]
***定義 [#rb11c2e0]
 #define WHITE 54    // 白色
 #define GWHITE 48   // 白灰色
 #define GBLACK 44   // 黒灰色
 #define BLACK 34    // 黒色
 #define SPEED_H 45  // ハイスピード
 #define SPEED 35    // ノーマルスピード
 #define SPEED_L 25  // ロースピード
 #define STEP 1
 #define OnRL(speedR,speedL) OnFwd(OUT_B,speedR);OnFwd(OUT_C,speedL);
 #define go OnFwdSync(OUT_BC,SPEED_H,0);               // 直進
 #define gofwd OnFwdSync(OUT_BC,SPEED_L,0);            // 直進(スロー)
 #define left_rotation_s OnRL(SPEED,-SPEED);           // 高速左回転
 #define left_rotation OnRL(SPEED_L,-SPEED_L);         // 左回転
 #define turn_left Off(OUT_C);OnFwd(OUT_B,SPEED);      // 左旋回
 #define turn_right Off(OUT_B);OnFwd(OUT_C,SPEED);     // 右旋回
 #define right_rotation OnRL(-SPEED_L,SPEED_L);        // 右回転
 今回は曲がり方に種類があり、値の範囲指定をするプログラムも多数存在したため、なるべく定義し、名称もつけることで見やすく、変更しやすいようにした。
 直進のプログラムを定義するとき、OnFwd, RotateMotor(Ex)ではなくOnSyncを使用した。OnFwdだと電池の残量や重心の偏りなどにより、微妙に左右に曲がることがあった。また、角度を指定するRotateMotor(Ex)だと、while,ifの部分で使用する場合においてはなめらかに動かすことができなかった。
***Uターン [#y0c1f6da]
 float GetAngle(float d)
 {
     const float diameter = 5.45;
     const float pi=3.1415;
     const float distance=12.5;
     float ang = (distance*d)/diameter;
     return ang;
 }
 
 void u_turn()
 {
     int angle = GetAngle(90.0);
     RotateMotorEx(OUT_BC,SPEED,angle,100,true,true);
 }
 方向転換したい角度からタイヤの回転角度を計算し、Uターンするためのプログラムに使用されている。
***後退 [#me0d75b4]
 void back(long b)
 {
     RotateMotorEx(OUT_BC,-SPEED,b,0,true,true);
 }
 タイヤの回転角度を入力し、その分真っ直ぐ後退する。
***ライントレース(交差点感知)[#xfba2e64]
 void follow_line()
 {
     SetSensorLight(S3);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < 200){
         if(SENSOR_3 <= BLACK){
             left_rotation
         }else if((BLACK < SENSOR_3) && (SENSOR_3 < GBLACK)){
             turn_left
         }else if((GBLACK <= SENSOR_3) && (SENSOR_3 <= GWHITE)){
             go
             t0 = CurrentTick();
         }else if((GWHITE < SENSOR_3) && (SENSOR_3 < WHITE)){
             turn_right
             t0 = CurrentTick();
         }else if(WHITE <= SENSOR_3){
             right_rotation
             t0 = CurrentTick();
         }
         Wait(STEP);
     }
     Off(OUT_BC);
 }
 ラインの左縁を進み、交差点を感知したときに停止する。交差点は光センサーが黒色を感知する時間(今回ではwhileの括弧内の200という値にあたる)で判断するようになっている。最初にlong t0とし、t0 = CurrentTick()でサブルーチンの開始時刻を設定しており、またt0 = CurrentTick()が黒、黒灰色以外を感知した場合のプログラムに入り、時間をリセットする。そのためセンサーが黒、黒灰色を感知している間に限り時間が経過し、whileの括弧内で定められている時間が経過すると、このループから抜けて次のプログラムへと移行する。
 今回、黒と判定する値の幅が狭いと交差点と認識せず左折し、ラインの左縁を進み続けた。そのため、交差点を感知する際、黒色と認識する値の幅を広くとった。

***ライントレース(交差点無視) [#l899b8be]
 void follow_line_for_intersection(long t)
 {
     SetSensorLight(S3);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < t){
         if(SENSOR_3 <= BLACK){
             left_rotation_s
         }else if((BLACK < SENSOR_3) && (SENSOR_3 < GBLACK)){
             turn_left
         }else if((GBLACK <= SENSOR_3) && (SENSOR_3 <= GWHITE)){
             go
         }else if((GWHITE < SENSOR_3) && (SENSOR_3 < WHITE)){
             turn_right
         }else if(WHITE <= SENSOR_3){
             right_rotation
         }
         Wait(STEP);
     }
     Off(OUT_BC);
 }
 時間を入力し、その間は光センサーが黒色を長時間感知しても停止することなく、ラインの左縁を進行する。
 時間を入力し、その間は光センサーが黒色を長時間感知しても停止することなく、ラインの左縁を進行する。基本的には『ライントレース(交差点感知)』と変わらない。
***アーム [#p87fb86b]
 void fetch_ball()
 {
     SetSensorLowspeed(S1);
 
     long t0;
     t0 = CurrentTick();
     while(CurrentTick()-t0 < 100){
         if(SensorUS(S1) <= 10){
             RotateMotor(OUT_A,-SPEED,70);
         }else{
             gofwd
             t0=CurrentTick();
         }
     }
     Off(OUT_BC);
 }
 前進し、超音波センサーでボールがあることによる距離の変化を読み取り、その後囲いを下ろすようになっている。今回、感知する物体が球体で、超音波センサーが距離の変化を感じとる時間が短いため、0,1秒間の感知でプログラムが進行するようになっている。
**プログラムの説明(task main) [#b0475cf3]
***区間A-B-C-D [#x98df0b4]
 RotateMotorEx(OUT_BC,SPEED_H,180,0,true,true);
 follow_line();
 Wait(1000);
 最初に直進してAの枠から出た後、ライントレースを開始する。そしてDの交差点まで進み、1秒間一時停止する。
***区間D-E-F-G [#g950ca41]
 RotateMotor(OUT_C,SPEED,60);
 follow_line();
 Wait(1000);
 Dの交差点で一時停止するとき、プログラム上若干左を向くため、右旋回を少しした後、ライントレースを開始する。今度はGの交差点まで進み、1秒間一時停止する。
***区間G-H [#r1a883f2]
 follow_line_for_intersection(2000);
 follow_line();
 Wait(1000);
 Gの交差点で一時停止した後、交差点を感知するライントレースをすると、光センサーが黒色を感知してしまい、即座に停止してしまう。そのためここでは、最初の2秒間は交差点を無視するライントレースをし、その後はまた元に戻し、Hの交差点で1秒間一時停止するようにした。
***区間H-I [#l27a81cf]
 follow_line_for_intersection(1000);
 back(120);
 fetch_ball();
 u_turn();
 Hの交差点から少しライントレースをし、まずは直線HIとロボットの向きが平行になるように調整をする。調整後はロボットがボールとの距離が近づきすぎてしまい、超音波センサーが感知できなくなる。そのため、一度少し下がってボールとの距離を適度に離し、再び前進することできちんと感知し、ボールを捉える。その後はUターンをし、戻るための向き調整をする。
***区間I-H-J [#b4c1829c]
 follow_line_for_intersection(15000);
 follow_line();
 Wait(1000);
 ボールを捉えた状態のまま、ライントレースを再開する。H-J間にある急なカーブでは、光センサが黒色を感知して交差点として認識することがあるため、最初の15秒間は交差点を無視するライントレースで進み、Jで1秒間一時停止する。
***区間J-A [#q16fa85f]
 turn_right
 Wait(200);
 Off(OUT_BC);
 go
 Wait(1000);
 Off(OUT_BC);
 Dの交差点のときと同様にJ地点では少々左を向いているため、少し右に向けた後直進してAの枠内へ帰還する。

*反省・改善点 [#q8bbd59a]
**ロボット面 [#g9ab0578]
- すぐに分解しにくい構造で持ち運びがしづらかった。
**プログラム面 [#o7c28231]
- 最初にアームの位置を正すプログラムを設定するべきだった。毎回開始するときにアームの調整が必要で手間がかかった。
- モーターのスピードを光センサーの値と比例させれば、スムーズに進行することが可能になった。

*まとめ [#e6fc06cb]
 今回は前回の反省を活かし、改行やスラッシュを使ってプログラムを見やすくすることができた。次回も今までの課題を踏まえた活動をしていきたい。

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS