[[2017a/Member]]
目次
#contents
*1,課題について [#l925a7ee]
**課題2 概要 [#i0138537]
黒い線に沿って動くロボットの作成。詳しくは[[課題2>2017a/Mission2]]を参照。
**走行コース [#lbc49625]
今回の課題ではコースが二種類あり、パートナーとは別のコースを走行するという指定があった。私はE地点右折コースを担当した。
*2,本体について [#i2f6deac]
本体制作では、過去の先輩のものを参考に三輪車タイプなどを試してみたが、写真のようなシンプルなデザインが一番しっくりきた。センサーとタイヤの位置関係は、近すぎると前進できなくなってしまい、離れすぎるとカーブを曲がり切れないので、ちょうどいい位置を探した。バランスを保つために支えが必要だったため、摩擦の少ない丸いパーツを採用した。光センサは地面から離れすぎるとセンサーからの光以外も入り込んでしまい感度が悪くなる。
&ref(./kadai2zenntai.jpg,80%,ロボ全体); &ref(./kadai2ura.jpg,80%,ロボ裏面);
*3,プログラムについて [#o3a9b4c4]
今回のプログラムは演算子についての理解が不十分だったため、使い方が間違っている箇所がある。実際に走らせていないプログラムは掲載できないルールなので訂正できないが、演算子を複数用いる場合は()でくくる必要がある。また、演算子をあまり用いずにサブルーチンや入れ子構造にする方が望ましい。
**マクロ [#n4d4e06f]
メインタスクで使われるものをあらかじめ定義した。
#define ikiti 42 //閾値
#define distinction 28 //交差判別のための時間
#define nasi PlaySound (SOUND_UP); //停止なし
#define ari PlaySound (SOUND_DOWN); Off(OUT_AC); until(FastTimer(0) >= 125); //停止あり
#define susumu OnFwd(OUT_AC); //前進
#define susumu_a OnFwd(OUT_AC); ClearTimer(0); //前進し、タイマーリセット
#define susumu_b(t) OnFwd(OUT_AC); until(FastTimer(0) >= (t)); //前進する時間を指定
#define sennkai_right OnFwd(OUT_A); OnRev(OUT_C); //右旋回
#define sennkai_right_a OnFwd(OUT_A); OnRev(OUT_C); ClearTimer(0); //右旋回し、タイマーリセット
#define sennkai_right_b(t) OnFwd(OUT_A); OnRev(OUT_C); until(FastTimer(0) >= (t));//右旋回する時間を指定
#define turn_right OnFwd(OUT_A); Off(OUT_C); //右に曲がる
#define turn_right_a OnFwd(OUT_A); Off(OUT_C); ClearTimer(0); //右に曲がり、タイマーリセット
#define turn_right_b(t) OnFwd(OUT_A); Off(OUT_C); until(FastTimer(0) >= (t));//右に曲がる時間を指定
#define sennkai_left OnFwd(OUT_C); OnRev(OUT_A); //左旋回
#define sennkai_left_a OnFwd(OUT_C); OnRev(OUT_A); ClearTimer(0); //左旋回し、タイマーリセット
#define sennkai_left_b(t) OnFwd(OUT_C); OnRev(OUT_A); until(FastTimer(0) >= (t));//左旋回する時間を指定
#define turn_left OnFwd(OUT_C); Off(OUT_A); //左に曲がる
#define turn_left_a OnFwd(OUT_C); Off(OUT_A); ClearTimer(0); //左に曲がり、タイマーリセット
#define turn_left_b(t) OnFwd(OUT_C); Off(OUT_A); until(FastTimer(0) >= (t));//左に曲がる時間を指定
**メインタスク [#l20ad699]
task main()
{
int n=0; //何回目の交差点か判別
SetSensor (SENSOR_2, SENSOR_LIGHT);
SetPower(OUT_AC,2); //トレースできる速度に調整
while (n<=15){ //15回目でプログラムが終了
if (n==0||n==1||n==5||n==10||n==13){
susumu;
} else if (n==2||n==4||n==11){
turn_left;
} else if (n==3||n==7||n==12||n==14){
turn_right;
} else if (n==6){
sennkai_left;
}
until(SENSOR_2 > ikiti+6); //端を見つけるまで回転(↑の条件はそれぞれの回転の方法)
ClearTimer (0); //交差点判別に用いるタイマー
if (n==7){ //急カーブゾーン前半で線を横断するタイミングを計るためのタイマー
ClearTimer(1);
}
else if (n==8) { //急カーブゾーン後半で線を横断するタイミングを計るためのタイマー
ClearTimer(2); susumu; until(SENSOR_2 > ikiti+6);//完全に線を渡るために前進
}
else if (n==9){ //急カーブゾーンでの補正
susumu; until(SENSOR_2 > ikiti+6);//完全に線を渡るために前進
}
while (FastTimer(0) <= distinction) {//交差点判別の値よりも短い間は繰り返す
if (SENSOR_2 > ikiti+8) { //白
if (n==0||n==5||n==6||n==9||n==13||n==14) {//右側トレース
sennkai_left_a;
} else if (n==7&&FastTimer(1) <= 580) {
sennkai_left_a;
} else if (n==8&&FastTimer(2) > 850) {
sennkai_left_a;
} else if ( n==1||n==2||n==3||n==4||n==10||n==11||n==12){//左側トレース
sennkai_right_a;
} else if (n==7&&FastTimer(1) > 600 ){
sennkai_right_a;
} else if (n==8&&FastTimer(2) <= 830){
sennkai_right_a;
}
} else if (SENSOR_2 > ikiti+5) { //少し白
if (n==0||n==5||n==6||n==9||n==13||n==14) {//右側トレース
turn_left_a;
} else if (n==7&&FastTimer(1) <= 580 ) {
turn_left_a;
} else if (n==8&&FastTimer(2) > 850) {
turn_left_a;
} else if ( n==1||n==2||n==3||n==4||n==10||n==11||n==12){//左側トレース
turn_right_a;
} else if (n==7&&FastTimer(1) > 600 ){
turn_right_a;
} else if (n==8&&FastTimer(2) <= 830) {
turn_right_a;
}
} else if (SENSOR_2 > ikiti) { //中間
susumu_a;
} else if (SENSOR_2 > ikiti-3) { //少し黒
if (n==0||n==5||n==6||n==9||n==13||n==14) {//右側トレース
turn_right_a;
} else if (n==7&&FastTimer(1) <= 580 ) {
turn_right_a;
} else if (n==8&&FastTimer(2) > 850) {
turn_right_a;
} else if ( n==1||n==2||n==3||n==4||n==10||n==11||n==12) {//左側トレース
turn_left_a;
} else if (n==7&&FastTimer(1) > 600 ){
turn_left_a;
} else if (n==8&&FastTimer(2) <= 830) {
turn_left_a;
}
} else { //黒
if (n==0||n==5||n==6||n==9||n==13||n==14) {//右側トレース
sennkai_right;
} else if (n==7&&FastTimer(1) <= 580 ) {
sennkai_right;
} else if (n==8&&FastTimer(2) > 850) {
sennkai_right;
} else if ( n==1||n==2||n==3||n==4||n==10||n==11||n==12) {//左側トレース
sennkai_left;
} else if (n==7&&FastTimer(1) > 600 ) {
sennkai_left;
} else if (n==8&&FastTimer(2) <= 830) {
sennkai_left;
}
}
Wait(1);
if (n==7&&FastTimer(1) > 580){ //線を横断する(タイマーの値が580の時にカーブ手前の中心辺りで横断できる)
turn_left; until(FastTimer(1)>= 600);//タイマーの値が600になるまで回転することでロボの光センサーを線の逆側まで移動させる
}else if (n==8&&FastTimer(2) > 830){//線を横断する(タイマーの値が830の時が中心辺りになる)
turn_right; until(FastTimer(2) >= 850);//タイマーの値が850になるまで回転させて線の逆側へ移動
}
}
n++;//交差点のカウント
if (n==1||n==3||n==4||n==7||n==10||n==12||n==13||n==14){
nasi; //停止なし
if (n==3||n==12){ //ロボの向きを整える(場所によって傾き方が違うため条件で向きの整え方を分けている)
turn_right_b(60);
} else if (n==4) {
turn_left_b(70);sennkai_right_b(90);
} else if (n==7||n==13||n==14){
turn_right_b(90);sennkai_left_b(100);
} else if (n==1||n==10){
susumu_b(60);
}
} else if (n==2||n==5||n==6||n==11||n==15){
ari; //停止あり
if (n==2){ //ロボの向きを整える(場所によって傾き方が違うため条件で向きの整え方を分けている)
sennkai_left_b(145);
} else if (n==5){
turn_right_b(245);
} else if (n==6){
susumu_b(145);
} else if (n==15){
Off(OUT_AC); //停止する
}
}
}
}
*説明 [#d0428d60]
#ref(./ライントレース図.jpg,80%,ライントレースの図)
**ライントレースについて [#bbeee5cf]
センサの読み取る値を白、少し白、中間、少し黒、黒の5段階に分け、黒と白では旋回、少し黒、白では曲がる、中間では直進としている。授業で光センサの値を五段階に分けてトレースする方法を学んだが、この方法では線の片側しかたどれず交差点や直角のカーブを片方しか曲がれない。そこで、線の両側を行ったり来たりするようなプログラムを考えた。どちら側を走るのかは、交差点判別で用いる変数nを使った。
**交差点判別について [#ea77c374]
変数nで何番目の交差点なのかを認識させている。nの値と場所の関係は図に示したとおりである。黒の値をとる時間の長さでnをカウントさせた。交差点以外では黒に差し掛かり、タイマーで計る時間がdistinctionの値より小さいためwhileのループから抜け出さずnはカウントされない。
**急カーブについて [#rd58a7c0]
今回最大の難関は連続の急カーブ地点であったと思う。ここを外回りで回るためにタイマーを使用して、一つ目のカーブを曲がり終えたところ�┐農�を横断し、二つ目のカーブを曲がり終えたところ��で再び横断するようにした。二つ目のカーブで横断させたのは、次のカーブ��が右折だったためである。
*4,遭遇した問題と解決策 [#b284beea]
タイマーとwaitを同時に用いると制御が難しくなるということを学んだ。最初は原因が分からなかったがwaitで待っている間もタイマーが進んでいるのではないかと思い、waitをuntil(タイマーの値)で制御するようにしたところ解決した。センサーの値選びにもかなり苦労した。右左に回る値の範囲を広くとると交差点を認識せずに通り過ぎてしまい、直進の値を広くとると交差点ではない場所で旋回の勢いが強くなり黒線に入り込んで交差点だと誤認してしまうという問題が生じた。解決策として、交差点を判別してくれないとどうしようもないので直進のとる値の範囲を広くし、黒線に入り込むのを防ぐために曲がった後にロボの進行する向きを整えるプログラムを入れた。曲がり終えた後に、向きを線に沿ってまっすぐにすることで解決した。プログラム送信時のエラーで解決に時間がかかったもので、()の対応、マクロに引数を付けるとき名前の後ろの(t)の付け忘れ、タイマーの番号の付け忘れなどがあった。一度自分で間違えると気を付けなければならないポイントを覚えるので、他の人のエラーの原因も見つけられるようになった。
*5,まとめ [#dd20913c]
**反省点 [#n49fc7f9]
正確性がやや怪しかった。原因としては書いた線が薄かったこと、光センサが地面スレスレではなかったことなどが考えられる。改善しようとも考えたが、線の濃さや光センサの位置を変えると値も変わってしまいロボットが正常に動かなくなってしまった。プログラムを書き換えれば直せたかもしれないが、原因に気づいた時には私もパートナーもほぼ完成していて書き換える時間がなかったため、今回の反省点である。また、急カーブを常に外回りで回るプログラムの部分で少しプログラムが複雑になってしまったようにも思える。自分ではわかりやすいプログラムだと思っていたが、第三者から見るとわかりにくいプログラムだったのだと終わった後に気づいた。自分の主観だけで考えすぎずに、ほかの人が見るとどう思うのかまで考えてプログラミングをしなければならないのだと学んだ。論理積や論理和は二つ演算子だということを知らずに使っていた。||はまたはという意味だけだと思い込んで使っていたが演算子の起源までさかのぼって勉強しなければならいのだと知った。
**感想 [#l9ad0ae2]
勉強した上でプログラムの製作に取りかったが、うまくいかないことがたくさん出てきた。今回のプログラムは反省点の多いものだった。なぜ、自分のプログラムではいけないのかが理解できたので、次にプログラムを作るときには同じ失敗をしないよう気を付けようと思う。特に、一般的な論理的にわかりやすいプログラムを製作することの重要性を学んだ。反省すべき点は多いが、遭遇した問題点の原因を自分で考え解決していくことができたことはよかったと思う。難しい問題ほど乗り越えた時の達成感は大きかった。