やってみた!

やってみた!

試したことを中心に、書評や興味のあること、思ったこととか

Open AI Gym Box2D BipedalWalkerをColaboratoryで動かしてみる(3)

 前回の続きです。歩行のスケジュールをチューニングした結果、2,3歩歩けるようになりました。これ以上は胴体の傾きをつかって制御するなどもう一工夫必要そうです。 

  スケジュール制御での歩行は一旦このくらいにしておきます。とりあえず現状の歩行を以下に報告します。

(2)歩行スケジュールのチューニング

 チューニング結果の歩行スケジュールを次に示します。

xsch = np.array([0,1,2,3,4,5,6,7,8,9,10],dtype = 'float')
yhip1  = np.array([ 0.021, -0.39, -0.58, -0.54, 0.45,    0.18, 0.88,  0.78, 0.60,  0.20, 0.021],dtype = 'float')
yknee1 = np.array([  0.17,  0.85,  0.95,  0.92, -0.38, -0.05,  -0.34,  -0.090,  0.074, 0.043,  0.17],dtype = 'float')
yhip2  = np.array([ 0.18,  0.88,  0.78,  0.60,  0.20, 0.021, -0.39, -0.58, -0.54, 0.45,    0.18],dtype = 'float')
yknee2 = np.array([ -0.05,-0.34,-0.090,  0.074,  0.043 ,  0.17,  0.85, 0.95,  0.92, -0.38, 0.05],dtype = 'float')

 前回のソースリストで上記スケジュールを差替え、次の赤文字のspeedを5→4.2に変更します。

for i in range(1):
    ・
    ・
    speed = 5

 歩き方はこんな感じ。スキップのような歩き方になりました。実行するたび若干変わりますが、たぶん地面のでこぼこが毎回ちょっとずつ違うんじゃないかと思います。

f:id:akifukka:20191212220343j:plain

 さすがに完全手動でチューニングするのは面倒なので、乱数でスケジュールをちょっとずつ変更してはBipedalWalkerの報酬(reward)が多ければ採用、小さければ不採用として、ある程度自動でチューニングさせました。ただし、報酬が安定しない(地面のでこぼこの違い)ので、最終的にどの数値を採用するかはセンス?適当に選びました・・・。

 つぎはぎで、あまり参考になりませんが乱数でチューニングするのに使ったソースリストを残しておきます。

import torch
import numpy as np
import gym
import random
from pyvirtualdisplay import Display
from scipy.interpolate import interp1d

display = Display(visible=0, size=(1024, 768))
display.start()
import os
#pyvirtualdisplayの仕様変更で次の文はエラーになるのでコメントアウトします(2021/6/5修正)
#os.environ["DISPLAY"] = ":" + str(display.display) + "." + str(display.screen)

#Bipedalwalkerのv2は無くなったのでv3に変更(2021/6/5)
#env = gym.make('BipedalWalker-v3')

#角度制御ゲイン
 #P(比例)項
pgain = np.array([4,4,4,4], dtype = 'float')
 #D(微分)項
dgain = np.array([0.2,0.2,0.2,0.2], dtype = 'float')

#脚の指令角スケジュール
xsch = np.array([0,1,2,3,4,5,6,7,8,9,10],dtype = 'float')
yhip1  = np.array([ 0.021, -0.39, -0.58, -0.54, 0.45,    0.18, 0.88,  0.78, 0.60,  0.20, 0.021],dtype = 'float')
yknee1 = np.array([  0.17,  0.85,  0.95,  0.92, -0.38, -0.05,  -0.34,  -0.090,  0.074, 0.043,  0.17],dtype = 'float')
yhip2  = np.array([ 0.18,  0.88,  0.78,  0.60,  0.20, 0.021, -0.39, -0.58, -0.54, 0.45,    0.18],dtype = 'float')
yknee2 = np.array([ -0.05,-0.34,-0.090,  0.074,  0.043 ,  0.17,  0.85, 0.95,  0.92, -0.38, 0.05],dtype = 'float')
numx = xsch.shape[0]

hip1demmand = interp1d(xsch, yhip1)
knee1demmand = interp1d(xsch, yknee1)
hip2demmand = interp1d(xsch, yhip2)
knee2demmand = interp1d(xsch, yknee2)

graphschys =[]
graphschys.append([hip1demmand(0)])
graphschys.append([knee1demmand(0)])
graphschys.append([hip2demmand(0)])
graphschys.append([knee2demmand(0)])
graphschxs =[0.0]

for graphschx in np.arange(0.1,10.0,0.1):
  graphschxs.append(graphschx)
  graphschys[0].append(hip1demmand(graphschx))
  graphschys[1].append(knee1demmand(graphschx))
  graphschys[2].append(hip2demmand(graphschx))
  graphschys[3].append(knee2demmand(graphschx))

#モデルタイムステップ(1秒間に50フレーム = 0.02s)
FPS = 50.0

speed = 4.2
bestspeed = 4.2
bestreward = -9999.0

prevyhip1 = yhip1.copy()
prevyknee1 = yknee1.copy()
prevyhip2 = yhip2.copy()
prevyknee2 = yknee2.copy()

for i in range(30):
    obs = env.reset()
    done = False
    frames = []
    observations = []
    actions = []
    ts = []
    legangle = np.array([0,0,0,0], dtype = 'float')
    leganglespeed = np.array([0,0,0,0], dtype = 'float')
    legangledemand = np.array([0,0,0,0], dtype = 'float')

    x = 0.0
    t = 0
    leg1gcount = 0
    leg2gcount = 0
    leg1touchground =0
    leg2touchground =0
    reward = 0.0

    while not done and t < 1000:
        #動画用画面保存-------------------------------
        frames.append(env.render(mode = 'rgb_array'))

        #指令値生成-----------------------------------
        x += speed/FPS
        if x>10.0 :x-=10

        #接地でスケジュール強制的に進める
#        if x>0.5 and x<2.5 and leg2touchground == 1:x = 4.5 - x
#        if x>5.5 and x<7.5 and leg1touchground == 1:x = 9.5 - (x - 5)

        legangledemand[0] = hip1demmand(x)
        legangledemand[1] = knee1demmand(x)
        legangledemand[2] = hip2demmand(x)
        legangledemand[3] = knee2demmand(x)

        #角度指令値上書き。スケジュールで動かす時はコメントアウトすること
#        legangledemand = np.array([1,-1,-1,-1], dtype = 'float')

        #脚の角度 PD制御
        action = pgain * (legangledemand - legangle) - dgain * leganglespeed

        #2足歩行物理モデル1ステップ(0.02s)-------------
        observation, r, done, info = env.step(action)
        reward = reward + r

        #脚の角度,角速度,胴体角取り出し----------------
        legangle[0] = observation[4] #HIP1
        leganglespeed[0] = observation[5]
        legangle[1] = observation[6] #KNEE1
        leganglespeed[1] = observation[7]
        legangle[2] = observation[9] #HIP2
        leganglespeed[2] = observation[10]
        legangle[3] = observation[11] #KNEE2
        leganglespeed[3] = observation[11]

        #leg1 接地判定 チャタリング防止
        if observation[8] == 1:
          if leg1gcount <3 :leg1gcount += 1
        else:
          leg1gcount = 0
        
        if leg1gcount == 3:
          leg1touchground = 1
        else:
          leg1touchground = 0

        #leg2 接地判定 チャタリング防止
        if observation[13] == 1:
          if leg2gcount <3 :leg2gcount += 1
        else:
          leg2gcount = 0;

        if leg2gcount == 3:
          leg2touchground = 1
        else:
          leg2touchground = 0

        #グラフ用データ保存----------------------------
        if t == 0 :
          for i in range(24):
            observations.append([observation[i]])
          for i in range(4):
            actions.append([action[i]])
        else :
          for i in range(24):
            observations[i].append(observation[i])
          for i in range(4):
            actions[i].append(action[i])
        ts.append(t/FPS)

        #時間を進める----------------------------------
        t += 1
  
    if reward > bestreward:
#      bestspeed = speed
      bestreward = reward.copy()
      print(yhip1)
      print(yknee1)
      print(yhip2)
      print(yknee2)
      print(i,bestreward)
    else:
      #元に戻す
      yhip1 = prevyhip1.copy()
      yknee1 = prevyknee1.copy()
      yhip2 = prevyhip2.copy()
      yknee2 = prevyknee2.copy()

    #一時退避
    prevyhip1 = yhip1.copy()
    prevyknee1 = yknee1.copy()
    prevyhip2 = yhip2.copy()
    prevyknee2 = yknee2.copy()

    prev1 = random.randint(0,1)
    prev21 = random.randint(0,9)
    #両足同じスケジュールにする
    prev22 = prev21 + 5
    if prev22 >9:
      prev22 = prev22 - 10 

    if prev1 == 0:
      yhip1[prev21] += random.uniform(-0.1,0.1)
      yhip2[prev22] = yhip1[prev21]
      #端点は同じ値を維持
      yhip1[10] = yhip1[0]
      yhip2[10] = yhip2[0]
    elif prev1 == 1:
      yknee1[prev21] += random.uniform(-0.1,0.1)
      yknee2[prev22] = yknee1[prev21]
      #端点は同じ値を維持
      yknee1[10] = yknee1[0]
      yknee2[10] = yknee2[0]

#    speed = bestspeed + random.uniform(-1, 1)
    hip1demmand = interp1d(xsch, yhip1)
    knee1demmand = interp1d(xsch, yknee1)
    hip2demmand = interp1d(xsch, yhip2)
    knee2demmand = interp1d(xsch, yknee2)

#end for loop -----------------------------------------

#元に戻す
yhip1 = prevyhip1
knee1 = prevyknee1
yhip2 = prevyhip2
knee2 = prevyknee2

 ちなみにspeedも乱数で変更して報酬が多くなるよう選びました。

 pythonは慣れないこともあり変数への '=' が代入では無く、新たに作られるオブジェクトへの参照だと知らなくて少し手間取りました。最初、スケジュールを退避したはずが、退避できずにおかしな動きをしていました。copy()を追加、新らたなオブジェクトを作ることで正しく動くようになりましたが、今後も変数の使い方は注意しないと。

 a+=1、a-=1といった演算も新たにオブジェクトを作ってくれないということで、pythonに慣れるまで気を付けないといけなさそうです。(a=a+1の場合は右辺で新たなオブジェクトを生成してくれるとのこと)。

 さて、スケジュール制御はここまでとして、次回からニューラルネットワークを使っていく予定です。今日は短いけどおしまい。

つづく

 強化学習 カテゴリーの記事一覧 - やってみた!