2018年5月
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Amazonウィジェット

  • お気に入り
  • 欲しいもの
  • 面白かった本
  • Raspberry Pi
  • ドローン
  • 書籍ランキング

AdSense

  • 広告
無料ブログはココログ

« 遺伝的アルゴリズムで育てる「お掃除ロボット」 その5 ~マップの読み込みと表示~ | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その7 ~ロボット動作の改良~ »

2018年3月21日 (水)

遺伝的アルゴリズムで育てる「お掃除ロボット」 その6 ~動かしてみた!!~

遺伝的アルゴリズムを使ったお掃除ロボットのシミュレーションシリーズ。 今まで作った関数を連結して、動かす段階に来た。

void main(void) {
	srand(time(NULL));
	indiv_t ind;
	
	readMap();
	makeRobot(&ind,1,0);
	draw();

	while (1) {
        puts("----------\n");
		execRobot(&ind);
		draw();
		
		Sleep(100);

		if (ind.life < 1) {
			break;
		}
	}
}

動かしたときの動画がこちら




一応正しい動きはしているのだが、もうちょっとランダムに動いてくれないと面白くない感じだ。

壁にぶつかると、移動できずにlifeだけが減っていく処理になっているのだが、 ロボットなのだからバンパーセンサぐらいは付けたい。

そこで、壁にぶつかったと判定されたら、回避行動をとる仕組みを入れることにした。



これはかなり大規模な変更になる。


というのも、移動方向はマップの上から見た状態で指定している。
もし、壁にぶつかったら左に避けろとプログラムしたとすると、 上に移動していて左に避けるなら簡単だ。

ところが右に移動していて左に避けるということは、 今の指定の仕方だと、ロボットにとっては後退になる。


つまり、ロボットローカルの移動方向で指定できるような仕組みが必要ということ。

ロボットの構造体の中で、東西南北どの方角を向いているかを管理する必要が生じる。

ロボットの移動方向は0:前、1:右、2:下、3:左の4種類。 ロボットの向いている方角を、0:北、1:東、2:南、3:西とした。 移動方向と方角を別々に管理するのでかなりややこしい。

ロボットを旋回させたあとの向きの計算方法は思いつくまでそれほどかからなかった。 ロボットの向いている方角に、移動方向を足したらよさそうだ。

場合を書き下した図

Idouhoukoku_saisyonokanngaekata_3

これをベースにして試作コードを書き下してみた。

//引数:robotの向きに対してどちらを向くかを指定。
int calcDir(int curr_dir, int temp_dir){
	int newdir;
	newdir = curr_dir + temp_dir;
    if(newdir > NDIR-1){
       return newdir - NDIR;
    }
    return newdir;
}






次にgenの構成方法を変更しに取り掛かった。

今までは遺伝子の配列genにgen_tを一様に入れただけだったが、
普段の掃除モードと、壁当たり後の回避モードの2種類をgen配列に設けることにした。

配列のある区間をインデックスで分けると、ちょっと実装を変更するだけで影響を受けそうなので、 構造体の外に配列を別々に用意した。

//ロボットのモードごとの状態を管理する構造体
typedef struct {
	int movCnt;//movement counter
	int curr;//current gen index
	int bufLen;
	gen_t *buff;
	int hittedFlag;
}work_t;

//ロボット1体分のデータを保持する構造体
typedef struct{
	//value
	int life;
	int item;
	double scr;
	//position
	point_t pos;
	point_t delta;
	//parameter
	work_t work;
	gen_t gen[GEN_LEN];
}indiv_t;

static gen_t search[SEARCH_LEN];
static gen_t hitted[HITTED_LEN];

static work_t searchMode = { 0, 0, SEARCH_LEN, search, 0 }; //お掃除モード
static work_t hittedMode = { 0, 0, HITTED_LEN, hitted, 1 }; //回避モード

動かし方を変えたので、それに合わせてexecRobotを修正。

void execRobot(indiv_t *ind){
	//alias
	work_t *w = &ind->work;
    //life count
	ind->life--;
	
	//dx,dy table (switch)
	const int dir_defx[] = {  0,1,0,-1 };
	const int dir_defy[] = { -1,0,1, 0 };

    //load dir
	int dest_dir = w->buff[w->curr].dir;

    int dest_idx = calcDir(ind->dir, dest_dir);
	ind->dir = dest_dir;
	
	int dx = dir_defx[dest_idx];
	int dy = dir_defy[dest_idx];

	char c = getMapVal(ind->x + dx, ind->y + dy);
	if (c != CH_WALL) {
        //壁でなければ
        moveRobot(ind,dx,dy);

		if (c == CH_ITEM) {
			getItem(ind);
		}

        w->movCnt++;
        if(w->movCnt > w->buff[w->curr].num-1){
            w->curr++;
            if(w->curr > GEN_LEN-1){
                w->curr = 0;
            }
            w->movCnt = 0;
        }
	}
	else {
		//壁に当たったら
		*w = hittedMode;
        puts("hitted");
	}
}

ロボットを動かしているときの変数は、すべてwork_tという構造体で管理している。

このように切り分けることによって、search(お掃除モード)とhitted(回避モード)の切り替えを、 構造体の代入の形で簡単に切り替えられるようにした。

壁に当たったと判定されると、

*w = hittedMode;

でhittedModeという定数で初期化済みの構造体が代入される。

ind->workをいちいち書くとソースの見通しが悪くなるので、 最初に短い名前で置き換えてしまうことにした。

work_t *w = &ind->work;

構造体から何度も同じメンバを取り出したり、 ネストされた構造体の深いところにあるメンバを使う時は、 別な名前で置き換えると速度的にも有利になる。



次回も続々改良編。
続く。

« 遺伝的アルゴリズムで育てる「お掃除ロボット」 その5 ~マップの読み込みと表示~ | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その7 ~ロボット動作の改良~ »

C言語」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/518723/66385778

この記事へのトラックバック一覧です: 遺伝的アルゴリズムで育てる「お掃除ロボット」 その6 ~動かしてみた!!~:

« 遺伝的アルゴリズムで育てる「お掃除ロボット」 その5 ~マップの読み込みと表示~ | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その7 ~ロボット動作の改良~ »