23 Jul 2017, 13:55

Twiddle(いじりまわし)でPID制御のパラメータ(ゲイン値)を求める

はじめに

前回の続き。

PID制御での、疑問点は、どうやってP,I,Dのゲイン値を求めるかということだ。

これについて、twiddle(いじり回し)アルゴリズムというのがあり、PIDのゲイン値の最適解が簡単に求まるらしい。

日本語の情報はおろか、英語の情報もなく、あるのは、AI for Roboticsの Forumの投稿記事くらいなので、どっからでてきたアルゴリズムなのかわからないけれども、スラン先生曰く、これは簡単に実装できて、とても強力、だそうだ。

アルゴリズム

  • 講義動画

[https://www.youtube.com/embed/2uQ2BSzDvXs]

  • スラン先生の板書。

ちょっと板書が汚いので、清書。

  • p = [0, 0, 0], dp = [1, 1, 1] で初期化する。
  • シミュレーションを実行して、best_error を求める。
  • while: sum(dp) > (しきい値)
    • for i in (パラメータ数)
      • p[i] += dp[i]
      • シミュレーションを実行して、error を求める。
      • if error が best_error よりも小さい
        • best_error = error
        • dp[i] = dp[i] * 1.1
      • else
        • p[i] = p[i] – 2*dp[i]
        • シミュレーションを実行して error を求める。
        • if error が best_errorよりも小さい
          • best_error = error
          • dp[i] = dp[i] * 1.0
        • else
          • p[i] = p[i] + dp[i]
          • d[i] = d[i] * 0.9

実装

前回と同じ、Afrel 楕円コースでのPID制御を用いた軌道走行を考える。

23 Jul 2017, 09:30

Afrel楕円コースの軌道をロボットカーでPID制御を使ってシミュレートしてみた

[mathjax]

はじめに

Udacityで Artificial Intelligence for Roboticsを受けている。

[blogcard url=”https://www.udacity.com/course/artificial-intelligence-for-robotics–cs373″]

この講座は、Googleカーの要素技術について学ぶ講座。 講師は、Udacityの創立者にしてGoogleカーの開発者、セバスチャン・スラン氏だ。

lesson5のテーマはPID制御。

この記事では、PID制御の概要を自分なりに整理し、目標とする軌道上をロボットが動くシミュレーション(Homework5.4)問題を書く。

PID制御とは

講義資料

講義動画

[https://www.youtube.com/embed/-8w0prceask]

PID制御とは、制御に使われる古典的な手法。P, I, D はそれぞれ、

  • P ・・・Propotional(比例)
  • I ・・・Integral(積分)
  • D ・・・Differential(微分)

を意味している。

今、自動車の制御を考える。目的とする基準軌道(横座標)と、車との距離をCross Track Error(CTE) という。

このときの望ましい制御は、CTEに比例して車のハンドルを切ること。そうすることで、スムーズに基準軌道へと車を近づけることができる。

今回、実験に使ったコードは以下。講義のなかのコードから抜粋した。

P 制御

Pは Propotional(比例)を表す。CTEに比例してハンドルを切る。式で表すと、

$$

steering = – \tau_p ・CTE

$$

厳密には、0 – \tau_p ・CTE としている。この問題設定では、xy座標で考えると、y=0を基準軌道としている。なので、(目標値) – (出力値) において、(目標値)が0となり、マイナス項だけが残る。

これをP制御という。P制御は、CTEが小さくなる、基準軌道に近づくに従って、修正量も比例して小さくなるようになっている。

しかし、P制御には、問題がある。以下の図のように、P制御しようとすると、基準軌道を通り越す(オーバーシュートという)してしまう。基準軌道に近づいては離れを繰り返して、安定しない。

PD制御

そこで、時間tで微分したCTEを式に加える。CTE_DIFFは時刻t-1におけるCTEから時刻tのCTEを引いた値。

$$

steering = – \tau_p ・CTE – \tau_d ・CTE_DIFF

$$

これによって、基準軌道にそった結果が得られた。

これを、PD制御という。

微分項を加えることで、制御の 反応性 が増す。この基準軌道の例では、仮に急激なCTEの減少が起こった場合、下の方にsteerを切りすぎないようにCTE_DIFFの値が大きくなって、オーバーシュートすることを防ぐ。

PID制御

I制御は、理解が曖昧で自分の言葉で説明できるかわからないので、参考にしたサイトの言葉を引用します。

偏差が小さいときに操作量が小さくなり過ぎてしまい、制御量が目標値とずれたところで安定してしまうということです。P制御の制御量は目標値より下側で安定してしまいます。この制御量と目的地の差を「定常偏差」あるいは「オフセット」と呼びます。

これは、例えば、タイヤが少し傾いていて、ハンドルを一定に切っても基準軌道に近づいてくれないようなシチュエーションで発生する。講義では、以下のようなメソッドで10度タイヤをずらすようなことをやっていた。

  robot.set_steering_drift(10.0/180.0*pi)

以下は、タイヤがずれた状態でのPD制御。

また引用。

ライントレーサの場合、P制御のパラメータ(Kp)が適正であれば、直線を走っているとき制御量は目標値付近で安定します。しかし、カーブがはじまるとオフセットが発生します。

カーブがはじまったとき、P制御によってその時点ごとの制御量に応じた操作量を決定し、カーブを曲がろうとします。目標値から離れれば離れるほど操作量が上がるP制御ですが、それは長所であると同時に短所にもなります。なぜなら、偏差が小さいと操作量も小さくなり、カーブの最中に偏差0の位置まで戻れず、カーブの外側に寄ってしまうからです。この状態が、目標値と違う場所で安定してしまう“オフセットが発生している状態”です。

オフセットが発生していてもカーブを抜けられればよいのですが、場合によってはコースアウトしてしまうことがあります。では、Kpを大きくし偏差が小さいときの操作量も大きくなるようにしてはどうでしょうか?

こうすることで、オフセットの発生は防げます。しかし、P制御では現在の値しか見ないため、いま直線にいるのか、カーブにいるのかが分かりません。そのため、P制御でせっかく力の抜き方を決めているのにもかかわらず、P制御のパラメータを大きくすると、カーブの間はいいのですが直線に戻っても操作量が大きくなってしまいます。その結果、せっかくオンオフ制御から改善したハンチングが再発する可能性があります。

そこで、「I制御」を用いてカーブにいる間だけ、操作量を上げるようにしましょう。

というわけで(笑)、I制御が必要となってきます。PD制御にIを加えた、最終的なPID制御式は、以下。

$$

steering = – \tau_p ・CTE – \tau_d ・CTE_DIFF – \tau_i ・* int_CTE

$$

$int_CTE$ は、時刻tまでのCTEの総和。これがハンチングに効いてくる。実験結果は以下。

Homework5.4

AI for Roboticsの Homework5.4は、レーストラックの軌道上をPID制御を使ってロボットカーを走らせるシミュレーションをすること。

自分は、課題のレーストラックを少し修正して、Afrelから売り出されている楕円コースの軌道をロボットが描くように修正してみた。

寸法は実物と同じなので、mindstorms ev3で実際にロボットカーをつくって走らせれば、軌道上をロボットが走行するはず!!実物を走らせるのは、次回!!

参考

12 Jul 2017, 13:57

USDJPYのヒストリカルデータをダウンロードしてDQN Agentを動かしてみた

前回の続き。

USDJPYでバックテスト

今回は、DukascopyからUSDJPYのヒストリカルデータをダウンロードして、DQN Agentを動かしてみました。

すごく苦労して、なんとかDQN Agentに学習させることに成功した。

ロジックは、前回とほぼ同じです。今回の Jupyter Notebookは以下。

苦労した点

  • はじめ、keras-rlで testメソッドを走らせたあとに、rewardが0になってしまった。いくら論理を見直しても、0になってしまう。
  • 波の形で学習できるときとできないときがあることが分かった。たとえば、サイン波は学習できるがコサイン波は学習できない。
  • 標準化を適用して、なるべくサイン波に近い形にヒストリカルデータを編集した。

11 Jul 2017, 22:55

Dukascopyのヒストリカルデータの日付をpandasでパースする方法

FXのヒストリカルデータを取得するために、私はいつもDukascopyを利用している。

おかしな日付

ダウンロードしたデータをプロットしてみたら、おかしなことになった。

self.csv_data = pd.read_csv(self.csv_path, index_col=0, parse_dates=True, header=0)

dukascopyの日時のフォーマットは、03.07.2017 00:00:00.000 のようになっている。

なので、2017年7月3日をpandasは間違えて 2017年3月7日としてパースしてしまったようだ。

正しい日付にパース

こういう場合は、自前のパーサを作成して、_pandas.readcsv に date_parser optionの引数としてパーサを渡すことで解決する。

f = '%d.%m.%Y %H:%M:%S.%f'
my_parser = lambda date: pd.datetime.strptime(date, f)
self.csv_data = pd.read_csv(self.csv_path, index_col=0, parse_dates=True, header=0,
                           date_parser=my_parser)

11 Jul 2017, 14:02

正弦曲線にしたがう為替の値動きについてDQNでバックテストをしてみた

DQNを勉強しているので、以下の記事に触発されて自分もFXでDQNをしてみたくなった。

2年前、OANDAでのシステムトレードで5万円を損した屈辱を今こそ晴らすのだ!

まずは、GMOの記事でも実施しているように、正弦曲線に従う為替の値動きについて、

DQNを適用してバックテストを行ってみた。

今回のJupyter Notebookは Gistにあげました。

正弦曲線

以下のような正弦曲線について、DQNでバックテストを試みる。

MDP

  • 状態
    • (-1.0, 1.0) の範囲の値
    • 売買のステータス(SELL, NONE, BUY)
  • 行動
    • SELL
    • HOLD
    • BUY
  • 報酬
    • 売買の即時報酬

コード

`import enum
class Action(enum.Enum):
    SELL = 2
    HOLD = 0
    BUY  = 1

class Position():
    def __init__(self):
        self.NONE = 0
        self.BUY = 1
        self.SELL = 2

        self.bid = 0.0
        self.pos_state = self.NONE

    def state(self):
        return np.array([self.bid, self.pos_state])

    def change(self, action, bid):
        self.bid = bid
        self.pos_state = action

    def close(self, action, bid):
        if self.pos_state == action:
            return 0

        self.pos_state = self.NONE
        if action == Action.BUY.value:
            reward = (bid - self.bid) * 1000
        else:
            reward = (self.bid - bid) * 1000
        return reward`

keras-rl を利用するために、環境を OpenAI gym ライクで作成する。

`import gym
import gym.spaces

class FXTrade(gym.core.Env):
    def __init__(self):
        self.action_space = gym.spaces.Discrete(3)
            high = np.array([1.0, 1.0])
            self.observation_space = gym.spaces.Box(low=-high, high=high)

            self._position = Position()

            self._sin_list = []
            for t in np.linspace(0, 48, 240):
                self._sin_list.append(np.sin(t))
            self.cur_id = 0

    def _step(self, action):
        bid = self._sin_list[self.cur_id]
        self.cur_id +=1
        done = True if self.cur_id == 240 else False

        if action == Action.HOLD.value:
            reward = 0
        else:
            if self._position.pos_state == self._position.NONE:
                self._position.change(action, bid)
                reward = 0
            else:
                reward = self._position.close(action, bid)
        return np.array([bid, self._position.pos_state]), reward, done ,{}

    def _reset(self):
        self.cur_id = 0
        self._position = Position()
        return np.array([0.0, 0.0])`

keras-rlの登場

強化学習を笑っちゃうほど簡単に実施するために Keras-rlをつかう。

このライブラリは抽象度が高すぎてもはやなにをやっているのかわからない。

exampleを参考にして、それっぽいコードを書いてみる。

`from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam

from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory

nb_actions = env.action_space.n

model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
model.summary()

# DQNに必要な Experience Replayのためのメモリ
memory = SequentialMemory(limit=50000, window_length=1)
# ボルツマン分布に従うポリシー
policy = BoltzmannQPolicy()
# DQN Agentの生成
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory,
               target_model_update=1e-2, policy=policy)
# optimizersはAdam, 損失関数は 平均二乗誤差
dqn.compile(Adam(lr=1e-3), metrics=['mae'])

# トレーニングを開始。同じ正弦曲線を9600 = 240 x 400回 回す。
dqn.fit(env, nb_steps=9600, visualize=False, verbose=1)

# トレーニング結果を確認
dqn.test(env, nb_episodes=5, visualize=False)

`

おお、rewardがプラスだ。。

`Testing for 5 episodes ...
Episode 1: reward: 603.962, steps: 240
Episode 2: reward: 603.962, steps: 240
Episode 3: reward: 603.962, steps: 240
Episode 4: reward: 603.962, steps: 240
Episode 5: reward: 603.962, steps: 240`

おわりに

  • 理論を抑えていないので何をやっているのかがいまいちわからないのだけれども、おそらく上がり調子のときは報酬もたくさんもらえるので、買い注文を多くしているのだと思う。
  • keras-rlは非常に強力なライブラリだけれども、抽象度が高すぎてなにやってるのかよくわからない。理解を深めるために numpyで実装してみるのもありかもしれない。
  • 状態は、その時の値のみを扱ったが、過去5bin分の状態を考慮にいれたらどうなるだろうか?いわゆるwindowの概念を強化学習にいれてみると、結果がよくなるだろうか?

09 Jul 2017, 08:58

CartPole問題にDQN(numpy only)で挑戦したけど解けなかった

前回の続き。

前回は、Kerasを利用したのだが、今回は numpyだけで実装してみた。ゼロから作るDeepLearningを大いに参考にした。

  • Kerasと同じことを実装はずなのに、結果が同じにならない。
  • エピソードを重ねても生存率が頭打ちになって、伸びない。
  • 調子のいいときと調子の悪いときがある。エピソードの開始時に運良く生存すると、その後の生存率が上がる。

結果

解けなかった。

https://gym.openai.com/evaluations/eval_iNrsSMkNSxW1wGF0b1lspg

コード

08 Jul 2017, 08:16

OpenAI Gymの CartPole問題をDQNで解いた

また、CartPole問題なのだが、今回はDeep Q-Network(DQN)で解いた。

解いたといっても、自力で解いたわけではなくて、Udacity DLNDの Reinforcement Learningの回のJupyter Notebookを参考にした。

このNotebookは tensorflowを利用しているのだけれども、理解を深めるためにKerasに移植してみた。

DQNのポイント

Deep Q-Network(DQN)とは、ディープラーニングと強化学習(Q学習)を組み合わせたアルゴリズム。

これがなんなのか、いまいちよくわからなかったので、参考になりそうなリンクをかき集めた。

Q学習で利用するテーブルをニューラルネットワークで関数近似しただけだと、

DQNとは呼ばないらしい。David Silver さんのスライドによると、

以下の3つの学習を促進するための工夫をしたことによって、DQNは安定した学習を可能にしている。

  • Experience Replay
  • Fixed Target Q-Network
  • 報酬のclipping

Fixed Target Q-Networkをいまいち理解していないので、正式なDQNではないのかも。Experience Replayは実装した。

これは、状態の相関関係によって学習がうまくいかないことを防ぐための手法。 の組を保存しておき、学習時に保存したデータ組からランダムにサンプリングしてミニバッチをつくり、それをニューラルネットで学習するという手法。アルゴリズムを Udacity DLNDから引用する。

  • Initialize the memory
  • Initialize the action-value network with random weights
  • For episode = 1, do
    • For , do
      • With probability select a random action , otherwise select
      • Execute action in simulator and observe reward and new state
      • Store transition in memory
      • Sample random mini-batch from :
      • Set if the episode ends at , otherwise set
      • Make a gradient descent step with loss
    • endfor
  • endfor

コード

結果

前回のQ学習が3000エピソードくらいで解けたのに対して、今回のDQNは250エピソードで解けた。大幅な進歩だ。

https://gym.openai.com/evaluations/eval_DYySvAnSxuP3oHjZchxaQ

05 Jul 2017, 15:56

Deep Q-Network(DQN)リンク集

日本語

英語

course

03 Jul 2017, 08:42

TD法による tic-tac-toe

前回の続き。

以下の本のoctaveでかかれたコードをpythonで書き直した。

勝率がよくないので、自分の実装がバグっている可能性大。でも、本でもそれほど勝率は高くなかったので、何とも言えない。

間違ったコードを公開することは抵抗があるけれども、今後誰かが勉強するための足がかりになればと思い公開。

SARSA法

サンプルコードのQテーブルの更新式が怪しい。Q学習と同じになっている。s-a-r-s-aになっていないのだ。なので、書籍とは違う実装にした。

Q学習

30 Jun 2017, 20:15

LEGO Mindstormsの crawler を強化学習で前に進むことを学習させた

LEGO x 強化学習の初の成果が出た。

強化学習の古典的問題、crawler に Q-Learningを適用して前に進むことを学習させたのだ

まずは見るのが早い。一つ目の動画は学習を開始して間もない動画。ランダムに尾をうごかして、運良く前にすすんでいることがわかる。

[https://www.youtube.com/embed/UD0_tK9DToc?ecver=1]

次に、10分ほど学習させた結果が以下。明らかに、意図して前にすすんでいることが分かる。

[https://www.youtube.com/embed/El13ZG2m_wY?ecver=1]

今回利用したアルゴリズムはQ-Learning。

赤外線センサで壁との位置を計測して、壁に塚づいたら報酬を与える。

以下が今回のソースコードです。