目次
前
次
走行パターン作成
MCR_VCマシンであるmugen2017は、6種の走行テートで移動します。
カメラセンサーユニット(Game Boy Camera unit)から取得できるのは
次の13種のセンサー情報。
- ALL_BLACK 0
- ALL_WHITE 1
- LEFT_WHITE 2
- RIGHT_WHITE 3
- CENTER 4
- TINY_RIGHT 5
- RIGHT 6
- BIG_RIGHT 7
- TINY_LEFT 8
- LEFT 9
- BIG_LEFT 10
- BOTH_WHITE 11
- ILLEAGAL 12
センサー情報と走行ステートを組み合わせると、移動に
必要となる左右モータのDUTY比を決定できます。
配列を利用して、走行ステートとセンサー情報に応じた
DUTY比を格納して利用します。
センサー情報は、0から12の数値になっているので
配列の添字として利用すれば、DUTY比を取得できる
ようにしてみました。
UBYTE nldx[13];
UBYTE nrdx[13];
UBYTE cldx[13];
UBYTE crdx[13];
UBYTE rldx[13];
UBYTE rrdx[13];
UBYTE lldx[13];
UBYTE lrdx[13];
UBYTE chldx[13];
UBYTE chrdx[13];
UBYTE blldx[13];
UBYTE blrdx[13];
void init_road_array()
{
/* NORMAL */
*(nldx+ALL_BLACK) = 10 ; *(nrdx+ALL_BLACK) = 10 ;
*(nldx+ALL_WHITE) = 30 ; *(nrdx+ALL_WHITE) = 30 ;
*(nldx+LEFT_WHITE) = 10 ; *(nrdx+LEFT_WHITE) = 10 ;
*(nldx+RIGHT_WHITE) = 10 ; *(nrdx+RIGHT_WHITE) = 10 ;
*(nldx+CENTER) = 50 ; *(nrdx+CENTER) = 50 ;
*(nldx+TINY_RIGHT) = 50 ; *(nrdx+TINY_RIGHT) = 55 ;
*(nldx+RIGHT) = 50 ; *(nrdx+RIGHT) = 60 ;
*(nldx+BIG_RIGHT) = 50 ; *(nrdx+BIG_RIGHT) = 65 ;
*(nldx+TINY_LEFT) = 55 ; *(nrdx+TINY_RIGHT) = 50 ;
*(nldx+LEFT) = 60 ; *(nrdx+RIGHT) = 50 ;
*(nldx+BIG_LEFT) = 65 ; *(nrdx+BIG_RIGHT) = 50 ;
*(nldx+BOTH_WHITE) = 10 ; *(nrdx+BIG_RIGHT) = 10 ;
*(nldx+ILLEAGAL) = 10 ; *(nrdx+ILLEAGAL) = 10 ;
/* CRANK */
*(cldx+ALL_BLACK) = 10 ; *(crdx+ALL_BLACK) = 10 ;
*(cldx+ALL_WHITE) = 10 ; *(crdx+ALL_WHITE) = 10 ;
*(cldx+LEFT_WHITE) = 10 ; *(crdx+LEFT_WHITE) = 10 ;
*(cldx+RIGHT_WHITE) = 30 ; *(crdx+RIGHT_WHITE) = 30 ;
*(cldx+CENTER) = 30 ; *(crdx+CENTER) = 35 ;
*(cldx+TINY_RIGHT) = 30 ; *(crdx+TINY_RIGHT) = 40 ;
*(cldx+RIGHT) = 30 ; *(crdx+RIGHT) = 45 ;
*(cldx+BIG_RIGHT) = 35 ; *(crdx+BIG_RIGHT) = 30 ;
*(cldx+TINY_LEFT) = 40 ; *(crdx+TINY_LEFT) = 30 ;
*(cldx+LEFT) = 45 ; *(crdx+LEFT) = 30 ;
*(cldx+BIG_LEFT) = 10 ; *(crdx+BIG_LEFT) = 10 ;
*(cldx+BOTH_WHITE) = 10 ; *(crdx+BOTH_WHITE) = 10 ;
*(cldx+ILLEAGAL) = 10 ; *(crdx+ILLEAGAL) = 10 ;
/* ROTATE */
*(rldx+ALL_BLACK) = 10 ; *(rrdx+ALL_BLACK) = 10 ;
*(rldx+ALL_WHITE) = 30 ; *(rrdx+ALL_WHITE) = 30 ;
*(rldx+LEFT_WHITE) = 10 ; *(rrdx+LEFT_WHITE) = 50 ;
*(rldx+RIGHT_WHITE) = 50 ; *(rrdx+RIGHT_WHITE) = 10 ;
*(rldx+CENTER) = 30 ; *(rrdx+CENTER) = 30 ;
*(rldx+TINY_RIGHT) = 30 ; *(rrdx+TINY_RIGHT) = 35 ;
*(rldx+RIGHT) = 30 ; *(rrdx+RIGHT) = 40 ;
*(rldx+BIG_RIGHT) = 30 ; *(rrdx+BIG_RIGHT) = 45 ;
*(rldx+TINY_LEFT) = 35 ; *(rrdx+TINY_LEFT) = 30 ;
*(rldx+LEFT) = 40 ; *(rrdx+LEFT) = 30 ;
*(rldx+BIG_LEFT) = 45 ; *(rrdx+BIG_LEFT) = 30 ;
*(rldx+BOTH_WHITE) = 25 ; *(rrdx+BOTH_WHITE) = 25 ;
*(rldx+ILLEAGAL) = 10 ; *(rrdx+ILLEAGAL) = 10 ;
/* LANE */
*(lldx+ALL_BLACK) = 10 ; *(lrdx+ALL_BLACK) = 10 ;
*(lldx+ALL_WHITE) = 30 ; *(lrdx+ALL_WHITE) = 30 ;
*(lldx+LEFT_WHITE) = 10 ; *(lrdx+LEFT_WHITE) = 50 ;
*(lldx+RIGHT_WHITE) = 50 ; *(lrdx+RIGHT_WHITE) = 10 ;
*(lldx+CENTER) = 30 ; *(lrdx+CENTER) = 30 ;
*(lldx+TINY_RIGHT) = 30 ; *(lrdx+TINY_RIGHT) = 35 ;
*(lldx+RIGHT) = 30 ; *(lrdx+RIGHT) = 40 ;
*(lldx+BIG_RIGHT) = 30 ; *(lrdx+BIG_RIGHT) = 45 ;
*(lldx+TINY_LEFT) = 35 ; *(lrdx+TINY_LEFT) = 30 ;
*(lldx+LEFT) = 40 ; *(lrdx+LEFT) = 30 ;
*(lldx+BIG_LEFT) = 45 ; *(lrdx+BIG_LEFT) = 30 ;
*(lldx+BOTH_WHITE) = 10 ; *(lrdx+BOTH_WHITE) = 10 ;
*(lldx+ILLEAGAL) = 10 ; *(lrdx+ILLEAGAL) = 10 ;
/* CHANGE */
*(chldx+ALL_BLACK) = 10 ; *(chrdx+ALL_BLACK) = 10 ;
*(chldx+ALL_WHITE) = 50 ; *(chrdx+ALL_WHITE) = 50 ;
*(chldx+LEFT_WHITE) = 10 ; *(chrdx+LEFT_WHITE) = 50 ;
*(chldx+RIGHT_WHITE) = 50 ; *(chrdx+RIGHT_WHITE) = 10 ;
*(chldx+CENTER) = 50 ; *(chrdx+CENTER) = 50 ;
*(chldx+TINY_RIGHT) = 55 ; *(chrdx+TINY_RIGHT) = 50 ;
*(chldx+RIGHT) = 60 ; *(chrdx+RIGHT) = 50 ;
*(chldx+BIG_RIGHT) = 65 ; *(chrdx+BIG_RIGHT) = 50 ;
*(chldx+TINY_LEFT) = 50 ; *(chrdx+TINY_LEFT) = 55 ;
*(chldx+LEFT) = 50 ; *(chrdx+LEFT) = 60 ;
*(chldx+BIG_LEFT) = 50 ; *(chrdx+BIG_LEFT) = 65 ;
*(chldx+BOTH_WHITE) = 10 ; *(chrdx+BOTH_WHITE) = 10 ;
*(chldx+ILLEAGAL) = 10 ; *(chrdx+ILLEAGAL) = 10 ;
/* BLIND */
*(blldx+ALL_BLACK) = 10 ; *(blrdx+ALL_BLACK) = 10 ;
*(blldx+ALL_WHITE) = 50 ; *(blrdx+ALL_WHITE) = 50 ;
*(blldx+LEFT_WHITE) = 50 ; *(blrdx+LEFT_WHITE) = 10 ;
*(blldx+RIGHT_WHITE) = 10 ; *(blrdx+RIGHT_WHITE) = 50 ;
*(blldx+CENTER) = 50 ; *(blrdx+CENTER) = 50 ;
*(blldx+TINY_RIGHT) = 55 ; *(blrdx+TINY_RIGHT) = 50 ;
*(blldx+RIGHT) = 60 ; *(blrdx+RIGHT) = 50 ;
*(blldx+BIG_RIGHT) = 65 ; *(blrdx+BIG_RIGHT) = 50 ;
*(blldx+TINY_LEFT) = 50 ; *(blrdx+TINY_LEFT) = 55 ;
*(blldx+LEFT) = 50 ; *(blrdx+LEFT) = 60 ;
*(blldx+BIG_LEFT) = 50 ; *(blrdx+BIG_LEFT) = 65 ;
*(blldx+BOTH_WHITE) = 10 ; *(blrdx+BOTH_WHITE) = 10 ;
*(blldx+ILLEAGAL) = 10 ; *(blrdx+ILLEAGAL) = 10 ;
}
上のように定義して、利用するメモリの容量は、13x2x6=156バイト
にしかなりません。実際に走行させ、左右のモータの固体差を吸収
へと持ち込みます。
少し冗長なので、次のように16ビットの配列で、左右のモータの
DUTY比が一目でわかるようにしました。
UWORD ndx[13];
UWORD cdx[13];
UWORD rdx[13];
UWORD ldx[13];
UWORD chdx[13];
UWORD bldx[13];
void init_road_array()
{
/* NORMAL */
*(ndx+ALL_BLACK) = (10 << 8) | 10 ;
*(ndx+ALL_WHITE) = (30 << 8) | 30 ;
*(ndx+LEFT_WHITE) = (10 << 8) | 10 ;
*(ndx+RIGHT_WHITE) = (10 << 8) | 10 ;
*(ndx+CENTER) = (50 << 8) | 50 ;
*(ndx+TINY_RIGHT) = (50 << 8) | 55 ;
*(ndx+RIGHT) = (50 << 8) | 60 ;
*(ndx+BIG_RIGHT) = (50 << 8) | 65 ;
*(ndx+TINY_LEFT) = (55 << 8) | 50 ;
*(ndx+LEFT) = (60 << 8) | 50 ;
*(ndx+BIG_LEFT) = (65 << 8) | 50 ;
*(ndx+BOTH_WHITE) = (10 << 8) | 10 ;
*(ndx+ILLEAGAL) = (10 << 8) | 10 ;
/* CRANK */
*(cdx+ALL_BLACK) = (10 << 8) | 10 ;
*(cdx+ALL_WHITE) = (10 << 8) | 10 ;
*(cdx+LEFT_WHITE) = (10 << 8) | 10 ;
*(cdx+RIGHT_WHITE) = (10 << 8) | 10 ;
*(cdx+CENTER) = (30 << 8) | 30 ;
*(cdx+TINY_RIGHT) = (30 << 8) | 35 ;
*(cdx+RIGHT) = (30 << 8) | 40 ;
*(cdx+BIG_RIGHT) = (30 << 8) | 45 ;
*(cdx+TINY_LEFT) = (35 << 8) | 30 ;
*(cdx+LEFT) = (40 << 8) | 30 ;
*(cdx+BIG_LEFT) = (45 << 8) | 30 ;
*(cdx+BOTH_WHITE) = (10 << 8) | 10 ;
*(cdx+ILLEAGAL) = (10 << 8) | 10 ;
/* ROTATE */
*(rdx+ALL_BLACK) = (10 << 8) | 10 ;
*(rdx+ALL_WHITE) = (30 << 8) | 30 ;
*(rdx+LEFT_WHITE) = (10 << 8) | 50 ;
*(rdx+RIGHT_WHITE) = (50 << 8) | 10 ;
*(rdx+CENTER) = (30 << 8) | 30 ;
*(rdx+TINY_RIGHT) = (30 << 8) | 35 ;
*(rdx+RIGHT) = (30 << 8) | 40 ;
*(rdx+BIG_RIGHT) = (30 << 8) | 45 ;
*(rdx+TINY_LEFT) = (35 << 8) | 30 ;
*(rdx+LEFT) = (40 << 8) | 30 ;
*(rdx+BIG_LEFT) = (45 << 8) | 30 ;
*(rdx+BOTH_WHITE) = (25 << 8) | 25 ;
*(rdx+ILLEAGAL) = (10 << 8) | 10 ;
/* LANE */
*(ldx+ALL_BLACK) = (10 << 8) | 10 ;
*(ldx+ALL_WHITE) = (30 << 8) | 30 ;
*(ldx+LEFT_WHITE) = (10 << 8) | 50 ;
*(ldx+RIGHT_WHITE) = (50 << 8) | 10 ;
*(ldx+CENTER) = (30 << 8) | 30 ;
*(ldx+TINY_RIGHT) = (30 << 8) | 35 ;
*(ldx+RIGHT) = (30 << 8) | 40 ;
*(ldx+BIG_RIGHT) = (30 << 8) | 45 ;
*(ldx+TINY_LEFT) = (35 << 8) | 30 ;
*(ldx+LEFT) = (40 << 8) | 30 ;
*(ldx+BIG_LEFT) = (45 << 8) | 30 ;
*(ldx+BOTH_WHITE) = (10 << 8) | 10 ;
*(ldx+ILLEAGAL) = (10 << 8) | 10 ;
/* CHANGE */
*(chdx+ALL_BLACK) = (10 << 8) | 10 ;
*(chdx+ALL_WHITE) = (50 << 8) | 50 ;
*(chdx+LEFT_WHITE) = (10 << 8) | 50 ;
*(chdx+RIGHT_WHITE) = (50 << 8) | 10 ;
*(chdx+CENTER) = (50 << 8) | 50 ;
*(chdx+TINY_RIGHT) = (55 << 8) | 50 ;
*(chdx+RIGHT) = (60 << 8) | 50 ;
*(chdx+BIG_RIGHT) = (65 << 8) | 50 ;
*(chdx+TINY_LEFT) = (50 << 8) | 55 ;
*(chdx+LEFT) = (50 << 8) | 60 ;
*(chdx+BIG_LEFT) = (50 << 8) | 65 ;
*(chdx+BOTH_WHITE) = (10 << 8) | 10 ;
*(chdx+ILLEAGAL) = (10 << 8) | 10 ;
/* BLIND */
*(bldx+ALL_BLACK) = (10 << 8) | 10 ;
*(bldx+ALL_WHITE) = (50 << 8) | 50 ;
*(bldx+LEFT_WHITE) = (10 << 8) | 50 ;
*(bldx+RIGHT_WHITE) = (50 << 8) | 10 ;
*(bldx+CENTER) = (50 << 8) | 50 ;
*(bldx+TINY_RIGHT) = (55 << 8) | 50 ;
*(bldx+RIGHT) = (60 << 8) | 50 ;
*(bldx+BIG_RIGHT) = (65 << 8) | 50 ;
*(bldx+TINY_LEFT) = (50 << 8) | 55 ;
*(bldx+LEFT) = (50 << 8) | 60 ;
*(bldx+BIG_LEFT) = (50 << 8) | 65 ;
*(bldx+BOTH_WHITE) = (10 << 8) | 10 ;
*(bldx+ILLEAGAL) = (10 << 8) | 10 ;
}
行数は同じですが配列の個数が減るのと、左右で
DUTY比がわかるので、こちらの方がよいと判断。
スプレッドシートを利用すれば、変数名、文字列、左右のDUTY比を
書いてCSV形式で出力すれば、すぐに生成できるでしょう。
スプレッドシートを使うまでもないとして、次のAWKのスクリプトで
コードを生成しました。
{
printf("*(%s+%s) = (%d << 8) | %d ;\n",$1,$2,$3,$4)
}
AWKのスクリプトに与える内容は、次のリストでテキストファイルに。
bldx ALL_BLACK 10 10
bldx ALL_WHITE 50 50
bldx LEFT_WHITE 10 50
bldx RIGHT_WHITE 50 10
bldx CENTER 50 50
bldx TINY_RIGHT 55 50
bldx RIGHT 60 50
bldx BIG_RIGHT 65 50
bldx TINY_LEFT 50 55
bldx LEFT 50 60
bldx BIG_LEFT 50 65
bldx BOTH_WHITE 10 10
bldx ILLEAGAL 10 10
スプレッドシートを立ち上げる時間が勿体ないので
6種のテキストファイルを用意して、一気にコード
を生成できました。
走行ステートに合わせたDUTY比を設定しているので
個別の走行ステートの移動をテストできます。
大きなコースを持たないでも、移動テストができる
という長所あり。
センサー情報との連携を考えると、基板上にある2つのLEDを使い
ALL_WHITE、LEFT_WHITE、RIGHIT_WHITEを表示します。
DUTY比とセンサー値の方は、8個のLEDを1セットとして2セット
あるので表示して、走行しているときの状態がわかるようにします。
DUTY比は、左右で0から99までの数値として定義されるので
8で割ったときの商を求め、左右のLED4個で表示。8で割る
のは右に3ビットシフトなので、計算時間を短くできます。
DUTY比の他に、走行モードを音で出力します。
音を出すとうるさいので、AMワイヤレスマイクを使い
AMラジオに送信。
4番ピンはリセットなので、音声周波数帯の信号を入れて
変調します。無変調でも、雑音くらいは聞こえますが。
送信範囲は半径10m四方であればよいので、1mくらいの
ビニル線を接続してアンテナとしておきます。
回路は、単純で発振ICの定番555を利用。
530kHzから880kHzまで可変できるので、札幌、大阪で
ラジオ局の周波数が異なっても、送信周波数を変えて対応可能
にしました。
電波法にかからないようにして、次のレシーバで音を捕捉。
ゲルマニウムダイオードを利用し、トランジスタ一石で増幅。
クリスタルイアフォンで聞けます。
スピーカで聞きたい場合には、次のアンプを追加。
マシン上のコンピュータは、走行モードにあわせたデータを
取り出す処理が必要になりますが、次のように単純。
#define MASKFF 0xff
UBYTE xmul ;
UBYTE flag ;
UBYTE xsensor ;
UWORD tmp ;
/* default */
flag = OFF ;
/* show state */
mstate = 1 ;
show_state( mstate );
/* get sensor */
xsensor = get_sensor();
/* update motor duty ratio */
tmp = *(ndx+xsensor);
left = (tmp >> 8) & MASKFF ;
right = tmp & MASKFF ;
/* default */
*(dir+0) = DIR_CENTER ;
*(dir+1) = DIR_CENTER ;
/* get sensor */
if ( xsensor == CENTER ) {
/* climb hill or down hill */
if ( sloap ) {
xmul = *(sloapt+sloap) ;
left = (UBYTE)( xmul * left * 1.0 / 100 );
right = (UBYTE)( xmul * right * 1.0 / 100 );
}
}
/* update */
send_dc(left,right);
/* judge */
if ( xsensor == ALL_WHITE ) {
rsm_tsk(CRANK);
flag = ON ;
}
if ( xsensor == LEFT_WHITE ) {
*(dir+0) = DIR_LEFT ;
*(dir+1) = DIR_RIGHT ;
rsm_tsk(LANE);
flag = ON ;
}
if ( xsensor == RIGHT_WHITE ) {
*(dir+0) = DIR_RIGHT ;
*(dir+0) = DIR_LEFT ;
rsm_tsk(LANE);
flag = ON ;
}
/* cyclic */
if ( flag == ON ) {
flag = OFF ;
slp_tsk();
} else {
wai_tsk( 10 );
}
走行モードを表示するには、関数show_stateを利用しますが
該当するLEDを点灯するだけで対応可能。
void show_state(UBYTE x)
{
UBYTE result;
/* generate code */
result = (1 << x);
/* impress */
P4DR = result ^ MASKFF ;
}
DUTY比の表示は、次の処理にパラメータを渡すだけ。
void show_duty(UBYTE ldx,UBYTE rdx)
{
UBYTE result ;
/* clear */
P3DR = 0x00 ;
/* clear */
result = 0 ;
/* upper */
result |= ((ldx >> 3) << 4) ;
/* lower */
result |= (rdx >> 3) ;
/* send */
P3DR = result ^ MASKFF ;
}
左右のDUTY比は、変数left、rightに格納されているので
それらを関数show_dutyに与えるだけで充分。
路面状態は、配列にパターンを格納してLEDに転送する
ことで対応します。
UBYTE road_pat[13] ;
void init_road_pat(void)
{
*(road_pat+ALL_BLACK) = 0x00 ; *(road_pat+ALL_WHITE) = 0xff ;
*(road_pat+LEFT_WHITE) = 0xf0 ; *(road_pat+RIGHT_WHITE) = 0x0f ;
*(road_pat+CENTER) = 0x18 ; *(road_pat+TINY_RIGHT) = 0x0c ;
*(road_pat+RIGHT) = 0x06 ; *(road_pat+BIG_RIGHT) = 0x03 ;
*(road_pat+TINY_LEFT) = 0x30 ; *(road_pat+LEFT) = 0x60 ;
*(road_pat+BIG_LEFT) = 0xc0 ; *(road_pat+BOTH_WHITE) = 0xc3 ;
*(road_pat+ILLEAGAL) = 0x5a ;
}
void show_road(UBYTE x)
{
UBYTE result ;
/* clear */
PADR = 0x00 ;
/* get pattern */
result = *(road_pat+x) ;
/* send */
PADR = result ^ MASKFF ;
}
音で走行モードを提示するには、周波数の変更が必要なので
発振ICの555の発振周波数を変更して対応。
RC発振回路の抵抗分Rをアナログスイッチで切り替えます。
コンピュータからは、どの周波数を利用するのかを
3ビットの選択信号で指定するだけ。
目次
前
次