Kei Minagawa's Blog

皆川圭(@keimina)のブログ、Pythonで試したことを書いていきます

gymモジュール Pendulum-v0 で振り子をトルクを与えて静止させる

前回に続き、gym モジュールの Pendulum-v0 の理解を深めます。今回は、手で振り子を水平状態にし、その状態をキープするようにトルク与えて、振り子を静止させるシミュレーションを行います。今回も、強化学習は行いません。シミュレータの理解を深めます。

1 前回の記事

前々回、前回の記事は以下の通りです。本記事を読むにあたり参考になるかもしれません。

1.1 gymモジュール Pendulum-v0 で振り子を等速度で回転させる

1.2 gymモジュール Pendulum-v0 で振り子を揺らす

2 今回行うこと

今回は、手で振り子を水平状態にし、その状態をキープするようにトルク与えて、振り子を静止させるシミュレーションを行います。この日本語を、プログラム的に表すと以下になります。

No. 日本語 プログラム 説明
1 手で振り子を水平状態にする initial_theta = pi/2 水平状態に相当する角度は pi/2 のため、振り子の初期の角度を pi/2にします。手をそっと離すため、初期の速度は0にします。
2 その状態をキープするようにトルク与えてシミュレーションする env.step([-5]) 手を離したあと、物理法則に従って振り子が動いて欲しいため env.step を使用します。引数の値はトルクを表し、引数を[-5]とすることで振り子を静止させます。([-5]にする理由は後述)

表1. 今回行うこと

手を離したときに振り子の状態をキープするために重力が振り子に与える力と同じ力を与える必要があります。結論を言ってしまうとトルクとして-5を与えれば良いのですが、これをどう導き出したのか以下に記載します。

最初に、使用する物理的な変数を以下の表にまとめます。

No. 変数名 説明
1 m 質量
2 g 重力
3 l 棒の長さ

表2. 物理的な変数の説明

まずは、水平状態で振り子にかかる力を求めます。振り子が水平の時、垂直方向にかかる力は重力しかないので、振り子にかかる力以下になります。

F = m * g

このFと同じ力を、重力と反対方向にかければ力が釣り合い、静止します。これは、振り子に重力と反対方向に g の加速度を与えてあげれば良いということです。

gym の Pendulum の環境では振り子に与える力を、支点からの距離と力の積であるトルクで指定する必要があるため、上記 F と釣りあうトルクを求めます。

トルクを求めるには、回転の中心である支点からの距離lに、その点にかかっている力 F を掛け算します。

T = F * l

回転体が糸と球でできているなら、この式で正しいですが、シミュレータをよく見みると、回転体は棒でできていることがわかります。質量の全てが棒の先端にあるわけではないため、棒の回転に必要なトルクは、そうである場合に比べて少なくてすむはずです。この場合、回転体の重心の位置(支点からl/2 離れた点)に質量mの球があるという問題に置き換えて考えます。以上より、今回の振り子(振り棒?)の重力を支えるために必要なトルクは以下になります。

T = F * l / 2

T = m * g * l / 2

最後に、実際にプログラムで指定すべきトルクの値を求めるため、gymモジュール の Pendulum で定義されている m, g, l の実際の値を調べます。調べたところそれぞれのデフォルトの値は以下になっていました。

No. 変数 デフォルト値
1 m 1
2 g 10
3 l 1

表3. gymモジュール の Pendulum で定義されている m, g, l の実際の値

よって、今回与えるべきトルク T は以下になります。(重力の反対方向に力を与えるため、マイナスにしています)

T = -1 * 10 * 1 / 2

T = -5

注意点としては、Pendulum ではトルクの上限がデフォルトで 2 までとなっています。今回は、トルクとして 5 を与える必要があるので、この上限を引きあげる必要があります。上限の設定は max_torque 変数を使用します。

3 コード

今回書いたコードは以下になります。

import gym
import time
from math import pi

env = gym.make('Pendulum-v0') # 強化学習の文脈における「環境」の作成
env.reset()                   # 環境の初期化(明示的に行う必要がある)
env.env.max_torque = 5        # トルクの上限を引き上げる               <---!前回との差分はこの行
# 初期状態
initial_theta = pi/2
initial_speed = 0
theta = initial_theta
speed = initial_speed
env.env.state = theta, speed # 振り子の theta, speed を設定
env.render('human')          # 描画
time.sleep(0.033)            # 待つ、 0.033 は 30fps にするため

for i in range(300):         # 30fps で 10秒間表示
        env.step([-5])            # トルク -5 で振り子を次の位置に更新する <---!前回との差分はこの行
        env.render('human')      # 描画
        time.sleep(0.033)        # 待つ、 0.033 は 30fps にするため

env.close()

4 コードの説明

env.step([x]) で、トルクx を振り子に与えて、シミュレーションを 1ステップ進めています。今回は env.step([-5])で、トルク-5を与えて、シミュレーションを1ステップ進めています。そして、 env.env.max_torque = 5 でトルクの上限を引き揚げています。前回との差分は以下の通りです。

No. 行数 前回 今回 説明
1 7 - env.env.max_torque = 5 トルクの上限を5に引き揚げる
2 18 env.step([0]) env.step([-5]) トルク-5を与え、シミュレーションを1ステップ進める

表4. 前回のコードとの差分

5 出力結果

出力結果は以下になります。

f:id:keimina:20200308170625p:plain

図1. 出力結果1

水平状態で重力と釣り合うようなトルクを与えているため、動かないことがわかります。

ただ、動かないと、本当にプログラムが正しく動作しているかわからないため、トルクをわずかに減らして T =-4.9 として実行して見ます。振り子がフラフラし、プログラムが動作していることがわかります。

f:id:keimina:20200308160812g:plain

図2. 出力結果2

6 まとめ

gymモジュールの Pendulum-v0 の環境を使用し、 手で振り子を水平状態にし、その状態をキープするようにトルク与えて、振り子を静止させるシミュレーションを行いました。

以上です。