2012b/Member/ddnp/Mission1
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
#contents
*ライントレス[#linetrace]
**まずはラインに沿って動かしてみる[#tracing]
とりあえず、ラインに沿って動くことが出来なければボールを...
そこで、練習用として、次のようなコースを用意した。
この時使った機体は、マニュアルに設計図の乗っているデフォ...
&ref(2012b/Member/ddnp/Mission1/practice_course.jpg, 100%...
***黒ライントレス[#black_trace]
光センサーは、光の反射量を読み取るため、黒い部分の値は低...
そこで、光センサーが白い部分を読み取ったら、つまり光セン...
コースの黒いところから、白いところまでの値を事前に読み取...
黒いところと白いところで、値の違いは大体38〜51。よって、4...
//---------------------//
// 定数定義
//---------------------//
#define WHITE 50 // 白い部分のしきい値
task main()
{
//-----------------------//
// 初期化
//-----------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT);
while(true)
{
OnFwd(OUT_AC); // 常に前進
// 白い場所を読み取っている間旋回
while(SENSOR_2 >= WHITE)
{
// 反時計回りに旋回
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
}
ちなみに、上記のプログラムでは、「黒い部分に入るまで旋回...
「白い部分に入ったら、黒が濃い部分を読み取るまで旋回する...
また、白いラインに入った際、少し時間を空けてから黒い部分...
ただし、開けすぎて白い部分で旋回し続けるようにならないよ...
つまり、定数定義に以下の二文を追加し、
#define BLACK 40 // 黒い部分のしきい値
#define STRAIGHT 10 // 白い部分読み取りの際の遊び
「白い部分を読み取っている間旋回」するプログラムを、以下...
// 白い部分を読み取った時
if(SENSOR_2 >= WHITE)
{
Wait(STRAIGHT); // 少しだけ直進
// 黒いラインの中心付近に来るまで旋回
while(SENSOR_2 <= BLACK)
{
// 反時計回りに旋回
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
これで、多少スムーズにライントレスするようになるだろう。
ちなみに、BLACKの値をあまり低く設定すると、中心からずれや...
よって、BLACKの値は多少高めでも問題無いだろう。
ところで、このプログラムには大きな問題がある。左カーブ時...
そこで、ある程度探しても黒線が見つからなかった場合、逆回...
そこで書いたのが、以下のプログラム。
//---------------------//
// 変数定義
//---------------------//
#define BLACK 42 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define RADIO_SIGHT 60 // 黒い部分を探す際の視野
#define STRAIGHT 10 // 黒い部分を探す処理に移る前の遊び
//--------------------------//
//
// その場で旋回する関数
//
// cw 旋回方向を指定する変数 奇数なら右回り(clock wise)
//-------------------------//
void stay_turn (int cw)
{
// 時計回り
if(cw % 2)
{
OnFwd(OUT_A);
OnRev(OUT_C);
}
// 反時計回り
else
{
OnFwd(OUT_C);
OnRev(OUT_A);
}
}
//-------------------------//
//
// メイン関数
//
//-------------------------//
task main ()
{
//----------------------//
// 初期化
//----------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
//----------------------//
// 変数定義
//----------------------//
int rotate = 1; // 回転方向を指定 奇数は右回り
int times = 0; // 旋回回数を記憶 かつ視野を増加させる
int switch = 1; // 処理を抜けるための変数
int timer = 0; // タイマーの時刻を記憶
//-----------------------//
// 黒ライントレス
//-----------------------//
while(switch)
{
// 黒い間は直進
while(SENSOR_2 <= WHITE)
{
OnFwd(OUT_AC);
}
Wait(STRAIGHT);
// 白い部分に来た時
if(SENSOR_2 > WHITE)
{
times = 0; // 回転回数初期化
// 黒い部分を探しながら、カーブの方向を決定する
while( (SENSOR_2 > BLACK) && (times <= 3) )
{
ClearTimer(0); // 旋回時間測定用のタイマーをセット...
// 視野の分だけ黒い場所を探す
while ( (SENSOR_2 > BLACK) && (FastTimer(0) <= RADIO...
{
stay_turn(rotate); // 旋回
}
// 黒い部分が見つからなかった場合
if(SENSOR_2 > BLACK)
{
rotate ++; // 逆回転を指定
times ++; // 回転回数と視野を増加
}
}
// 黒い線が見つからなかった時(一回転以上した時)
if(times > 3)
{
// 完全に白い場所に出たということで、処理を中止。あ...
switch = 0;
}
}
}
// 停止
Off(OUT_AC);
}
黒線を探す際の視野というのは、どの程度回転させるかを示し...
急カーブでコースアウトした際に、回転する方向は合っていて...
最悪、逆探索をした際に近くにある別の線を読みとる場合も考...
なので、視野の値は、急カーブでも一回目の旋回で曲がりきれ...
このプログラムでは、大体120度くらい旋回するようにしてある。
何はともあれ、これで黒線をトレースすることができた。めで...
と、思うじゃん?
先生「白と黒の境界をトレスする方が効率が良いです」
( ゚д゚) ...
( ゚д゚) ...&ruby(どういうことなの…?){What do you mean...?};
***境界ライントレス[#border_trace]
黒ライントレスの方法では、ラインから外れた場合、ただがむ...
要するに、カーブが右カーブなのか左カーブなのかを判別する...
しかし、黒線と白い部分の間を進む、つまり境界線をトレース...
#ref(2012b/Member/ddnp/Mission1/border_trace.gif, 100%, ...
例えば、ロボットに「特に白い部分を感知したら右に、特に黒...
とプログラムしたとすると、ロボットは黒いラインの左側をト...
これによって最も嬉しいことは、ロボット自身で「境界線のど...
これは、ロボットが右カーブと左カーブを判別することが出来...
早速、プログラムを書いて動かしてみよう。
//----------------------------//
//
// 定数定義
//
//----------------------------//
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 43 // 黒のしきい値
#define WHITE 47 // 白のしきい値
#define DEEPWHITE 50 // 濃い白のしきい値
//----------------------------//
//
// メイン処理
//
//----------------------------//
task main()
{
//----------------------------//
// 初期化
//----------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT);
//----------------------------//
// 境界ライントレス
//----------------------------//
while(true)
{
// 白側に出た時
if(SENSOR_2 > DEEPWHITE)
{
// 境界に近づくまで回転
while(SENSOR_2 <= WHITE)
{
// 時計回り
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
// 黒側に出た時
else if(SENSOR_2 < DEEPBLACK)
{
// 境界に近づくまで回転
while(SENSOR_2 >= BLACK)
{
// 反時計回り
OnFwd(OUT_C);
OnRev(OUT_A);
}
}
// 境界線上は直進
else
{
OnFwd(OUT_AC);
}
}
}
黒トレスと比べて、すっきりしたプログラムになった。
境界ライントレスについては、これで問題なく動作したので、...
*実際のコースで動作させる [#c883296f]
**スタートからゴールまでのライントレス[#from_start_to_goal]
***課題の題材となったコースと注意するポイント[#checkpoint]
下図を参照。
このコースの端からスタートして、もう片方の端にあるゴール...
#ref(2012b/Member/ddnp/Mission1/course_1.jpg, 100%, 課題...
S字カーブ、直角カーブ、急カーブ、交差点と、入り組んだコー...
S字カーブの真ん中に障害物を設置し、それに触れないように進...
まずは、シュートの前段階として、スタート地点からゴール地...
そのためにクリアしなければいけない課題として、以下のこと...
-S字の障害物を避けるため、本体を小さくする。
-急カーブを曲がれるよう工夫する。
-交差点と直角カーブの区別を明確にする。
-ゴールを判別し、ゴールに標準を合わせる。
とりあえず、次の項では本体を小さくすることに焦点を当てて...
***その前にデフォ子の追悼式[#see_you_default_bot]
この日、授業の初めの方から付き合って下さったデフォ子様の...
お集まりの皆様方には、デフォ子様との想い出の日々が以下都...
***本体を小さくする[#make_the_robot_small]
とりあえず、実際に本体を作らず、紙面上で実験。
ブロックやシャフトを使い、縦長、横長など様々な大きさの枠...
-デフォ子に使われているタイヤは太すぎて、使うには難しい。
-左右のモーターが多少大きいので、付ける位置を近づけるなど...
-旋回する時中心となる点から、機体が前や後ろに長すぎると、...
まぁぶっちゃけ全体的にコンパクトになってればなってるほど...
結果的に、以下の三点を実施。
-二輪に変更
--実際にはバランスを取るために三輪。ただ三番目のタイヤは...
-モーター同士をできる限りくっつけてみた。
--しかし不安定性が増したので、上下に固定用のブロックを足...
--同時に、車軸を固定するパーツ&ref(2012b/Member/ddnp/Miss...
-RCX本体が縦長なので、デフォ子のように寝かせるのでなく、...
--中心でなく後ろ側に寄せて立てることで、前側に光センサー...
--嬉しいことに、寝かせるより立てたほうが、本体ががっちり...
--センサーが離れてしまったのでコードを延長したのだが、延...
--ただし、これによって重心が後ろに傾いた。何も影響がなけ...
--欠点としては、スイッチ類が押しにくかったり、View値が読...
これを考慮して出来た機体がこれ。
#ref(2012b/Member/ddnp/Mission1/half_of_robot.jpg, 100%, ...
さらに、光センサーの位置についても考慮してある。
出来るだけ正確に読み取れるように、地面に近づけ、本体の中...
このことにより、光センサーが本体の中心からより離れている...
つまり、
#ref(2012b/Member/ddnp/Mission1/degree_difference.gif, 10...
上図のように、光センサーの位置によって、回転した時間が同...
これはつまり、回転の中心に光センサーを寄せるほど、ゆっく...
よって、多少早い動作をしても、光センサーの読み取りミスが...
ただし、欠点もある。近くに寄せ過ぎると、光センサーの読み...
ラインがほぼ真下にあるのに読み込めないという事態が発生し...
今回の機体では、大体写真の位置で読み取るよう設定した。
&ref(2012b/Member/ddnp/Mission1/read_distance.jpg, 100%, ...
これにて基盤は完成。
***ゴールを目指す[#aim_the_goal]
機体は出来た。ということで、テストも兼ねてラインをトレー...
試しに、先ほど作った境界トレスのプログラムを動作させてみ...
唯一変更した点は、光センサーのしきい値くらいである。
それにしても、再利用できてよかった。
さて、ラインはトレースできても、ゴールまで行きつけなけれ...
この時点で、自分がクリアすべき課題は
+コース途中の交差点をどう通過するか
+ゴールを判定するにはどうするか
の2つであった。
#ref(2012b/Member/ddnp/Mission1/course_2.jpg, 100%, 通過...
まずは、途中の通過すべき交差点の処理を考える。
普通なら、交差点であることを判別して、黒い線を跨ぐような...
ちょっとした思いつきで、交差点の判別をしないで超えてみる...
使用するのは、前に作った黒トレスのプログラム。
交差点に突入する前に、黒ライントレスを開始し、
通り過ぎた頃に黒ライントレスから境界ライントレスに移行す...
ただし、黒ライントレスから境界ライントレスに移行する時に...
本来は黒ライントレス時の回転方向を決定する変数を利用して...
//--------------------------//
//
// 定数定義
//
//--------------------------//
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 45 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define DEEPWHITE 53 // 濃い白のしきい値
#define CHANGE_BLACK 1100 // 境界→黒トレス切り替え時間
#define CHANGE_OUTLINE 400 // 黒→境界トレス切り替え時間
#define SEESIGHT 120 // 黒トレス時のライン捜索時間
//---------------------------//
//
// メイン処理
//
//---------------------------//
task main()
{
//-------------------------//
// 初期化
//-------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
ClearTimer(0); // 境界・黒ライントレスの入れ替わり...
//----------------------//
// 変数定義
//----------------------//
int rotate = 1; // 回転方向を指定 奇数は右回り
int times = 0; // 旋回回数を記憶 かつ視野を増加させる
//-------------------------//
// ライントレス
//-------------------------//
while(true)
{
// 指定時間外かつライン左側で輪郭線ライントレス
if((FastTimer(0) <= CHANGE_BLACK) || (FastTimer(0) >= ...
{
//---------------------------//
// 輪郭線ライントレス
//---------------------------//
・・・
・・
・
}
// 指定時間内かつライン右側で黒ライントレス
else if(FastTimer(0) > CHANGE_BLACK || FastTimer(0) < ...
{
//----------------------------//
// 黒ライントレス
//----------------------------//
・・・
・・
・
}
}
}
ライントレス処理は上記とほぼ同じなので省略。
途中、プログラムの入ったUSBメモリをなくして作りなおしたの...
USBメモリの管理には気をつけましょう。
これにて、目論見どおり交差点を渡ることが出来た。
#ref(2012b/Member/ddnp/Mission1/course_3.jpg, 100%, シュ...
次は、ゴール、つまりシュートする位置を判別出来るようにし...
といっても、シュートする位置を示す特徴が、交差点くらいし...
そこで、境界ライントレス時に、旋回する時間が長い場合は、...
#ref(2012b/Member/ddnp/Mission1/course_4.jpg, 100%, コー...
地形的な特徴と、黒ライントレスの処理があることから、黒側→...
ゴールまで計3つのコーナーを通過することになるので、3つ...
定数と変数を付け加え、
#define CORNER_TIME 40 // コーナーと判別する時間
int backturn = 0; // コーナーを曲がるのにかかった時間...
int corner_times = 0; // 曲がったコーナーの数
境界ライントレスの、反時計回り旋回時のプログラムをこのよ...
// 黒側→白側
else if(SENSOR_2 < DEEPBLACK)
{
ClearTimer(2); // コーナーを曲がっている時間を測定
// 白側へ旋回
while(SENSOR_2 < BLACK)
{
// 左旋回
OnFwd(OUT_C);
OnRev(OUT_A);
}
// 曲がるのに時間がかかった場合、直角だと判断
if(FastTimer(2) >= CORNER_TIME)
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時間...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知ら...
}
}
これで直角を判別してくれるようになった。
直角を判定した時には、機体はコーナーを曲がりきってしまっ...
シュート処理するときにはゴールの方向へ機体を向ける必要が...
そこで、曲がるまでにかかった時間を記憶して、シュート処理...
しかし、処理の遅延なのか、実際には多少ズレが生じるので、...
さらに、シュート処理に移るときは、ライントレスをする必要...
コーナーを3回以上曲がった場合に処理を抜けるように、ルー...
つまり、
while(true)
の一文を、
while(corner_times < 3)
に変更する。
ここで、再び問題が発生。
コーナーで、「2回以上コーナーを曲がったことにしてしまう...
どうやら、トレスの方法に、「色が濃い場所までいってから、...
コーナーに入る角度によって、コーナーのトレスに2,3回軌...
コーナーと判定する時間を45くらいまで増やしてみたところ、...
定数でどうにかするには心もとない。
ということで、一度コーナーを判定したら、しばらくコーナー...
定数と変数をさらに追加。
#define CORNER_WAIT 1000 // コーナー判定を無視する時間
int corner_wait = -800; // コーナーを曲がった時の時間
コーナー判定プログラムは次のようにした。
// コーナーを判定且つ以前コーナーに来てからしばらく経過...
if(FastTimer(2) >= CORNER_TIME && FastTimer(0) >= corner...
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時間...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知ら...
corner_wait = FastTimer(0); // コーナーを曲がったとき...
}
コーナーを曲がるときの条件式に合わせて、コーナーを曲がっ...
コレがないと一個目のコーナーを判別できない可能性が高い。
改善前もコーナー以外で誤動作することはなかったが、こうす...
ところがどっこい今度も謎の問題が発生
図の部分だけ、曲がりきることが出来ない問題が発生した。
#ref(2012b/Member/ddnp/Mission1/course_5.jpg, 100%, 謎の...
定数値をいじってみるも、問題が解決しないので、まさかと思...
この場所だけ、白の部分が50という値を観測。他が55なので、...
探ってみたところ、どうやら自身の影が光センサーを邪魔して...
光センサーは、色ではなく、跳ね返ってくる光の強弱でライン...
実際、日光のあたる場所に連れて行くと、白い部分で最大89を...
直射日光、蛍光灯や本体自身の影等、あらゆる条件によって読...
光センサーの扱いは注意したほうが良いということを実感した...
発表間近でこれを経験すると意外と焦るので気をつける。
以上で、ライントレスは完成。スタートからゴールまでの道は...
**ボールを持ち運んで、シュートする。[#carrying_and_shooti...
***まずは、ボールをどうやって持つか。[#how_to_chatch_the_...
正直、これを考えるのが一番楽しかったかもしれない。
腕で捕まえる、上の方に持ちあげる等、いろいろとアイディア...
#ref(2012b/Member/ddnp/Mission1/arm_1.jpg, 100%, ピンポン...
#ref(2012b/Member/ddnp/Mission1/arm_catch.jpg, 100%, ピン...
ワームが回転し、光センサーとワームを回すシャフト自身がピ...
しかも、ワームを逆回転させればシュートも出来るという優れ...
つかむ時、写真手前側にボールが逃げないよう、5つ穴のブロ...
光センサーの裏を使って挟み込むことは、省スペースにも繋が...
本当は、ワームの上にただボールを載せておくだけで、シュー...
ボールを抱え込んでワーム上に設置するのが難しかったのでこ...
***ボールを持って、シュートさせる[#how_to_shoot]
パーツは出来たので、後はプログラム。
しかし実際は、モーターを前後に動かすだけでキャッチもシュ...
つかむプログラム
OnRev(OUT_B); // 腕内側回転
Wait(PICKUPTIME); // 少しの間回転
Off(OUT_B); // 停止
シュートするプログラム
SetPower(OUT_B, 5); // パワー強め
OnFwd(OUT_B); // シュート
Wait(100);
Off(OUT_ABC); // シュート停止とともに全動作を停止
パワーを強めにしてみたはいいものの、あまり実感は出来なか...
以上で、ボールキャッチからシュートまでの処理は完成。
*まとめ[#conclusion]
**今回使った機体[#used_robot]
#ref(2012b/Member/ddnp/Mission1/robot.jpg, 100%, 使ったロ...
そういえば名前とか付けてない…。
全体的に強度が足りない時期があったので、細かいブロックな...
その結果、ロゴでよくある「あのパーツが足りない」現象が起...
薄い2*2ブロックとか1*1ブロックとか高さ調整ですごい使った...
そしてその小型ブロックのせいで、分解時に爪を痛めたのもい...
**実際に使用したプログラム[#used_program_code]
//------------------------------------------------//
//
// Title : Carrying-ball
//
// Use : ボールを拾い上げた後、
// 進行方向に対して左側の境界線を
// ライントレス
// 障害を乗り越えた後、
// ボールをゴールにシュート
//
//------------------------------------------------//
/*
############################
定数定義
############################
*/
// トレス判定系
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 45 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define DEEPWHITE 53 // 濃い白のしきい値
#define CHANGE_BLACK 1150 // 境界→黒トレス切り替え時間
#define CHANGE_OUTLINE 400 // 黒→境界トレス切り替え時間
#define CORNER_WAIT 1000 // コーナー判定を無視する時間
// 境界トレス用
#define CORNER_TIME 38 // 直角判定する時間
// 黒トレス用
#define SEESIGHT 120 // 黒トレス時のライン捜索時間
// ピンポン玉処理系
#define PICKUPTIME 30 // ピンポン玉取得のための腕回転...
#define SHOOT 600 // ピンポン玉発射時の腕回転時間
// 音符
#define C4 523
#define E4 659
#define F4 698
#define G4 784
#define A5 880
#define B5 988
#define C5 1046
#define D5 1175
#define E5 1318
#define F5 1397
#define G5 1568
#define C6 2093
/*
#############################
# マクロ
#############################
*/
#define RotateRight OnFwd(OUT_A); OnRev(OUT_C); // 時計...
#define RotateLeft OnFwd(OUT_C); OnRev(OUT_A); // 反時...
/*
#############################
# 右旋回ルーチン
#############################
*/
sub rotateright()
{
OnFwd(OUT_A);
OnRev(OUT_C);
}
/*
#############################
# 左旋回ルーチン
#############################
*/
sub rotateleft()
{
OnFwd(OUT_C);
OnRev(OUT_A);
}
/*
###############################
# 歓喜の舞
###############################
*/
sub kirby_clear ()
{
// 超舞う
RotateLeft;
PlayTone(F4, 10); Wait(13);
PlayTone(G4, 10); Wait(13);
PlayTone(A5, 10); Wait(13); RotateRight;
PlayTone(B5, 10); Wait(13);
PlayTone(A5, 10); Wait(13);
PlayTone(B5, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(C5, 10); Wait(25);
PlayTone(E4, 10); Wait(13);
PlayTone(E4, 10);
Off(OUT_B);Wait(13); Wait(25);
RotateRight;
PlayTone(F4, 10); Wait(13);
PlayTone(G4, 10); Wait(13);
PlayTone(A5, 10); Wait(13); RotateLeft;
PlayTone(B5, 10); Wait(13);
PlayTone(A5, 10); Wait(13);
PlayTone(B5, 10); Wait(13); Off(OUT_AC); OnRev(OUT_B);
PlayTone(C5, 10); Wait(25);
PlayTone(E4, 10); Wait(13);
PlayTone(C4, 10);
Off(OUT_B); Wait(13); Wait(25);
RotateLeft;
PlayTone(F4, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(G4, 10); Wait(13); Off(OUT_B); RotateRight;
PlayTone(A5, 10); Wait(13); Off(OUT_AC); OnRev(OUT_B);
PlayTone(B5, 10); Wait(13); Off(OUT_B); RotateRight;
PlayTone(A5, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(B5, 10); Wait(13); Off(OUT_B); RotateLeft;
PlayTone(C5, 22); Wait(25); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(G4, 10); Wait(13); RotateRight;
PlayTone(E4, 22); Wait(25);
PlayTone(G5, 10); Wait(13);
PlayTone(F5, 22); Wait(25);
PlayTone(E5, 10); Wait(13);
PlayTone(D5, 22); Wait(25);
PlayTone(E5, 10); Wait(13); Off(OUT_AC);
PlayTone(C5, 35); Wait(38);Off(OUT_B);
PlayTone(C6, 10); Wait(13);
}
/*
################################
#
# メイン関数
#
################################
*/
task main()
{
//-------------------------//
// 初期化設定
//-------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
SetPower(OUT_ABC, 2); // モーター動力を少し低く指定
ClearTimer(0); // プログラムを走らせだしてからの時...
//-------------------------//
// 変数宣言
//-------------------------//
int clockwise = 3; // 黒トレス時の回転方向を記憶 奇...
int rad_increase = 1; // 黒トレス時の反転回数 倍角...
int corner_times = 0; // コーナーを曲がった回数
int backturn = 0; // シュート時の角度補正用
int corner_wait = -800; // コーナーを曲がった時の時間
//-------------------------//
// 玉取得
//-------------------------//
OnRev(OUT_B); // 腕内側回転
Wait(PICKUPTIME); // 少しの間回転
Off(OUT_B); // 停止
//-------------------------//
// ライントレス
//-------------------------//
// コーナーを3回以上回るまでライントレス処理を実行
while(corner_times < 3)
{
// 指定時間外かつライン左側で輪郭線ライントレス
if((FastTimer(0) <= CHANGE_BLACK) || (FastTimer(0) >= ...
{
//---------------------------//
// 輪郭線ライントレス
//---------------------------//
// 白側→黒側
if(SENSOR_2 > DEEPWHITE)
{
// 薄くなるまで回転
while(SENSOR_2 > WHITE)
{
rotateright(); // 右小回転
}
}
// 黒側→白側
else if(SENSOR_2 < DEEPBLACK)
{
ClearTimer(2); // 旋回時間を測定
// 薄くなるまで回転
while(SENSOR_2 < BLACK)
{
rotateleft(); // 左小回転
}
// コーナーを判定且つ以前コーナーに来てからしばらく...
if(FastTimer(2) >= CORNER_TIME && FastTimer(0) >= co...
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知...
corner_wait = FastTimer(0); // コーナーを曲がったと...
}
}
else
{
OnFwd(OUT_AC); // 常に前進
}
}
// 指定時間内かつライン右側で黒ライントレス
else if(FastTimer(0) > CHANGE_BLACK || FastTimer(0) < ...
{
//----------------------------//
// 黒ライントレス
//----------------------------//
// 完全に白い場所を感知した時、方向修正
if(SENSOR_2 >= DEEPWHITE)
{
rad_increase = 1; // 倍角変数を初期化
// 黒ラインに入るまで処理。黒ラインが見当たらないと...
while(SENSOR_2 >= DEEPWHITE && rad_increase <= 3)
{
ClearTimer(1); // タイマーリセット
// 黒ラインに入るまで、且つ旋回時間が過ぎるまで旋回
while((SENSOR_2 >= BLACK) && (FastTimer(1) <= SEESI...
{
// 時計回り
if(clockwise % 2)
{
rotateright();
}
// 反時計回り
else if(!(clockwise % 2))
{
rotateleft();
}
}
// 白い場所から脱出できなかった場合
if(SENSOR_2 >= BLACK)
{
clockwise ++; // 回転方向を変更
rad_increase += 1; // 回転時間を増加
}
}
}
else
{
OnFwd(OUT_AC); // 常に前進
}
}
}
// 止まる
Off(OUT_AC);
//-----------------------------//
// シュート処理
//-----------------------------//
// 曲がった分バック
rotateright();
Wait(backturn);
Off(OUT_AC);
// 腕を回転
SetPower(OUT_B, 5);
OnFwd(OUT_B);
Wait(100);
// 動作を停止
Off(OUT_ABC);
// 準備体操
SetPower(OUT_B, 2);
// 歓喜の舞
Wait(300);
kirby_clear();
}
上で記述しなかった改善点がいくつか存在する。
-境界→黒トレスまでの秒数を調整。1150が調子良かった様子。
-コーナー判定の秒数を短くした
--可能な限り確実にコーナーを判定できるよう、40〜35を試し...
-右回転、左回転をサブルーチン化
--授業資料に、サブルーチンを使用すると、何回か出てくる処...
-黒トレス時の回転方向を指定する変数の初期値を変更
--そういえば、int型では小数切り捨てだったことを思い出した...
-全体的にコメントの書き方を一新した
--自分にとって、毎度のことながらコメントで汚してる感が否...
-変数や定数の名前が一部変更
--前述のUSBメモリ紛失事件や、プログラムの見直しなど様々な...
-歓喜の舞を実装
--シュート後に音楽を鳴らしながら踊り狂う命令を追加。趣味...
--やってみる人はご自由に。舞は再現できなくても音はまんま...
**感想 [#k14bc134]
楽しみながらスムーズに完成させられたように思う。
プログラミングについては、趣味で多少やっているので、困っ...
個人的な狙いとして、如何にして綺麗にコードを書くかという...
そこができていれば今回の課題はソフトウェア的にはOKだと感...
ハードウェア面は、不慣れなLEGOに悪戦苦闘した割りには、よ...
それ以前に、LEGOの楽しさに触れられたことの方に充実感を感...
どうやって目的の動作をするパーツを作るかという醍醐味に引...
次は、これ以上にアイディアのつまったロボットを組み立てら...
全体的に見ればまだまだ突き詰められる場所があるように感じ...
***余談 [#g417b242]
歓喜の舞に、Pythonで音階の周波数を計算してA2〜A7までつら...
忘れすぎて嫌になるはずの「;」が、Pythonだと逆に無くて落...
だいぶNQCに慣れてきたようなので、これから後の課題もがんば...
分解している最中に、こんな表情を見せた。
名前はスマイリーとかでいいかもしれない。:)
#ref(2012b/Member/ddnp/Mission1/smile.jpg, 100%, スマイル)
終了行:
#contents
*ライントレス[#linetrace]
**まずはラインに沿って動かしてみる[#tracing]
とりあえず、ラインに沿って動くことが出来なければボールを...
そこで、練習用として、次のようなコースを用意した。
この時使った機体は、マニュアルに設計図の乗っているデフォ...
&ref(2012b/Member/ddnp/Mission1/practice_course.jpg, 100%...
***黒ライントレス[#black_trace]
光センサーは、光の反射量を読み取るため、黒い部分の値は低...
そこで、光センサーが白い部分を読み取ったら、つまり光セン...
コースの黒いところから、白いところまでの値を事前に読み取...
黒いところと白いところで、値の違いは大体38〜51。よって、4...
//---------------------//
// 定数定義
//---------------------//
#define WHITE 50 // 白い部分のしきい値
task main()
{
//-----------------------//
// 初期化
//-----------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT);
while(true)
{
OnFwd(OUT_AC); // 常に前進
// 白い場所を読み取っている間旋回
while(SENSOR_2 >= WHITE)
{
// 反時計回りに旋回
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
}
ちなみに、上記のプログラムでは、「黒い部分に入るまで旋回...
「白い部分に入ったら、黒が濃い部分を読み取るまで旋回する...
また、白いラインに入った際、少し時間を空けてから黒い部分...
ただし、開けすぎて白い部分で旋回し続けるようにならないよ...
つまり、定数定義に以下の二文を追加し、
#define BLACK 40 // 黒い部分のしきい値
#define STRAIGHT 10 // 白い部分読み取りの際の遊び
「白い部分を読み取っている間旋回」するプログラムを、以下...
// 白い部分を読み取った時
if(SENSOR_2 >= WHITE)
{
Wait(STRAIGHT); // 少しだけ直進
// 黒いラインの中心付近に来るまで旋回
while(SENSOR_2 <= BLACK)
{
// 反時計回りに旋回
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
これで、多少スムーズにライントレスするようになるだろう。
ちなみに、BLACKの値をあまり低く設定すると、中心からずれや...
よって、BLACKの値は多少高めでも問題無いだろう。
ところで、このプログラムには大きな問題がある。左カーブ時...
そこで、ある程度探しても黒線が見つからなかった場合、逆回...
そこで書いたのが、以下のプログラム。
//---------------------//
// 変数定義
//---------------------//
#define BLACK 42 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define RADIO_SIGHT 60 // 黒い部分を探す際の視野
#define STRAIGHT 10 // 黒い部分を探す処理に移る前の遊び
//--------------------------//
//
// その場で旋回する関数
//
// cw 旋回方向を指定する変数 奇数なら右回り(clock wise)
//-------------------------//
void stay_turn (int cw)
{
// 時計回り
if(cw % 2)
{
OnFwd(OUT_A);
OnRev(OUT_C);
}
// 反時計回り
else
{
OnFwd(OUT_C);
OnRev(OUT_A);
}
}
//-------------------------//
//
// メイン関数
//
//-------------------------//
task main ()
{
//----------------------//
// 初期化
//----------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
//----------------------//
// 変数定義
//----------------------//
int rotate = 1; // 回転方向を指定 奇数は右回り
int times = 0; // 旋回回数を記憶 かつ視野を増加させる
int switch = 1; // 処理を抜けるための変数
int timer = 0; // タイマーの時刻を記憶
//-----------------------//
// 黒ライントレス
//-----------------------//
while(switch)
{
// 黒い間は直進
while(SENSOR_2 <= WHITE)
{
OnFwd(OUT_AC);
}
Wait(STRAIGHT);
// 白い部分に来た時
if(SENSOR_2 > WHITE)
{
times = 0; // 回転回数初期化
// 黒い部分を探しながら、カーブの方向を決定する
while( (SENSOR_2 > BLACK) && (times <= 3) )
{
ClearTimer(0); // 旋回時間測定用のタイマーをセット...
// 視野の分だけ黒い場所を探す
while ( (SENSOR_2 > BLACK) && (FastTimer(0) <= RADIO...
{
stay_turn(rotate); // 旋回
}
// 黒い部分が見つからなかった場合
if(SENSOR_2 > BLACK)
{
rotate ++; // 逆回転を指定
times ++; // 回転回数と視野を増加
}
}
// 黒い線が見つからなかった時(一回転以上した時)
if(times > 3)
{
// 完全に白い場所に出たということで、処理を中止。あ...
switch = 0;
}
}
}
// 停止
Off(OUT_AC);
}
黒線を探す際の視野というのは、どの程度回転させるかを示し...
急カーブでコースアウトした際に、回転する方向は合っていて...
最悪、逆探索をした際に近くにある別の線を読みとる場合も考...
なので、視野の値は、急カーブでも一回目の旋回で曲がりきれ...
このプログラムでは、大体120度くらい旋回するようにしてある。
何はともあれ、これで黒線をトレースすることができた。めで...
と、思うじゃん?
先生「白と黒の境界をトレスする方が効率が良いです」
( ゚д゚) ...
( ゚д゚) ...&ruby(どういうことなの…?){What do you mean...?};
***境界ライントレス[#border_trace]
黒ライントレスの方法では、ラインから外れた場合、ただがむ...
要するに、カーブが右カーブなのか左カーブなのかを判別する...
しかし、黒線と白い部分の間を進む、つまり境界線をトレース...
#ref(2012b/Member/ddnp/Mission1/border_trace.gif, 100%, ...
例えば、ロボットに「特に白い部分を感知したら右に、特に黒...
とプログラムしたとすると、ロボットは黒いラインの左側をト...
これによって最も嬉しいことは、ロボット自身で「境界線のど...
これは、ロボットが右カーブと左カーブを判別することが出来...
早速、プログラムを書いて動かしてみよう。
//----------------------------//
//
// 定数定義
//
//----------------------------//
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 43 // 黒のしきい値
#define WHITE 47 // 白のしきい値
#define DEEPWHITE 50 // 濃い白のしきい値
//----------------------------//
//
// メイン処理
//
//----------------------------//
task main()
{
//----------------------------//
// 初期化
//----------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT);
//----------------------------//
// 境界ライントレス
//----------------------------//
while(true)
{
// 白側に出た時
if(SENSOR_2 > DEEPWHITE)
{
// 境界に近づくまで回転
while(SENSOR_2 <= WHITE)
{
// 時計回り
OnFwd(OUT_A);
OnRev(OUT_C);
}
}
// 黒側に出た時
else if(SENSOR_2 < DEEPBLACK)
{
// 境界に近づくまで回転
while(SENSOR_2 >= BLACK)
{
// 反時計回り
OnFwd(OUT_C);
OnRev(OUT_A);
}
}
// 境界線上は直進
else
{
OnFwd(OUT_AC);
}
}
}
黒トレスと比べて、すっきりしたプログラムになった。
境界ライントレスについては、これで問題なく動作したので、...
*実際のコースで動作させる [#c883296f]
**スタートからゴールまでのライントレス[#from_start_to_goal]
***課題の題材となったコースと注意するポイント[#checkpoint]
下図を参照。
このコースの端からスタートして、もう片方の端にあるゴール...
#ref(2012b/Member/ddnp/Mission1/course_1.jpg, 100%, 課題...
S字カーブ、直角カーブ、急カーブ、交差点と、入り組んだコー...
S字カーブの真ん中に障害物を設置し、それに触れないように進...
まずは、シュートの前段階として、スタート地点からゴール地...
そのためにクリアしなければいけない課題として、以下のこと...
-S字の障害物を避けるため、本体を小さくする。
-急カーブを曲がれるよう工夫する。
-交差点と直角カーブの区別を明確にする。
-ゴールを判別し、ゴールに標準を合わせる。
とりあえず、次の項では本体を小さくすることに焦点を当てて...
***その前にデフォ子の追悼式[#see_you_default_bot]
この日、授業の初めの方から付き合って下さったデフォ子様の...
お集まりの皆様方には、デフォ子様との想い出の日々が以下都...
***本体を小さくする[#make_the_robot_small]
とりあえず、実際に本体を作らず、紙面上で実験。
ブロックやシャフトを使い、縦長、横長など様々な大きさの枠...
-デフォ子に使われているタイヤは太すぎて、使うには難しい。
-左右のモーターが多少大きいので、付ける位置を近づけるなど...
-旋回する時中心となる点から、機体が前や後ろに長すぎると、...
まぁぶっちゃけ全体的にコンパクトになってればなってるほど...
結果的に、以下の三点を実施。
-二輪に変更
--実際にはバランスを取るために三輪。ただ三番目のタイヤは...
-モーター同士をできる限りくっつけてみた。
--しかし不安定性が増したので、上下に固定用のブロックを足...
--同時に、車軸を固定するパーツ&ref(2012b/Member/ddnp/Miss...
-RCX本体が縦長なので、デフォ子のように寝かせるのでなく、...
--中心でなく後ろ側に寄せて立てることで、前側に光センサー...
--嬉しいことに、寝かせるより立てたほうが、本体ががっちり...
--センサーが離れてしまったのでコードを延長したのだが、延...
--ただし、これによって重心が後ろに傾いた。何も影響がなけ...
--欠点としては、スイッチ類が押しにくかったり、View値が読...
これを考慮して出来た機体がこれ。
#ref(2012b/Member/ddnp/Mission1/half_of_robot.jpg, 100%, ...
さらに、光センサーの位置についても考慮してある。
出来るだけ正確に読み取れるように、地面に近づけ、本体の中...
このことにより、光センサーが本体の中心からより離れている...
つまり、
#ref(2012b/Member/ddnp/Mission1/degree_difference.gif, 10...
上図のように、光センサーの位置によって、回転した時間が同...
これはつまり、回転の中心に光センサーを寄せるほど、ゆっく...
よって、多少早い動作をしても、光センサーの読み取りミスが...
ただし、欠点もある。近くに寄せ過ぎると、光センサーの読み...
ラインがほぼ真下にあるのに読み込めないという事態が発生し...
今回の機体では、大体写真の位置で読み取るよう設定した。
&ref(2012b/Member/ddnp/Mission1/read_distance.jpg, 100%, ...
これにて基盤は完成。
***ゴールを目指す[#aim_the_goal]
機体は出来た。ということで、テストも兼ねてラインをトレー...
試しに、先ほど作った境界トレスのプログラムを動作させてみ...
唯一変更した点は、光センサーのしきい値くらいである。
それにしても、再利用できてよかった。
さて、ラインはトレースできても、ゴールまで行きつけなけれ...
この時点で、自分がクリアすべき課題は
+コース途中の交差点をどう通過するか
+ゴールを判定するにはどうするか
の2つであった。
#ref(2012b/Member/ddnp/Mission1/course_2.jpg, 100%, 通過...
まずは、途中の通過すべき交差点の処理を考える。
普通なら、交差点であることを判別して、黒い線を跨ぐような...
ちょっとした思いつきで、交差点の判別をしないで超えてみる...
使用するのは、前に作った黒トレスのプログラム。
交差点に突入する前に、黒ライントレスを開始し、
通り過ぎた頃に黒ライントレスから境界ライントレスに移行す...
ただし、黒ライントレスから境界ライントレスに移行する時に...
本来は黒ライントレス時の回転方向を決定する変数を利用して...
//--------------------------//
//
// 定数定義
//
//--------------------------//
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 45 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define DEEPWHITE 53 // 濃い白のしきい値
#define CHANGE_BLACK 1100 // 境界→黒トレス切り替え時間
#define CHANGE_OUTLINE 400 // 黒→境界トレス切り替え時間
#define SEESIGHT 120 // 黒トレス時のライン捜索時間
//---------------------------//
//
// メイン処理
//
//---------------------------//
task main()
{
//-------------------------//
// 初期化
//-------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
ClearTimer(0); // 境界・黒ライントレスの入れ替わり...
//----------------------//
// 変数定義
//----------------------//
int rotate = 1; // 回転方向を指定 奇数は右回り
int times = 0; // 旋回回数を記憶 かつ視野を増加させる
//-------------------------//
// ライントレス
//-------------------------//
while(true)
{
// 指定時間外かつライン左側で輪郭線ライントレス
if((FastTimer(0) <= CHANGE_BLACK) || (FastTimer(0) >= ...
{
//---------------------------//
// 輪郭線ライントレス
//---------------------------//
・・・
・・
・
}
// 指定時間内かつライン右側で黒ライントレス
else if(FastTimer(0) > CHANGE_BLACK || FastTimer(0) < ...
{
//----------------------------//
// 黒ライントレス
//----------------------------//
・・・
・・
・
}
}
}
ライントレス処理は上記とほぼ同じなので省略。
途中、プログラムの入ったUSBメモリをなくして作りなおしたの...
USBメモリの管理には気をつけましょう。
これにて、目論見どおり交差点を渡ることが出来た。
#ref(2012b/Member/ddnp/Mission1/course_3.jpg, 100%, シュ...
次は、ゴール、つまりシュートする位置を判別出来るようにし...
といっても、シュートする位置を示す特徴が、交差点くらいし...
そこで、境界ライントレス時に、旋回する時間が長い場合は、...
#ref(2012b/Member/ddnp/Mission1/course_4.jpg, 100%, コー...
地形的な特徴と、黒ライントレスの処理があることから、黒側→...
ゴールまで計3つのコーナーを通過することになるので、3つ...
定数と変数を付け加え、
#define CORNER_TIME 40 // コーナーと判別する時間
int backturn = 0; // コーナーを曲がるのにかかった時間...
int corner_times = 0; // 曲がったコーナーの数
境界ライントレスの、反時計回り旋回時のプログラムをこのよ...
// 黒側→白側
else if(SENSOR_2 < DEEPBLACK)
{
ClearTimer(2); // コーナーを曲がっている時間を測定
// 白側へ旋回
while(SENSOR_2 < BLACK)
{
// 左旋回
OnFwd(OUT_C);
OnRev(OUT_A);
}
// 曲がるのに時間がかかった場合、直角だと判断
if(FastTimer(2) >= CORNER_TIME)
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時間...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知ら...
}
}
これで直角を判別してくれるようになった。
直角を判定した時には、機体はコーナーを曲がりきってしまっ...
シュート処理するときにはゴールの方向へ機体を向ける必要が...
そこで、曲がるまでにかかった時間を記憶して、シュート処理...
しかし、処理の遅延なのか、実際には多少ズレが生じるので、...
さらに、シュート処理に移るときは、ライントレスをする必要...
コーナーを3回以上曲がった場合に処理を抜けるように、ルー...
つまり、
while(true)
の一文を、
while(corner_times < 3)
に変更する。
ここで、再び問題が発生。
コーナーで、「2回以上コーナーを曲がったことにしてしまう...
どうやら、トレスの方法に、「色が濃い場所までいってから、...
コーナーに入る角度によって、コーナーのトレスに2,3回軌...
コーナーと判定する時間を45くらいまで増やしてみたところ、...
定数でどうにかするには心もとない。
ということで、一度コーナーを判定したら、しばらくコーナー...
定数と変数をさらに追加。
#define CORNER_WAIT 1000 // コーナー判定を無視する時間
int corner_wait = -800; // コーナーを曲がった時の時間
コーナー判定プログラムは次のようにした。
// コーナーを判定且つ以前コーナーに来てからしばらく経過...
if(FastTimer(2) >= CORNER_TIME && FastTimer(0) >= corner...
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時間...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知ら...
corner_wait = FastTimer(0); // コーナーを曲がったとき...
}
コーナーを曲がるときの条件式に合わせて、コーナーを曲がっ...
コレがないと一個目のコーナーを判別できない可能性が高い。
改善前もコーナー以外で誤動作することはなかったが、こうす...
ところがどっこい今度も謎の問題が発生
図の部分だけ、曲がりきることが出来ない問題が発生した。
#ref(2012b/Member/ddnp/Mission1/course_5.jpg, 100%, 謎の...
定数値をいじってみるも、問題が解決しないので、まさかと思...
この場所だけ、白の部分が50という値を観測。他が55なので、...
探ってみたところ、どうやら自身の影が光センサーを邪魔して...
光センサーは、色ではなく、跳ね返ってくる光の強弱でライン...
実際、日光のあたる場所に連れて行くと、白い部分で最大89を...
直射日光、蛍光灯や本体自身の影等、あらゆる条件によって読...
光センサーの扱いは注意したほうが良いということを実感した...
発表間近でこれを経験すると意外と焦るので気をつける。
以上で、ライントレスは完成。スタートからゴールまでの道は...
**ボールを持ち運んで、シュートする。[#carrying_and_shooti...
***まずは、ボールをどうやって持つか。[#how_to_chatch_the_...
正直、これを考えるのが一番楽しかったかもしれない。
腕で捕まえる、上の方に持ちあげる等、いろいろとアイディア...
#ref(2012b/Member/ddnp/Mission1/arm_1.jpg, 100%, ピンポン...
#ref(2012b/Member/ddnp/Mission1/arm_catch.jpg, 100%, ピン...
ワームが回転し、光センサーとワームを回すシャフト自身がピ...
しかも、ワームを逆回転させればシュートも出来るという優れ...
つかむ時、写真手前側にボールが逃げないよう、5つ穴のブロ...
光センサーの裏を使って挟み込むことは、省スペースにも繋が...
本当は、ワームの上にただボールを載せておくだけで、シュー...
ボールを抱え込んでワーム上に設置するのが難しかったのでこ...
***ボールを持って、シュートさせる[#how_to_shoot]
パーツは出来たので、後はプログラム。
しかし実際は、モーターを前後に動かすだけでキャッチもシュ...
つかむプログラム
OnRev(OUT_B); // 腕内側回転
Wait(PICKUPTIME); // 少しの間回転
Off(OUT_B); // 停止
シュートするプログラム
SetPower(OUT_B, 5); // パワー強め
OnFwd(OUT_B); // シュート
Wait(100);
Off(OUT_ABC); // シュート停止とともに全動作を停止
パワーを強めにしてみたはいいものの、あまり実感は出来なか...
以上で、ボールキャッチからシュートまでの処理は完成。
*まとめ[#conclusion]
**今回使った機体[#used_robot]
#ref(2012b/Member/ddnp/Mission1/robot.jpg, 100%, 使ったロ...
そういえば名前とか付けてない…。
全体的に強度が足りない時期があったので、細かいブロックな...
その結果、ロゴでよくある「あのパーツが足りない」現象が起...
薄い2*2ブロックとか1*1ブロックとか高さ調整ですごい使った...
そしてその小型ブロックのせいで、分解時に爪を痛めたのもい...
**実際に使用したプログラム[#used_program_code]
//------------------------------------------------//
//
// Title : Carrying-ball
//
// Use : ボールを拾い上げた後、
// 進行方向に対して左側の境界線を
// ライントレス
// 障害を乗り越えた後、
// ボールをゴールにシュート
//
//------------------------------------------------//
/*
############################
定数定義
############################
*/
// トレス判定系
#define DEEPBLACK 40 // 濃い黒のしきい値
#define BLACK 45 // 黒のしきい値
#define WHITE 50 // 白のしきい値
#define DEEPWHITE 53 // 濃い白のしきい値
#define CHANGE_BLACK 1150 // 境界→黒トレス切り替え時間
#define CHANGE_OUTLINE 400 // 黒→境界トレス切り替え時間
#define CORNER_WAIT 1000 // コーナー判定を無視する時間
// 境界トレス用
#define CORNER_TIME 38 // 直角判定する時間
// 黒トレス用
#define SEESIGHT 120 // 黒トレス時のライン捜索時間
// ピンポン玉処理系
#define PICKUPTIME 30 // ピンポン玉取得のための腕回転...
#define SHOOT 600 // ピンポン玉発射時の腕回転時間
// 音符
#define C4 523
#define E4 659
#define F4 698
#define G4 784
#define A5 880
#define B5 988
#define C5 1046
#define D5 1175
#define E5 1318
#define F5 1397
#define G5 1568
#define C6 2093
/*
#############################
# マクロ
#############################
*/
#define RotateRight OnFwd(OUT_A); OnRev(OUT_C); // 時計...
#define RotateLeft OnFwd(OUT_C); OnRev(OUT_A); // 反時...
/*
#############################
# 右旋回ルーチン
#############################
*/
sub rotateright()
{
OnFwd(OUT_A);
OnRev(OUT_C);
}
/*
#############################
# 左旋回ルーチン
#############################
*/
sub rotateleft()
{
OnFwd(OUT_C);
OnRev(OUT_A);
}
/*
###############################
# 歓喜の舞
###############################
*/
sub kirby_clear ()
{
// 超舞う
RotateLeft;
PlayTone(F4, 10); Wait(13);
PlayTone(G4, 10); Wait(13);
PlayTone(A5, 10); Wait(13); RotateRight;
PlayTone(B5, 10); Wait(13);
PlayTone(A5, 10); Wait(13);
PlayTone(B5, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(C5, 10); Wait(25);
PlayTone(E4, 10); Wait(13);
PlayTone(E4, 10);
Off(OUT_B);Wait(13); Wait(25);
RotateRight;
PlayTone(F4, 10); Wait(13);
PlayTone(G4, 10); Wait(13);
PlayTone(A5, 10); Wait(13); RotateLeft;
PlayTone(B5, 10); Wait(13);
PlayTone(A5, 10); Wait(13);
PlayTone(B5, 10); Wait(13); Off(OUT_AC); OnRev(OUT_B);
PlayTone(C5, 10); Wait(25);
PlayTone(E4, 10); Wait(13);
PlayTone(C4, 10);
Off(OUT_B); Wait(13); Wait(25);
RotateLeft;
PlayTone(F4, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(G4, 10); Wait(13); Off(OUT_B); RotateRight;
PlayTone(A5, 10); Wait(13); Off(OUT_AC); OnRev(OUT_B);
PlayTone(B5, 10); Wait(13); Off(OUT_B); RotateRight;
PlayTone(A5, 10); Wait(13); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(B5, 10); Wait(13); Off(OUT_B); RotateLeft;
PlayTone(C5, 22); Wait(25); Off(OUT_AC); OnFwd(OUT_B);
PlayTone(G4, 10); Wait(13); RotateRight;
PlayTone(E4, 22); Wait(25);
PlayTone(G5, 10); Wait(13);
PlayTone(F5, 22); Wait(25);
PlayTone(E5, 10); Wait(13);
PlayTone(D5, 22); Wait(25);
PlayTone(E5, 10); Wait(13); Off(OUT_AC);
PlayTone(C5, 35); Wait(38);Off(OUT_B);
PlayTone(C6, 10); Wait(13);
}
/*
################################
#
# メイン関数
#
################################
*/
task main()
{
//-------------------------//
// 初期化設定
//-------------------------//
SetSensor(SENSOR_2, SENSOR_LIGHT); // 光センサーを設定
SetPower(OUT_ABC, 2); // モーター動力を少し低く指定
ClearTimer(0); // プログラムを走らせだしてからの時...
//-------------------------//
// 変数宣言
//-------------------------//
int clockwise = 3; // 黒トレス時の回転方向を記憶 奇...
int rad_increase = 1; // 黒トレス時の反転回数 倍角...
int corner_times = 0; // コーナーを曲がった回数
int backturn = 0; // シュート時の角度補正用
int corner_wait = -800; // コーナーを曲がった時の時間
//-------------------------//
// 玉取得
//-------------------------//
OnRev(OUT_B); // 腕内側回転
Wait(PICKUPTIME); // 少しの間回転
Off(OUT_B); // 停止
//-------------------------//
// ライントレス
//-------------------------//
// コーナーを3回以上回るまでライントレス処理を実行
while(corner_times < 3)
{
// 指定時間外かつライン左側で輪郭線ライントレス
if((FastTimer(0) <= CHANGE_BLACK) || (FastTimer(0) >= ...
{
//---------------------------//
// 輪郭線ライントレス
//---------------------------//
// 白側→黒側
if(SENSOR_2 > DEEPWHITE)
{
// 薄くなるまで回転
while(SENSOR_2 > WHITE)
{
rotateright(); // 右小回転
}
}
// 黒側→白側
else if(SENSOR_2 < DEEPBLACK)
{
ClearTimer(2); // 旋回時間を測定
// 薄くなるまで回転
while(SENSOR_2 < BLACK)
{
rotateleft(); // 左小回転
}
// コーナーを判定且つ以前コーナーに来てからしばらく...
if(FastTimer(2) >= CORNER_TIME && FastTimer(0) >= co...
{
backturn = FastTimer(2)+5; // 曲がるのにかかった時...
corner_times++; // コーナーの数を数える
PlayTone(440, 30); // コーナーの数を数えたことを知...
corner_wait = FastTimer(0); // コーナーを曲がったと...
}
}
else
{
OnFwd(OUT_AC); // 常に前進
}
}
// 指定時間内かつライン右側で黒ライントレス
else if(FastTimer(0) > CHANGE_BLACK || FastTimer(0) < ...
{
//----------------------------//
// 黒ライントレス
//----------------------------//
// 完全に白い場所を感知した時、方向修正
if(SENSOR_2 >= DEEPWHITE)
{
rad_increase = 1; // 倍角変数を初期化
// 黒ラインに入るまで処理。黒ラインが見当たらないと...
while(SENSOR_2 >= DEEPWHITE && rad_increase <= 3)
{
ClearTimer(1); // タイマーリセット
// 黒ラインに入るまで、且つ旋回時間が過ぎるまで旋回
while((SENSOR_2 >= BLACK) && (FastTimer(1) <= SEESI...
{
// 時計回り
if(clockwise % 2)
{
rotateright();
}
// 反時計回り
else if(!(clockwise % 2))
{
rotateleft();
}
}
// 白い場所から脱出できなかった場合
if(SENSOR_2 >= BLACK)
{
clockwise ++; // 回転方向を変更
rad_increase += 1; // 回転時間を増加
}
}
}
else
{
OnFwd(OUT_AC); // 常に前進
}
}
}
// 止まる
Off(OUT_AC);
//-----------------------------//
// シュート処理
//-----------------------------//
// 曲がった分バック
rotateright();
Wait(backturn);
Off(OUT_AC);
// 腕を回転
SetPower(OUT_B, 5);
OnFwd(OUT_B);
Wait(100);
// 動作を停止
Off(OUT_ABC);
// 準備体操
SetPower(OUT_B, 2);
// 歓喜の舞
Wait(300);
kirby_clear();
}
上で記述しなかった改善点がいくつか存在する。
-境界→黒トレスまでの秒数を調整。1150が調子良かった様子。
-コーナー判定の秒数を短くした
--可能な限り確実にコーナーを判定できるよう、40〜35を試し...
-右回転、左回転をサブルーチン化
--授業資料に、サブルーチンを使用すると、何回か出てくる処...
-黒トレス時の回転方向を指定する変数の初期値を変更
--そういえば、int型では小数切り捨てだったことを思い出した...
-全体的にコメントの書き方を一新した
--自分にとって、毎度のことながらコメントで汚してる感が否...
-変数や定数の名前が一部変更
--前述のUSBメモリ紛失事件や、プログラムの見直しなど様々な...
-歓喜の舞を実装
--シュート後に音楽を鳴らしながら踊り狂う命令を追加。趣味...
--やってみる人はご自由に。舞は再現できなくても音はまんま...
**感想 [#k14bc134]
楽しみながらスムーズに完成させられたように思う。
プログラミングについては、趣味で多少やっているので、困っ...
個人的な狙いとして、如何にして綺麗にコードを書くかという...
そこができていれば今回の課題はソフトウェア的にはOKだと感...
ハードウェア面は、不慣れなLEGOに悪戦苦闘した割りには、よ...
それ以前に、LEGOの楽しさに触れられたことの方に充実感を感...
どうやって目的の動作をするパーツを作るかという醍醐味に引...
次は、これ以上にアイディアのつまったロボットを組み立てら...
全体的に見ればまだまだ突き詰められる場所があるように感じ...
***余談 [#g417b242]
歓喜の舞に、Pythonで音階の周波数を計算してA2〜A7までつら...
忘れすぎて嫌になるはずの「;」が、Pythonだと逆に無くて落...
だいぶNQCに慣れてきたようなので、これから後の課題もがんば...
分解している最中に、こんな表情を見せた。
名前はスマイリーとかでいいかもしれない。:)
#ref(2012b/Member/ddnp/Mission1/smile.jpg, 100%, スマイル)
ページ名: