今回は、4人で課題に取り組むことになった。ここに参加メンバーを記しておく。
・kiyomizu(私) ・suiden ・ ・ryu-abe
まず、課題2の内容やルールを確認したいと思う。今回は、ロボットコンテストをクラス全体で行うとのことでルールが前回の課題1より増え、新たに評価基準も設けられている。すべてをここに記すとレポートが煩雑になると考えられるため、以下に大まかなルールや内容のみ記しておく。
内容 1.下図のスタート地点にロボットの機体を置き、プログラムを実行する。(これ以降、機体に触れてはならない。) 2.図の左半分に置かれている紙コップ(番号が1から順に振られているもの)を右半分へ運搬する。 3.右半分に置かれている紙コップに、2で運搬してきた紙コップを重ねる。 4.どのような出来だったのか評価を受ける。
図1、課題2のコース
・ルール補足
(注1*左側に置かれる紙コップの位置はA,B,Cの地点である。
ただし、紙コップの順序は、競技直前にさいころを振った結果により決定される。
コップにはそれぞれ1から3までの番号が振られているので、コップの配置はある程度ランダムなものになる。
)
(注2*紙コップは通常の使用をする際に置く向きとは、逆向き{幅の広い方が下}に置かれている。紙コップを10cm以上動かしてしまった場合得点は0となる。)
(注3*評価は、まずコップを重ねる正確さ{同じ番号に重ねることができれば10点、番号が1つずれたならば2点、番号が2つずれたか重ねることができなかったならば0点}の得点{これを基本得点とする}を審判が決める。次に、そのロボットの動作や機体の美しさなどを他の全てのチームが20点満点で評価しその平均点{これを技術点とする}を求める。これら基本得点と技術点の合計点により評価を数値化し各チームで競い合う。)
上を見て、やるべきことを考えたところ、やはり課題1の時と同じように2段階の作業が必要であることがわかった。
1つ目、今回の課題をクリアするための能力を持ったロボットを作る。
2つ目、それを要求通りに動かすプログラムを作る。
以下、それらについて書きたいと思う。
さて、課題2をクリアするためにどのような能力が必要であろうか。私たちのグループは次の5つの能力が必要であると結論を出した。
・移動する能力 ・紙コップを検知する能力 ・紙コップをつかむ能力 ・紙コップを持ち上げる能力 ・紙コップを重ねる能力
<移動する能力>…紙コップを移動させ重ねるという課題であるが、フィールドがそこそこ広いので機体をスタート地点に置いたまま、なんらかの手段で(ロボットの腕をバカ長く作りそれを使って紙コップを操作するなど)課題達成を目指すというのは、無理があるだろうということになり、できるだけコンパクトかつフィールド上を動き回れる機体を制作しようということになった。図1がロボットの土台部分で、ここに機体の移動を司る車輪などが搭載されている。(また、部分的にライントレースが必要だと考えられたので土台部分にすでに光センサーが取り付けられている。)
<紙コップを検知する能力>…紙コップを検知する能力であるが、3つの案が挙がった。
1つ目、紙コップにタッチセンサーを当て、センサーが反応したかしなかったかで判断するというもの。これは、紙コップによって果たしてタッチセンサーが反応するのか(つまり、タッチセンサーが反応するには紙コップが軽すぎはしないか)、またタッチセンサーが反応するよううまくセンサーを加工できたとしてもコップを捕まえる機構の邪魔になるのではないかとの意見により却下された。
2つ目、超音波センサーを使って紙コップとの距離を測り、検知するというもの。これは、実現されれば考案されたものの中で一番確実にコップを補足できると考えられたが、超音波センサーそのものの取り扱いの難しさなどにより却下された。
3つ目、紙コップに何らかの模様をつけそれを光センサーで読み取り識別するというもの。これならば、紙コップにつける模様を工夫することによってある程度確実性を上げることも可能であり、比較的容易にできるのではということになり、3つ目すなわち、光センサーによる識別を採用することが決定した。なお、紙コップの模様は図2を参照のこと。
図2、コップの模様
<紙コップをつかむ、持ち上げる、重ねる能力>…開発初期段階では、つかむ能力と持ち上げる能力を別々にする案が出たが、コンピュータ同士の通信が必要となる(モーターの都合上、2台のコンピュータを使う必要が出てくるため)ので複雑になると考えられるこちらに対し、つかむ能力と持ち上げる能力を1つのモーターで賄うことが可能な機構が制作されたため、より簡単にできることをわざわざ複雑にする必要はないとの結論に至り、結果的に図3のような機構を採用することになった。 この機構の原型は私の制作したものである。が、少々大きすぎ、重すぎたため後にも述べているがロボットの異常な動作の原因になったと考えられ、suiden氏による軽量化を経て図3のようなすっきりとした形状になった。
原理を説明すると図5のように、はさむ機構がまず稼働し、掴んだ瞬間にこれまで稼働していた挟む機構の回転が停止し、代わりにこの機構全体の根本にある、モーターと直結した部分が動き(今までは、挟む機構に動力を伝えていたが、それが稼働しなくなることによりこの機構が動き出す)、持ち上げることができるようになるというわけである。言葉で説明するよりも実際に見た方が早いと思われるので図5も合わせて参照されたい。
図3、つかむ機構(まずモーターが付いている歯車が動き、1のアームが動く。次に2の歯車が効力を発揮しコップを持ち上げる。)
図4−1コップをつかみ
図4−2コップを持ち上げる
そんなこんなでできたのがこのロボット(図5)である。(実は、先にも述べた通り1回ロボットの機体も大幅に改修した結果ダイエットをし、このようになった。)
図5、完成機体
ロボットの機体ができたので、プログラムを組む。先ほど盛り込んだ機能を問題なく使えるようにすることを目指して組むことになった。したがって、あまり余計なものを足さないことに重きを置いている。
さて、実際のプログラムの内容であるが、以下の4つのプログラムを組むことになった。
・移動するプログラム ・ライントレースをするプログラム (先に機体を組むときに言及されていたある程度のライントレースが必要ではないかという意見から) ・コップを識別するプログラム ・コップをつかむ、保持する、持ち上げる、重ねるプログラム
<移動するプログラム>…ライントレースのみで移動すると移動中に紙コップを引っかける、コップの識別をした後に決定される進むルートで最短距離をとることができないなど好ましくないこと(得点を下げること)が多発しかねないので、ある程度の手動での移動調整が必要であると考えた。
<ライントレースをするプログラム>…モーターの出力調整のみで移動していくと必ず何らかのずれ(主にコースのずれ)が生じるため、正確な移動を実現するためにはライントレースが必要であり、今回の課題でもこの難題に挑むこととなった。
しかし、図7の施策型ロボットの段階では、ライントレースが全くうまくいかず、早くもこの時点で足止めを食うはめになった。誰の組んだライントレースのプログラムでも決してうまくいかなかったため、原因究明は困難を極めた。みなで考えたところ、プログラム(以前は同じ内容でも動いていたにもかかわらず、この機体で動かなくなり、しきい値を変えたり、機体の移動速度の調整をしたところでうまく稼働しなかった)ので、機体そのものに原因があるのではないかということになった。(最終的にこう判断した決め手は、ライントレース時のドリフトの発生、左タイヤの完全停止{モーターの出力を変えたところで一切反応しなかった}などによる。)
これらの異常動作の原因は、それぞれドリフトが発生したことにより、機体の重心が高すぎたのではないか、左タイヤの完全停止は機体の鈍重さゆえに起きたのではないかとの結論に至り、機体の大幅改修に至った。(図6、7参照。)これによりまともな動きのできるロボットができたため、ある程度まともなライントレースのできるロボットができた。
今回のライントレースは、再度前回のライントレースを採用した。ロボット機体の下部に光センサーを取り付け、センサーが黒い線の左側の境界をたどるようなプログラムを制作したものである。具体的にどうしたのかを説明すると、あらかじめロボットについている明るさの度合いを調べる機能で黒い線の上、紙の白い部分、それらの境界の明るさを測定しておく。その数値を用い黒い線の上の数値が出たら進路を左へ、紙の白い部分の数値だったら進路を右へ、紙の白い部分だったら進路はそのままとなるようにした。これにより、ある程度正確に線の上を移動することができるようになった。
また、今回も交差点の判別も必要である。これはロボットの判定回数を利用することで解決した。どういうことかというと、あらかじめある一定の数を設定しておきロボットが異常な数(最初に設定したある数)以上に横に曲がる判定をロボットがした場合、一時的にライントレースを中断し直進させ、その後またライントレースを開始するという方法である。
これら<移動するプログラム><ライントレースをするプログラム>を用い私たちのロボットは図6に示すような軌道で移動することとなった。なお、ルートが事前に決定しているのは下の<コップを識別するプログラム>の事情にもよる。
図6、ロボットの進む軌道
<コップを識別するプログラム>…紙コップに模様をつけそれを光センサーで読み取り識別するというプログラムを制作することになった。紙コップにつける模様を工夫することによって比較的容易にできるのではという考えのもとにこのプログラムを組むことになったが、私達のチームの中でだれもうまく考えつかず、紙コップ識別機能が実装されることはなかった。したがって、我々のロボットはその位置を紙コップの数字に依存することなく進路を決定することになっている。
<コップをつかむ、保持する、持ち上げる、重ねるプログラム>…すでに組立て段階で、つかむ、保持する、重ねることのプログラムは、モーターを回転させるだけで可能なように工夫して作ってあるので、容易に組むことができた。
これらをもとに実際に組んだプログラムがこれである。(注4*実際にプログラムを組んだのは私ではないので、その点に十分留意されたし。{suiden氏が組んだものである。})
#define THRESHOLD 50 //しきい値(明るさ判定でつかう値)// #define turn_left0 OnFwd(OUT_B,30); //左に曲がる関数の定義// #define turn_left1 OnFwd(OUT_B,30);OnFwd(OUT_C,-30);//左に旋回の関数の定義// #define turn_right0 OnFwd(OUT_C,30); //右に曲がる関数の定義// #define turn_right1 OnFwd(OUT_C,30);OnFwd(OUT_B,-30);//右に旋回の関数の定義// #define STEP 1 //明るさ判定の頻度 (x回/1000s)// #define nMAX 250 //交差点判定での異常な数の目安// #define short_break Off(OUT_BC);Wait(1000); //判定の後、交差点をわたるまでの微妙な時間調整// #define cross_line;OnFwd(OUT_BC,40);Wait(300);short_break;PlaySound(SOUND_UP);//交差点を突っ切る動作の定義//
sub catch() //紙コップをつかむ動作の関数の定義// { OnFwd(OUT_A,-20);Wait(1000); OnFwd(OUT_A,-50);Wait(400); Off(OUT_A); } sub release() //紙コップを離す動作の関数の定義// { OnFwd(OUT_A,20);Wait(1000); OnFwd(OUT_A,50);Wait(100); Off(OUT_A); }
これから下が実際に行う動作のプログラムである。
task main() //紙コップを離す動作の関数の定義// { SetSensorLight(S2); //光センサー(紙コップ検知用)の接続先指定// SetSensorLight(S3); //光センサー(ライントレース用)の接続先指定// int nOnline = 0; //後々使う交差点判定のときの数を定義+現在その値は0と指定// int nPoint = 0; //通過した交差点の数カウンターの定義+現在その値は0と指定// 1の部分(図8参照){手動の動作} OnFwdSync(OUT_BC,60,0);Wait(2000);Off(OUT_BC); //ここから OnFwdSync(OUT_BC,75,-100);Wait(450);Off(OUT_BC);Wait(1000); OnFwdSync(OUT_BC,60,0);Wait(1200);Off(OUT_BC); catch(); {A地点のコップをつかむ} OnFwdSync(OUT_BC,60,0);Wait(700); OnFwdSync(OUT_BC,60,-100);Wait(500);Off(OUT_BC);Wait(1000);ここまで、 スタート地点からA地点の紙コップをつかむまでの動作(この部分は私が作った。)// long t0 = CurrentTick(); //現在の時間をとる// 2の部分{ライントレース} while (CurrentTick() - t0 < 3000) { //最初からの経過時間が3秒以内ならば、以下を実行// if (SENSOR_3 > THRESHOLD +2) { //[*1]ライントレースをするために線の上に乗せる調整の動作// OnFwd(OUT_B,40); } else { Off(OUT_BC); } Wait(STEP); } while (nPoint <= 1) { //通過した交差点の数がある数以下ならば以下を実行// while (nOnline < nMAX){ //横に曲がる判定の数が異常ではない場合、以下の関数を実行しろ// if (SENSOR_3 < THRESHOLD -10) { //もしセンサーの値がある値より小さければ右へ旋回// turn_right1; nOnline++; //横(右)に曲がる数を数えるカウンターに+1// } else { if (SENSOR_3 < THRESHOLD -5) { //でない状況で、もしセンサーの値がある値より小さければ右へ曲がれ// turn_right0; } else if (SENSOR_3 < THRESHOLD +2) {//でない状況で、もしセンサーの値がある値より小さければまっすぐ進め// OnFwdSync(OUT_BC,30,0); } else if (SENSOR_3 < THRESHOLD +8) {//でない状況で、もしセンサーの値がある値より小さければ左へ曲がれ// turn_left0; } else { //でない状況で、もしセンサーの値がある値より小さければ左へ旋回// turn_left1; } nOnline=0; //横(右)に曲がる数を数えるカウンターにをリセット// } Wait(STEP); //判断時間と黒い線に沿うための動作時間の合計の指定// } short_break; //時間調整のための小休止// turn_left1; Wait(nMAX*STEP); cross_line; //交差点を渡れ// nPoint++; //通過した交差点の数カウンターの値に+1// nOnline=0; //横(右)に曲がる数を数えるカウンターにをリセット//[*2] } while (nPoint == 2) { //通過した交差点の数がある数と同じならば// OnFwdSync(OUT_BC,30,0);Wait(1500);Off(OUT_BC); Wait(1000);Wait(1000); //ここから OnFwdSync(OUT_BC,-30,0);Wait(500);Off(OUT_BC); release(); {1の位置でAのコップを重ねる} OnFwd(OUT_C,-30);Wait(200); OnFwdSync(OUT_BC,-60,0);Wait(900);Off(OUT_BC); catch(); {アームが2の位置で引っかからないよう閉じる} OnFwdSync(OUT_BC,75,-100);Wait(900);Off(OUT_BC);ここまで、Aのコップを重ねる動作と そのための機体の位置調整と2にあるコップにアームを引っかけないための動作// nPoint++; } long t1 = CurrentTick(); //t1に開始から今までの時間を保存// while (CurrentTick() - t1 < 3000) {//シュート後の時間がある値以下だったら// if (SENSOR_3 > THRESHOLD +2) { //ここから OnFwd(OUT_C,40); } else { Off(OUT_BC); } Wait(STEP); ここまで、次の場所への機体の位置の移動(手動)// } while (nPoint <= 6) { //通過した交差点の数がある数以下だったら// while (nOnline < nMAX + 50){ //ここから if (SENSOR_3 < THRESHOLD -10) { turn_left1; nOnline++; } else { if (SENSOR_3 < THRESHOLD -5) { turn_left0; } else if (SENSOR_3 < THRESHOLD +2) { OnFwdSync(OUT_BC,30,0); } else if (SENSOR_3 < THRESHOLD +8) { turn_right0; } else { turn_right1; } nOnline=0; } Wait(STEP); } short_break; turn_right1; Wait(nMAX*STEP); cross_line; nPoint++; nOnline=0; ここまで、[*1]から[*2]と同じ// } while (nPoint == 7) { //今まで通過した交差点の数がある数だったら// OnFwdSync(OUT_BC,75,100);Wait(300);Off(OUT_BC);Wait(1000);// OnFwdSync(OUT_BC,-60,0);Wait(900);Off(OUT_BC); OnFwdSync(OUT_BC,75,100);Wait(200);Off(OUT_BC);Wait(1000); release(); {先ほど閉じたアームを開く} OnFwdSync(OUT_BC,60,0);Wait(1000);Off(OUT_BC);Wait(1000); catch(); {ここでBのコップをつかむ} nPoint++; OnFwdSync(OUT_BC,60,0);Wait(500);Off(OUT_BC);Wait(1000); ここまで、機体の位置調整とBのコップをつかむ動作// } long t2 = CurrentTick(); //t2に現在の時間を保存// while (CurrentTick() - t2 < 5000) { //直前の動作が終了してからの時間がある値より小さければ、 以下の動作を実行// if (SENSOR_3 > THRESHOLD +2) { //ここから OnFwd(OUT_B,-20);OnFwd(OUT_C,50); } else { Off(OUT_BC); } Wait(STEP); //ここまで、ライントレースのための位置調整// } while (nPoint == 8) { //今まで通過した交差点の数がある数だったら// while (nOnline < nMAX){ //ここから if (SENSOR_3 < THRESHOLD -10) { turn_left1; nOnline++; } else { if (SENSOR_3 < THRESHOLD -5) { turn_left0; } else if (SENSOR_3 < THRESHOLD +2) { OnFwdSync(OUT_BC,30,0); } else if (SENSOR_3 < THRESHOLD +8) { turn_right0; } else { turn_right1; } nOnline=0; } Wait(STEP); } short_break; turn_right1; Wait(nMAX*STEP + 600); cross_line; OnFwd(OUT_BC,30);Wait(200); nPoint++; nOnline=0; ここまで、[*1]から[*2]と同じ// } long t3 = CurrentTick(); //t3に開始から今までの時間を保存// while (CurrentTick() - t3 < 18000) { //直前の動作が終了してからの時間が ある値より小さければ、以下の動作を実行// if (SENSOR_3 < THRESHOLD -10) { //ここから turn_right1; } else { if (SENSOR_3 < THRESHOLD -5) { turn_right0; } else if (SENSOR_3 < THRESHOLD +2) { OnFwd(OUT_BC,30); } else if (SENSOR_3 < THRESHOLD +8) { turn_left0; } else { turn_left1; } Wait(STEP); ここまで、[*1]から[*2]と同じ (ただし、交差点系統の関数は抜く)// } } OnFwdSync(OUT_BC,75,-100);Wait(1000);Off(OUT_BC);Wait(1000);//位置調整// while (nPoint == 9) { //今まで通過した交差点の数がある数だったら// while (nOnline < nMAX){ //ここから if (SENSOR_3 < THRESHOLD -10) { turn_left1; nOnline++; } else { if (SENSOR_3 < THRESHOLD -5) { turn_left0; } else if (SENSOR_3 < THRESHOLD +2) { OnFwdSync(OUT_BC,30,0); } else if (SENSOR_3 < THRESHOLD +8) { turn_right0; } else { turn_right1; } nOnline=0; } Wait(STEP); } short_break; turn_right1; Wait(nMAX*STEP); cross_line; nPoint++; nOnline=0; ここまで、[*1]から[*2]と同じ// } while (nPoint == 10) { //今まで通過した交差点の数がある数だったら// OnFwdSync(OUT_BC,30,0);Wait(800);Off(OUT_BC);Wait(1000);//ここから OnFwdSync(OUT_BC,-30,0);Wait(600);Off(OUT_BC);Wait(1000); release(); {ここでBのコップを重ねる} nPoint++; ここまでBのコップを重ねる動作とそのための位置調整// } while (nPoint == 11) { //今まで通過した交差点の数がある数だったら// OnFwdSync(OUT_BC,-60,0);Wait(1200);Off(OUT_BC); //ここから OnFwdSync(OUT_BC,75,-100);Wait(500);Off(OUT_BC);Wait(1000); OnFwdSync(OUT_BC,60,0);Wait(1800);Off(OUT_BC); catch(); {ここでCのコップをつかむ} nPoint++; ここまでCのコップをつかむ動作と そのための位置調整// } while (nPoint == 12) { //今まで通過した交差点の数がある数だったら// OnFwdSync(OUT_BC,75,-100);Wait(1000);Off(OUT_BC);Wait(1000);//ここから OnFwdSync(OUT_BC,60,0);Wait(2500);Off(OUT_BC); OnFwdSync(OUT_BC,-30,0);Wait(600);Off(OUT_BC);Wait(1000); release(); {ここでCのコップを重ねる} nPoint++; ここまでCのコップを 重ねる動作とそのための位置調整// } }
ライントレースでは最後に向いている向きが一定ではないので、ある程度重ねる際にずれが生じてしまう。
(つかむ動作ではアーム構造によってある程度融通が効くのであまり問題はなかった。)
結局、重ねる部分でライントレースの不安定さがあるので、手動調整がそこそこ必要になった。
結果的に、コップを重ねることには失敗してしてしまったが、惜しいところまでいったらしく、全体で第三位に入賞することができた。私はその時都合が悪く出席することができなかったので、当日の失敗を見ることができなかったが、失敗してしまった原因の考察をしてみることにする。(どのような失敗をしてしまったのか見ていないため、反省が的外れになってしまってもどうか勘弁してください。)
1つ目の原因としてコップの識別のプログラムをうまく組めなかったことが挙げられると思う。なぜならば、この課題の中で一番高い配点である、基本得点を左右する重要な要素であったにもかかわらず、それがうまくいかないというのは大きな問題であったと思う。みなの力の分配を間違えた(ライントレースなど機体を動かす方に力を割き過ぎた)感が否めない。こちらにも人員をきちんと割きプログラムを構築すべきだった。計画初期段階でのミスであると言える。
2つ目の原因として、先にも述べたが、ライントレース後のコップを重ねる動作をより確実にする方法を考案するべきだったと思われる。全手動だとどうしてもずれが生じてしまうし、ライントレースをする必要があると考えたが、コップを重ねる動作の直前に左右どちらの向きをむいているのかなどを判断した上での調整が複雑すぎてうまくできなかった。この点をどうにかすべきだったと思われる。一瞬、プログラム以前にバンパーを付けるなど機構面でどうにかしようという意見も上がったが、バンパーを付けたところであまり成功率が上がらなかったため、その案も途中で頓挫してしまった。今考えると、その方面を突き詰めて考える必要があったと思う。
総じてプログラムと構築のバランスをしっかり考え、時間をとり課題と向かい合う必要があった。このバランスを頭の中に入れた上で計画の初期段階でしっかりと役割分担、計画(時間的な)、そしてプログラムでカバーすべき事項と機構でカバーすべき事項の区別などすべきだったと思った。今後、このような状況をまた体験することがあればこのことをしっかりとやって、課題達成を目指したいと思う。
以上、課題2レポートでした。長文お読みいただきありがとうございました。