[[2005/C4]] [[2005/C4/ロボコン]] * 目次 [#fe16358d] #contents * NQCのプログラム [#u8e1028a] * プログラムの解説 [#za624f6a] - プログラムの構成 ~ &ref(robots-prog-chart.odg); ~ &ref(robots-prog-chart-img.png); ~ cacher.nqc .... 主に箱をキャッチするためのプログラム。片方のRCXのため。 ~ trace-cource.nqc ... 主にライントレースをするためのプログラム。もう一方のRCXのため。 ~ turn.nqc ... trace-cource.nqcで使われているプログラム。[箱を取る/箱を置く]などの動作の直前・直後の処理が書かれている。 ~ command.nqc ... trace-cource.nqcとcacher.nqcに使われているプログラム。赤外線通信を使い、二台のRCX間で通信するときのプロトコル(通信規則)を定義している。 ~ command.h ... command.nqcに使われているヘッダファイル。通信時に通信開始・通信終了など常数を定義している。 ~ command-proc.h ... trace-cource.nqcとcacher.nqcに使われているヘッダファイル。[箱を取る・箱を落とす・ライントレースする]などの動作を伝えるときに使う常数を定義している。 - 赤外線通信の詳細 ~ &ref(robots-ir-connect-chart.odg); ~ &ref(robots-ir-connect-chart-img.png); ~ 通信によって、二台のRCXは同期する。 ~ 箱を取ったり置いたりするRCXが、全体の動作を管理し、もう一台に動作一つ一つを開始するように指示を出している。 - 二台のRCX : プログラムの分担 ~ ライントレースをするRCXは、機械の移動を覚えるだけで精一杯の感があった。 ~ 最初に大雑把に全体をコーディングしたところ、サブルーチンの数がNQCの制限の8個を越えてしまっていたからだ。 ~ ライントレースをするだけで片方は限界なので、もう一方のRCXに全体の進行や、箱を取ったり置いたりする動作を任せることとなった。 - ライントレースの制御の詳細 ~ &ref(cource-number.png); ~ ロボコンのフィールドのそれぞれの区間に番号を割り当てる。 ~ 機械は、変数に自分のいる位置を覚えているのでそれをもとにライントレースをする。 ~ 問題点 : 最初の割り当て方の問題 ~ 黒い線で分けられた間の空間にそれぞれ一つずつ数字を割り当てたが、 ~ それよりも、黒い線にそれぞれ数字を割り当てた方がプログラムしやすかったかもしれない。 ~ 小さな仕様 : 線の直後 ~ ライントレースや、細かな移動の動作(箱を取る、置くなどの動作以外)の直後は黒い線の直後の位置に必ずくるようにしている。 ~ この仕様通りにしていなかったのが、区間5の直後から区間4を超えて区間3に入る部分であった。 ~ 区間3に入った後は区切る線をずっと超えたところに来ていた。 ~ これにより、区間3から箱を取る動作は、ほかの区間1や区間2とは違う動作となり、調整に苦心した。 ~ 最終的には、区間3で箱を取る動作をする前に、おかしな位置にいる機械を区間3の区切る線の直後に配置する動作を加えて対処した。 * プログラム作成の苦労した点 [#y9acc66b] - 赤外線通信 ~ 初期のcommand.nqcでは、通信が失敗することが多々あった。 ~ ロボコン当日の早朝六時から、スクラッチから書き直した。 ~ これにより、少しは通信が失敗する確立が減った。 ~ しかし、前より通信が完了するまでの時間が遅くなった。 ~ 調整によりできるだけ短時間で通信が完了するように気を配った。 - 安定していたライントレース ~ ロボコン前日まではライントレースはかなり安定していた。 ~ しかし、すこし動作が遅いのが難点であった。 ~ すると、当日にとても速いスピードで走っているチームを発見した。 ~ そして、自分たちも負けていられないと、ギヤ比を変更しプログラムを変更し、とても速いロボットにした。 ~ しかし、そう簡単に速くかつ安定なライントレースは実現せず、 ~ スリップやコースアウトなどのトラブルを背負う機械になってしまった。 ~ 結局はロボコンで速いまま出場させたが、終わった後に遅い動作に戻したところ、ほぼ正確な動作を示した。 ~ 明らかにこちらの安定性を取るべきであった。 - パソコンのプログラミングと違う ~ 本当にブレークポイントを指定したかった。 ~ debug用のprintもしたかった。 ~ だが、NQCにはそんな機能はない。 - 再現しないプログラムエラー ~ ロボットはタイヤなど、物理的な要素が予期しない動作を招くのか、 ~ 一度発生しても、二度と再現しないバグが多々あった。 - 厳しいNQCの制限 ~ サブルーチンの数の制限や、ファンクションを用いるとコンパイル後に巨大になる、 ~ サブルーチンからサブルーチンを呼べない、などのNQCの制限に開発は阻まれた。 ~ 最初は綺麗だったコードが、対策のため一気にtask main(){}に集中しもうゴチャゴチャになった。 ~ 長すぎる、冗長な、ひと続きのルーチンはどこに何があるのかさっぱりわからず、デバッグに苦労した。 - 編集しにくい環境 ~ 10番教室にはMACがあり、チームで数台のMACを使える。 ~ しかし、複数のMAC間でファイル共有の方法などのやり方がわからず、 ~ 結局は大体、一台のMACでのみ編集する羽目になった。 ~ CVSなど同時に複数の人が編集できる環境があれば良いと感じた。 ~ (CVSなど使ったことがないのにこんな事を言うのも変だ。) ~ (コードの編集履歴などを見れるようになっていれば、なお理想的であるし。) - 遅いNQCエディタ ~ 個人のiBookでファイルを編集していたところ、 ~ 長いプログラムの入力に対する、画面の反応がだんだん鈍くなり、最後には使い物にならない程遅くなった。 ~ MacNQCは使えない。 ~ また、RCXの電池の電圧をどこで見たらいいかわからなかったのもきつい。 ~ (コマンドラインのnqcコマンドはなかった。) - 空腹 ~ 空腹の状態で、解決できない問題を直すように試み続けるのは得策でなかった。 ~ 食べ物を食べて血糖値を上げれば問題解決が5倍速になる。 - 無駄なmake ~ NQCは制限が厳しく、自由にプログラムを組めないのを、これまでのゼミでの経験で少しはわかっていた。 ~ 不安の中、brickosというNQCより自由度が高い、クロスコンパイラをつかうNQC似の言語を発見し、個人的に、苦労して開発環境をインストールした。 ~ ところが、なれたNQCが安心だろうということでbrickosは使わないことになった。 ~ makeの苦労が水の泡。 * ソースコード [#n4ae9adb] * ソースコードを見るときの注意 [#ge9beec3] -コメント文について ~ 今はまだコメント文が入っていない(2006/02/08現在)。 ~ コードには、大量の要らないコメントアウトされた命令文がある。 ~ 見やすくするために、将来それらを消すかもしれない。 * コード : trace-cource.nqc [#b73c9a4e] #define LINETRACE #define LIGHT_L SENSOR_3 #define LIGHT_C SENSOR_2 #define LIGHT_R SENSOR_1 #define L OUT_C #define R OUT_A #define LIGHT_THRESHOLD 40 #if 1 #define FWD OnFwd #define REV OnRev #else #define FWD OnRev #define REV OnFwd #endif int pcounter=0; int c_point1=1,c_point2=0,c_point3=0; int place=1; //put first position!!!!!!!! int tmp; int startplace; int endplace; #include "command-proc.h" #include "turn.nqc" #include "command.nqc" task main(){ ClearMessage(); SetSensor(LIGHT_L,SENSOR_LIGHT); SetSensor(LIGHT_R,SENSOR_LIGHT); SetSensor(LIGHT_C,SENSOR_LIGHT); while(1){ recive(); if(command==COMMAND_GO){ startplace=place; recive(); endplace=command; if(startplace<endplace){ //PlaySound(SOUND_CLICK); //PlaySound(SOUND_CLICK); while(place!=3 && place<endplace){ //gostrait(); linetrace(); find_bar(); place++; } if(place<endplace){ linetrace(); find_light(); place++; } if(place<endplace){ //godiffzone3to5(); //SetPower(L,4); //SetPower(R,4); Off(L); FWD(R); until(LIGHT_R<LIGHT_THRESHOLD); REV(L); //FWD(R); until(LIGHT_R>LIGHT_THRESHOLD); Off(R); REV(L); until(LIGHT_L>LIGHT_THRESHOLD); // REV(L); REV(R); until(LIGHT_R<LIGHT_THRESHOLD); REV(L); REV(R); until(LIGHT_R>LIGHT_THRESHOLD); REV(L); REV(R); until(LIGHT_L<LIGHT_THRESHOLD || LIGHT_R<LIGHT_THRESHOLD); REV(L); REV(R); until(LIGHT_R>LIGHT_THRESHOLD); /*REV(L+R); Wait(15); Off(L); FWD(R); until(LIGHT_C<LIGHT_THRESHOLD); REV(L+R); until(LIGHT_C>LIGHT_THRESHOLD); until(LIGHT_C>LIGHT_THRESHOLD); REV(R);FWD(L); until(LIGHT_R>LIGHT_THRESHOLD && LIGHT_L>LIGHT_THRESHOLD); */ Off(L+R); //SetPower(L,OUT_FULL); //SetPower(R,OUT_FULL); place++; } if(place<endplace){ //SetPower(L,3); //SetPower(R,3); linetrace(); find_bar(); //SetPower(L,OUT_FULL); //SetPower(R,OUT_FULL); place++; } while(place!=9 && place<endplace){ //gostrait(); linetrace(); find_bar(); place++; } //place--; }else if(endplace<startplace){ //PlaySound(SOUND_CLICK); while(place!=5 && place>=endplace){ //gostrait(); linetrace(); find_bar(); place--; } if(place>=endplace){ //SetPower(L,3); //SetPower(R,3); linetrace(); find_light(); FWD(L+R); until(LIGHT_L<LIGHT_THRESHOLD || LIGHT_R<LIGHT_THRESHOLD); Off(L+R); find_light(); //SetPower(L,OUT_FULL); //SetPower(R,OUT_FULL); place--; } if(place>=endplace){ //godiffzone5to3(); //SetPower(L,4); //SetPower(R,4); REV(L);Off(R); until(LIGHT_C<LIGHT_THRESHOLD); //REV(L);Off(R); //until(LIGHT_C>LIGHT_THRESHOLD); REV(L+R); until(LIGHT_L<LIGHT_THRESHOLD); REV(L+R); until(LIGHT_L>LIGHT_THRESHOLD); FWD(L);REV(R); //Wait(25); //Wait(88); until(LIGHT_C>LIGHT_THRESHOLD); until(LIGHT_C<LIGHT_THRESHOLD); until(LIGHT_C>LIGHT_THRESHOLD); //REV(L+R); //Wait(20); //until(LIGHT_C<LIGHT_THRESHOLD); Off(L+R); //SetPower(L,OUT_FULL); //SetPower(R,OUT_FULL); place--; } if(endplace==3){ //SetPower(L,OUT_HALF); //SetPower(R,OUT_HALF); linetrace(); FWD(L+R); //until(LIGHT_L>LIGHT_THRESHOLD && LIGHT_R>LIGHT_THRESHOLD); //until(LIGHT_L<LIGHT_THRESHOLD && LIGHT_R<LIGHT_THRESHOLD); until(LIGHT_L>LIGHT_THRESHOLD && LIGHT_R>LIGHT_THRESHOLD); until(LIGHT_L<LIGHT_THRESHOLD && LIGHT_R<LIGHT_THRESHOLD); REV(L+R); until(LIGHT_L>LIGHT_THRESHOLD && LIGHT_R>LIGHT_THRESHOLD); //SetPower(L,OUT_FULL); //SetPower(R,OUT_FULL); Off(L+R); } while(place!=0 && place>endplace){ //gostrait(); linetrace(); find_bar(); place--; } //place++; } place=endplace; }else if(command==COMMAND_GET_READY){ if(place==1){ c_point1++; }else if(place==2){ c_point2++; }else if(place==3){ c_point3++; } turn_back=0; turn(); }else if(command==COMMAND_TURN_LINE){ turn_back=1; turn(); } } } sub linetrace(){ //PlaySound(SOUND_DOUBLE_BEEP); REV(L+R); while(true){ if(LIGHT_R<LIGHT_THRESHOLD && LIGHT_L<LIGHT_THRESHOLD && LIGHT_C<LIGHT_THRESHOLD){ Off(L+R); //SetPower(L+R,OUT_FULL); break; } if(LIGHT_C<LIGHT_THRESHOLD && LIGHT_L<LIGHT_THRESHOLD){ Off(L); REV(R); }else if(LIGHT_C<LIGHT_THRESHOLD && LIGHT_R<LIGHT_THRESHOLD){ Off(R); REV(L); }else{ if(LIGHT_R<LIGHT_THRESHOLD){ Off(R); ////SetPower(L,OUT_HALF); }else{ ////SetPower(L,OUT_FULL); REV(R); } if(LIGHT_L<LIGHT_THRESHOLD){ Off(L); ////SetPower(R,OUT_HALF); }else{ REV(L); ////SetPower(R,OUT_FULL); } } } } /*sub linetrace(){ //PlaySound(SOUND_DOUBLE_BEEP); REV(L+R); while(true){ if(LIGHT_R<LIGHT_THRESHOLD && LIGHT_L<LIGHT_THRESHOLD && LIGHT_C<LIGHT_THRESHOLD){ Off(L+R); //SetPower(L+R,OUT_FULL); break; }else if(LIGHT_C<LIGHT_THRESHOLD && LIGHT_L>LIGHT_THRESHOLD &&LIGHT_R>LIGHT_THRESHOLD){ REV(L+R); }else if(LIGHT_C<LIGHT_THRESHOLD && LIGHT_L<LIGHT_THRESHOLD){ //Float(L); //FWD(L); Off(L); REV(R); }else if(LIGHT_C<LIGHT_THRESHOLD && LIGHT_R<LIGHT_THRESHOLD){ //Float(R); //FWD(R); Off(R); REV(L); }else if(LIGHT_C>LIGHT_THRESHOLD){ if(LIGHT_R<LIGHT_THRESHOLD && LIGHT_L>LIGHT_THRESHOLD){ FWD(R); REV(L); ////SetPower(L,OUT_HALF); }else if(LIGHT_R>LIGHT_THRESHOLD && LIGHT_L<LIGHT_THRESHOLD){ ////SetPower(L,OUT_FULL); REV(R); FWD(L); //Off(L); } }else{ // PlaySound(SOUND_DOWN); } } }*/ sub find_light(){ REV(L+R); until((LIGHT_R>LIGHT_THRESHOLD) && (LIGHT_L>LIGHT_THRESHOLD) && (LIGHT_C>LIGHT_THRESHOLD)); Off(L+R); } sub find_bar(){ REV(L+R); while((LIGHT_R<LIGHT_THRESHOLD) && (LIGHT_L<LIGHT_THRESHOLD) && (LIGHT_C<LIGHT_THRESHOLD)); Off(L+R); } * コード : turn.nqc [#ec069005] #define GO_STRAIGHT REV(OUT_A+OUT_C); #define TURN_LEFT FWD(OUT_C);REV(OUT_A); #define BACK_STRAIGHT FWD(OUT_A+OUT_C); #define TURN_RIGHT FWD(OUT_A);REV(OUT_C); #define BATT_POWER 8/12 // 8/13 int turn_back; sub turn() { int tmp=0,tmp2; if(place==1){ tmp2=c_point1; }else if(place==2){ tmp2=c_point2; }else if(place==3){ tmp2=c_point3; }else{ tmp2=0; } if(turn_back){ PlaySound(SOUND_DOWN); BACK_STRAIGHT if(tmp2==1){ tmp=5*BATT_POWER; }else if(tmp2==2){ tmp=60*BATT_POWER; }else{ tmp= 90*BATT_POWER; } Wait(tmp); if(place<4){ TURN_LEFT }else{ TURN_RIGHT } //Wait(95*BATT_POWER); until(LIGHT_C<LIGHT_THRESHOLD); Off(OUT_A+OUT_C); }else{ PlaySound(SOUND_UP); int sec_first; int sec_second; SetPower(L,2); SetPower(R,2); FWD(L+R); until(LIGHT_L<LIGHT_THRESHOLD || LIGHT_R<LIGHT_THRESHOLD); Off(L+R); if(LIGHT_L<LIGHT_THRESHOLD){ REV(L); until(LIGHT_L>LIGHT_THRESHOLD); Off(L); }else if(LIGHT_R<LIGHT_THRESHOLD){ REV(R); until(LIGHT_R>LIGHT_THRESHOLD); Off(R); } REV(L+R); until(LIGHT_L>LIGHT_THRESHOLD &&LIGHT_R>LIGHT_THRESHOLD); Off(L+R); SetPower(L,OUT_FULL); SetPower(R,OUT_FULL); if(place==2){ sec_first=250; sec_second=80; }else if(place==3){ sec_first=250; sec_second=80; }else if(place==1){ sec_first=250; sec_second=80; }else if(place>4){ sec_first=239; sec_second=109; } //if(place==3){ // //} GO_STRAIGHT Wait(sec_first*BATT_POWER); if(place<4){ TURN_LEFT }else{ TURN_RIGHT } Wait(140*BATT_POWER); if(tmp2==1){ tmp=sec_second*BATT_POWER*1/16; }else if(tmp2==2){ tmp=sec_second*BATT_POWER*7/10; }else{ tmp=sec_second*BATT_POWER; } GO_STRAIGHT Wait(tmp); Off (OUT_A+OUT_C); } } * コード : cacher.nqc (変なファイル名) [#p22f1e44] /*#define GREEN 44 #define BLACK 30 #define WHITE 50 */ #include "command.nqc" #include "command-proc.h" #define LIGHT SENSOR_1 #define ARM OUT_A #define HAND OUT_B task main() { ClearMessage(); SetSensor(LIGHT,SENSOR_LIGHT); Wait(10); //SendMessage(COMMAND_TURN_LINE); //command=COMMAND_TURN_LINE; //send(); int i,dest,src,tmp; int c_place1=0,c_place2=0,c_place3=0; src=1;///// for(i=0;i<9;i++){ Wait(450); if(src==1){ c_place1++; tmp=c_place1; }else if(src==2){ c_place2++; tmp=c_place2; }else{ c_place3++; tmp=c_place3; } //tsukamu(); if(src==1 || src ==3){ //SetPower(ARM,OUT_FULL); OnFwd(ARM) ; Wait(30); OnRev(HAND); Wait(150); SetPower(ARM,OUT_FULL); OnRev(ARM); Wait(90); //SetPower(HAND,OUT_FULL); Off(ARM); //Off(HAND); }else{ OnFwd(ARM) ; //SetPower(ARM,OUT_FULL); Wait(50); Off(ARM); //SetPower(HAND,OUT_FULL); OnRev(HAND); Wait(150); SetPower(ARM,OUT_FULL); OnRev(ARM); Wait(90); Off(ARM); //Off(HAND); } // /* Wait(40); OnRev(HAND); Wait(80); OnRev(ARM); //SetPower(ARM,OUT_LOW); Wait(130); SetPower(HAND,OUT_HALF); OnRev(ARM); SetPower(ARM,OUT_FULL); Wait(50); Off(ARM);*/ //Off(B); // if(LIGHT>48){ //white dest=7; }else if(LIGHT>39){ //green dest=6; }else{ //brack dest=8; } PlaySound(SOUND_LOW_BEEP); command=COMMAND_TURN_LINE; send(); command=COMMAND_GO; send(); command=dest; send(); command=COMMAND_GET_READY; send(); Wait(450); //hanasu(); OnFwd(ARM) ; SetPower(ARM,OUT_HALF); Wait(30); OnRev(ARM); SetPower(ARM,OUT_LOW); OnFwd(HAND); SetPower(HAND,OUT_FULL); Wait(50); Off(HAND); OnRev(ARM); SetPower(ARM,OUT_FULL); Wait(40); Off(ARM); // command=COMMAND_TURN_LINE; send(); PlaySound(SOUND_CLICK); if(i==3-1){ src++; PlaySound(SOUND_FAST_UP); }else if(i==6-1){ src++; PlaySound(SOUND_FAST_UP); } command=COMMAND_GO; send(); command=src; send(); command=COMMAND_GET_READY; send(); } } * コード : command-proc.h [#nd416cf1] #define COMMAND_GO 110 #define COMMAND_GET_READY 111 #define COMMAND_TURN_LINE 112 * コード : command.h [#s545b962] #define COMMAND_START 201 #define COMMAND_END 202 #define COMMAND_RECIVED 203 #define COMMAND_READY 204 #define COMMAND_CONTENT 205 #define COMMAND_LISNING 206 #define COMMAND_NULL 255 * コード : command.nqc [#q01a7bfc] #include "command.h" int command=0; #ifdef LINETRACE sub recive(){ int count,tmp; until(Message()==COMMAND_START || Message()==COMMAND_CONTENT); until(Message()==COMMAND_CONTENT){ Wait(10); SendMessage(COMMAND_LISNING); } while(1){ Wait(2); //PlaySound(SOUND_CLICK); tmp=Message(); if(tmp==COMMAND_CONTENT){ tmp=COMMAND_NULL; until(tmp!=COMMAND_CONTENT){ tmp=Message(); } command=tmp; Wait(3); SendMessage(command); Wait(13); }else if(tmp==COMMAND_END){ break; } } Wait(20); until(Message()==COMMAND_END){ SendMessage(COMMAND_RECIVED); Wait(7); } Wait(10); } #else sub send(){ int i; until(Message()==COMMAND_READY || Message()==COMMAND_LISNING){ SendMessage(COMMAND_START); Wait(15); } until(Message()==command){ Wait(23); SendMessage(COMMAND_CONTENT); Wait(5); SendMessage(command); } Wait(10); for(i=0;i<15;i++){ SendMessage(COMMAND_END); Wait(5); } } #endif * コメント欄 [#q71750c4] どうぞ。 -NQCに対する不満はごもっともです。実際にはNQCの制限というよりLEGOのファームウェアの制限がほとんどなのですが、他の開発環境を使いたくなるくらい少し高いレベルの学生が増えてくると、lejos などを授業で使うということも考えないといけませんね。しかしそのようなことを自発的にやってみようと思う学生が存在するということは教員にとって大変うれしいことです。 -- [[松本(教員)]] &new{2006-02-08 (水) 14:58:25}; -ファイル共有に関しては、wikiを積極的に利用してもらえればよかったと思います。CVSとまでは行かないですが、ファイルを共有したり差分を見たりする程度なら十分使えます。それでも不満が出てきた場合にはもちろんCVSやsubversionを導入することは難しくありませんが、ただチーム全員に使い方をマスターしてもらうのは、またそれなりに時間かかります。 -- [[松本(教員)]] &new{2006-02-08 (水) 15:03:39}; -サブルーチンの数や機能の制限にぶち当たった場合には、インライン関数を使うことで多少状況は改善すると思われます。 -- [[松本(教員)]] &new{2006-02-08 (水) 15:05:32}; -wikiを使う案は思いつきませんでした。そうすればすこしはよかったんですね。インライン関数については知らないので、勉強します。コメントありがとうございました。 -- [[プログラムを書いた人]] &new{2006-02-08 (水) 16:18:44}; -インライン関数とは、NQCではfunction ABC(){}でしょうか。 -- [[プログラムを書いた人]] &new{2006-02-09 (木) 01:55:24};&new{2006-02-09 (木) 02:00:00}; #comment