こんにちは。koraです。
今回は、前回の記事で解説した迷路探索アルゴリズムを、HM-StarterKitに実装して迷路を走らせてみます。
迷路探索関連の関数の追加
search.hとsearch.c
サンプルプログラムのStep7から、search.hとsearch.cをコピーして、CS+のプロジェクトに加えます。これで、以下の関数が追加されます。
- search_lefthand(x, y)
左手方で探索する関数。今回は使用しません。 - init_map(x, y)
歩数マップの初期化関数。歩数マップ更新のたびに呼び出されます。 - make_map(x, y, mask)
ゴール座標(x, y)の歩数マップを更新する関数。 - set_wall(x, y)
光センサの情報をもとに、現在の座標(x, y)の壁情報を設定する関数。 - is_unknown(x, y)
指定した座標(x, y)が未探索区間か否かを判定する関数。get_priority関数から呼ばれます。 - get_priority(x, y, dir)
現在の座標(x, y)から方角dirに移動する優先度を取得する関数。指定した方角が未探索の場合や直進の場合、高い優先度になります。get_nextdir関数から呼ばれます。 - get_nextdir(x, y, mask, *dir)
次に行くべき方向を取得する関数。 - search_adachi(gx, gy)
指定したゴール座標(gx, gy)に向かって、足立法で探索する関数。
構造体、グローバル変数、定数の追加
mytypedef.h
mytypedef.hに次の列挙型(enum)と構造体の型(struct)の宣言を追加します。
typedef enum
{
front=0, //前
right=1, //右
rear=2, //後
left=3, //左
unknown, //方向不明
}t_local_dir; //自分から見た方向を示す列挙型
typedef enum
{
north=0,
east=1,
south=2,
west=3,
}t_direction;
typedef struct
{
short x;
short y;
t_direction dir;
}t_position;
typedef struct
{
unsigned char north:2; //北の壁情報
unsigned char east:2; //東の壁情報
unsigned char south:2; //南の壁情報
unsigned char west:2; //西の壁情報
}t_wall; //壁情報を格納する構造体(ビットフィールド)
glob_var.h
構造体をグローバル変数として使えるよう、glob_var.hに以下を追加します。
GLOBAL t_control con_fwall; //制御構造体 GLOBAL t_position mypos; //自己座標 GLOBAL t_wall wall[MAZESIZE_X][MAZESIZE_Y]; //壁の情報を格納する構造体配列 GLOBAL unsigned char map[MAZESIZE_X][MAZESIZE_Y]; //歩数マップ
static_parameters.h
探索に必要な定数を追加します。
#define MAZESIZE_X (32) //迷路の大きさ(MAZESIZE_X * MAZESIZE_Y)迷路 #define MAZESIZE_Y (32) //迷路の大きさ(MAZESIZE_X * MAZESIZE_Y)迷路 #define UNKNOWN 2 //壁があるかないか判らない状態の場合の値 #define NOWALL 0 //壁がないばあいの値 #define WALL 1 //壁がある場合の値 #define VWALL 3 //仮想壁の値(未使用)
parameters.h
探索を開始させるときは、マウスの前に手をかざして、光センサ変化を読み取らせます。そのための光センサの閾値をparameters.hに追加します。
#define SEN_DECISION 2000 //メニュー決定用の光センサ閾値
init.c
init_maze関数をinit.cに追加します。
/*****************************************************************************************
迷路情報の初期化
*****************************************************************************************/
void init_maze(void) //迷路情報の初期化
{
int i,j;
for(i = 0; i < MAZESIZE_X; i++)
{
for(j = 0; j < MAZESIZE_Y; j++)
{
wall[i][j].north = wall[i][j].east = wall[i][j].south = wall[i][j].west = UNKNOWN; //迷路の全体がわからない事を設定する
}
}
for(i = 0; i < MAZESIZE_X; i++)
{
wall[i][0].south = WALL; //四方の壁を追加する(南)
wall[i][MAZESIZE_Y-1].north = WALL; //四方の壁を追加する(北)
}
for(j = 0; j < MAZESIZE_Y; j++)
{
wall[0][j].west = WALL; //四方の壁を追加する(西)
wall[MAZESIZE_X-1][j].east = WALL; //四方の壁を追加する(東)
}
wall[0][0].east = wall[1][0].west = WALL; //スタート地点の右の壁を追加する
}
init_all関数内にinit_maze関数を呼び出す行を追加します。
init_maze();
探索のテスト
my_hm_starterkit.c
サンプルプログラムを参考にmain関数を次のように書き換えます。これで4×4マスの迷路を探索できるはずです。
void main(void)
{
init_all();
// ブザー
BEEP();
while(1)
{
//センサーの前に手をかざしてスタート
if(sen_fr.value + sen_fl.value + sen_r.value + sen_l.value > SEN_DECISION * 4)
{
// ブザー
BEEP();
// 積分用の変数を初期化
I_tar_ang_vel = 0;
I_ang_vel = 0;
I_tar_speed = 0;
I_speed = 0;
// 車体方向を初期化
degree = 0;
// ジャイロの参照値を取得
gyro_get_ref();
// ブザー
BEEP();
// 車体の座標と方角を初期化
mypos.x = mypos.y = 0;
mypos.dir = north;
// 足立法でゴール座標(3, 3)まで探索する
search_adachi(3, 3);
// ゴールしたら180度回転して方角を更新する
turn(180,TURN_ACCEL,TURN_SPEED,RIGHT);
mypos.dir = (mypos.dir+6) % 4;
// ゴールしたことをアピール
wait_ms(100);
BEEP();
wait_ms(100);
BEEP();
// モータの電源OFF
MOT_POWER_OFF;
// 終了したことをアピール
BEEP();
}
}
}
このプログラムで実際に探索を行った動画がこちらです。
ふらつきながらもなんとかゴールすることができました。
パラメータの調整
マウスが迷路を走るとき、光センサやタイヤなどの個体差に加え、環境光、路面状況、壁の反射率の違いなどで走行が安定しません。安定させるためにはparameter.hに記載されたパラメータを微調整する必要があります。
- タイヤの直径
- 各光センサの閾値
- LED発光からAD変換開始までの時間
- PIDゲイン
などを調整した結果がこの動画です。
比較的まっすぐに走るようになりました。
次回
4×4の小さな迷路ですが、ゴールまでの探索に成功しました。
しかし、実際のマイクロマウス競技では16×16や32×32になりますので、もっと高速化が必要です。
次回は高速化のため「スラローム探索(通称スラ探)」を追加しようと思います。
