ライントレーサーの環境をOpenAI I/F的にした

[ライントレーサーをDeep Q Learningで教育する \- Chainer \- Qiita](http://qiita.com/chachay/items/555638e3079fce9d59c9)で取り扱った環境をOpenAI gymライクに扱えるように環境とAIを分離・整備しました。分離したといっても、renderのところを当初wxPythonと描画・ロジック一体で作りこんでしまったので、おかしな状態になっていますが、動くので良しという段階です。

この単眼ライントレーサは[POMDP](https://ja.wikipedia.org/wiki/%E9%83%A8%E5%88%86%E8%A6%B3%E6%B8%AC%E3%83%9E%E3%83%AB%E3%82%B3%E3%83%95%E6%B1%BA%E5%AE%9A%E9%81%8E%E7%A8%8B)の例ともいえますが、（最初にコース上に乗せる前提で）ロジックベースで動かしてみている人は少なからずいると思います。
POMDPってなんぞというかたは、@okdshinさんが最近親切な説明を書かれていたので、参考にするといいと思います。
⇒ [外部メモリ（External Memory）を利用した強化学習 \- Qiita](http://qiita.com/okdshin/items/c486da9c3e563147b0ff)

先述の以前のチャレンジでは、過去4ステップ分のステートを状態としてDQNにくれてやっていました。このほか、特徴量エンジニアリングの要領で”線”に対してどのくらいの位置に自分がいるかを表現するパラメータを入れてやっても割とよくなります。
（ま、信念みたいなものですね）

で、OpenAI Gymについては@icoxfog417さんがハンズオンを2016/11/16に企画されているそうで、事前準備資料としてgithubに上がっているのが分かりやすいかなと思います。
[icoxfog417/techcircle\_openai\_handson: Tech\-Circle OpenAI Handson Repository](https://github.com/icoxfog417/techcircle_openai_handson)

(Windows 7だと導入が難しいみたいですけどね、本件はWindwos7、CPU ONLYでも動きます）

（他人の褌で相撲取りまくり）

# リポジトリ

[Gym\_LineFollower: Simple Open AI gym like Environment](https://github.com/Chachay/Gym_LineFollower)
![Demo.png](https://qiita-image-store.s3.amazonaws.com/0/117379/a4499ead-c93c-c673-1568-4926b10ac7c1.png)

ダウンロードしていただいて、

```
python exapmle.py
```

で、ランダムに動き回るエージェントが1000ステップだけ計算します。
ここの中を書き換えて、DQNやそのほかの方式のAIで動かせます。

* actionは(左輪速度, 右輪速度)で、それぞれ[-1.0,1.0]の値域を取れます。DQNのように離散値を使いたい場合は、actionlist=[[1.0,1.0],[-1.0,1.0],...]のようにした挙句に、actionlist[0]を渡すようなイメージですかね。
* observationは単眼の光センサで中身は(検知・不検知)、つまり(1 or 0)です。
* infoにはデバッグなどように、ライントレーサの位置や角度が入っていますので、必要でしたら使ってみてね！
* rewardは後述の通り考え中！

```python:example.py
    def run(self):
        observation = env.reset()
        for t in range(1000):
            env.render()
            print(observation)
            action = agent.act()
            observation, reward, done, info = env.step(action)
            if done:
                print("Episode finished after {} timesteps".format(t+1))
                break
            wx.Yield()

        env.monitor.close()
        
        print("Simulation completed\nWaiting for closing window..")
```

サンプルエージェントは@ugo-namaさんのtorcs gymからパクリベースです。
[gym\_torcs/sample\_agent\.py](https://github.com/ugo-nama-kun/gym_torcs/blob/master/sample_agent.py)

```python:sample_agent
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

class Agent(object):
    def __init__(self, dim_action):
        self.dim_action = dim_action

    def act(self):
        return np.tanh(np.random.randn(self.dim_action)) # random action
```

# 工事中のところ/宿題（ここをメモするための投稿ともいえる）
## 1. コースが決め打ち
AIの研究というからにはいろんな環境で走れたほうがいいですよね。ですが、オーバルで決め打ちです。

## 2. 報酬設計
[ライントレーサーをDeep Q Learningで教育する](http://qiita.com/chachay/items/555638e3079fce9d59c9)で使った報酬とは違うものを考えていますが、今は仮のものを入れています。

```python:現状の雑な報酬
        if not done:
            # 生き残っていればコースにいなくてもご褒美
            reward = 1.0
        elif self.steps_beyond_done is None:
            # Robot just went out over the boundary
            self.steps_beyond_done = 0
            reward = 1.0
        else:
            if self.steps_beyond_done == 0:
                logger.warn("You are calling 'step()' even though this environment has already returned done = True. You should always call 'reset()' once you receive 'done = True' -- any further steps are undefined behavior.")
            self.steps_beyond_done += 1
            reward = 0.0
```

以前は無理にコースに乗せるために色々ハックしていましたが、これも美しくないです。

```python:以前の妙に細かい怪しい報酬
            # Reward
            # コースにどれだけ沿っているかのご褒美
            proximity_reward = 0.0
            if self.Course.adLines([ag.pos_x, ag.pos_y], 5.0):
                proximity_reward = 1.0
            elif self.Course.adLines([ag.pos_x, ag.pos_y], 10.0):
                proximity_reward = 0.5
            # 壁にぶつかったら罰則
            if self.Box.adLines([ag.pos_x, ag.pos_y], 3.0):
                proximity_reward -= 1.0

            # 直進を選好するようにご褒美
            forward_reward   = 0.0
            if(action[0] == action[1] and proximity_reward > 0.75):  
                forward_reward = 0.1 * proximity_reward

            # センサがコースを検知し続けるようなインセンティブ           
            eye_reward = 1.0 if ag.EYE.obj == 1 else 0.0

            # ご褒美の合計
            reward = proximity_reward + forward_reward + eye_reward
```

### 3. 描画とロジックの分離
困った挙句にマルチスレッドにしてしまいましたが、グローバル変数使ったり美しくない。OpenAI gymに寄せるか、ugonamaさんみたいに描画サーバ方式に寄せるか…。

以上