DonkeyCar simulatorで強化学習のサンプルを実行してみる(PPO2編)
DonkeyCar simulatorで強化学習(その1) ではポリシーにDDQNを使用したサンプルを実行してみたが、今回はもう一つのサンプル(PPO2を使用)を試してみる。
# 作業ディレクトリ作成
mkdir -p /work2/donkey_sim2
cd /work2/donkey_sim2
# 仮想環境構築
pyenv install 3.7.12
pyenv virtualenv 3.7.12 donkey_sim2
pyenv local donkey_sim2
pip install --upgrade pip setuptools wheel
# 必要なライブラリのインストール
pip install opencv-python
pip install stable-baselines
pip install tensorflow==1.14.0
# 現時点での最新リリース v21.7.24 をcheckoutしておく
git clone https://github.com/tawnkramer/gym-donkeycar -b v21.07.24
# gym-donkeycarライブラリのインストール
pip install -e gym-donkeycar
[!NOTE] stable-baselines は tensorflow ~1.14.0 しかサポートしていないので、バージョン指定してインストールする。
tensorflow 1.14.0 は python ~3.7 しかサポートしていないので、3.7系の最新版を使用している。
[!NOTE] gym-donkeycar は -e (–editable) オプション付きでインストールしているので、編集も可能。 git cloneした先を参照しているので、インストール後もgym-donkeycarを削除しちゃダメ。
githubから直接インストールする場合は以下。
この場合、パッケージは通常のディレクトリにインストールされる。pip install git+https://github.com/tawnkramer/gym-donkeycar@v21.07.24
でも、以下でサンプルプログラム使うので、git cloneもしないとダメ。
DonkeyCar simulatorで強化学習(その1) と同じ
用意されているサンプルプログラムにパッチをあてようと思ったのだけど、
ppo_train.py
はやっつけ感満載のイマイチソースなので いっそ全書き換えで。
主な対応内容は、
--host
)--step
)--test_step
)ppo.py
import sys
import os
import signal
import argparse
import uuid
import datetime
import typing
from typing import Union, List, Dict, Any, Optional
import gym
import gym_donkeycar
from stable_baselines import PPO2
# from stable_baselines.common import set_global_seeds
from stable_baselines.common.policies import CnnPolicy
from stable_baselines.common.vec_env import DummyVecEnv, VecEnv
from stable_baselines.common.callbacks import EventCallback
from stable_baselines.common.base_class import BaseRLModel
class MyCallback(EventCallback):
def __init__(self,
eval_env: Union[gym.Env, VecEnv],
save_freq: int = 1000,
save_file: Optional[str] = None,
verbose: int = 1):
super(MyCallback, self).__init__(verbose=verbose)
self.save_file = save_file
self.save_freq = save_freq
# Convert to VecEnv for consistency
if not isinstance(eval_env, VecEnv):
eval_env = DummyVecEnv([lambda: eval_env])
assert eval_env.num_envs == 1, "You must pass only one environment for evaluation"
self.eval_env = eval_env
def init_callback(self, model: 'BaseRLModel') -> None:
print("#### INIT ####")
super(EventCallback, self).init_callback(model)
def _init_callback(self):
print("#### _INIT ####")
def _on_step(self) -> bool:
if self.save_freq > 0 and self.n_calls % self.save_freq == 0:
now = datetime.datetime.now()
if self.verbose > 0:
print(f'{now} saving...')
if self.save_file :
now_str = now.strftime('%y%m%d_%H%M%S')
filename = self.save_file
# filename = os.path.join(os.path.dirname(self.save_file), f'{now_str}_{os.path.basename(self.save_file)}')
self.model.save(filename)
return True
if __name__ == "__main__":
# Initialize the donkey environment
# where env_name one of:
env_list = [
"donkey-warehouse-v0",
"donkey-generated-roads-v0",
"donkey-avc-sparkfun-v0",
"donkey-generated-track-v0",
"donkey-roboracingleague-track-v0",
"donkey-waveshare-v0",
"donkey-minimonaco-track-v0",
"donkey-warren-track-v0",
"donkey-thunderhill-track-v0",
"donkey-circuit-launch-track-v0",
]
parser = argparse.ArgumentParser(description="ppo_train")
parser.add_argument(
"--sim",
type=str,
default="sim_path",
help="path to unity simulator. maybe be left at manual if you would like to start the sim on your own.",
)
parser.add_argument("--model", type=str, default="ppo_donkey", help="path to model")
parser.add_argument("--host", type=str, default="127.0.0.1", help="simulator address")
parser.add_argument("--port", type=int, default=9091, help="port to use for tcp")
parser.add_argument("--step", type=int, default=10000, help="port to use for tcp")
parser.add_argument("--test_step", type=int, default=0, help="port to use for tcp")
parser.add_argument("--test", action="store_true", help="load the trained model and play")
parser.add_argument("--env_name", type=str, default="donkey-warehouse-v0", help="name of donkey sim environment", choices=env_list)
args = parser.parse_args()
test_step = args.test_step
if args.test and test_step == 0 :
test_step = 1000
# Complement the file extension
if not args.model.endswith(".zip") :
model_path = args.model + ".zip"
else :
model_path = args.model
conf = {
"exe_path": args.sim,
"host": args.host,
"port": args.port,
"body_style": "car01",
"body_rgb": (128, 255, 128),
"car_name": "me",
"font_size": 100,
"racer_name": "PPO",
"country": "USA",
"bio": "Learning to drive w PPO RL",
"guid": str(uuid.uuid4()),
"max_cte": 10,
}
# Make an environment test our trained policy
env = gym.make(args.env_name, conf=conf)
env = DummyVecEnv([lambda: env])
# hook terninate signal
def signal_handler(signal, frame):
print("catching ctrl+c")
env.close()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGABRT, signal_handler)
try :
# check model path
if os.path.exists(model_path):
# load model
model = PPO2.load(model_path, env = env)
else :
if not args.test:
# create model
print("create new model")
model = PPO2(CnnPolicy, env, verbose=1)
else :
raise ValueError(f"Error: the file {model_path} could not be found")
# change throttle lower limit
env.action_space.low[1] = 0.1
if not args.test:
# in training mode
callback = MyCallback(env, save_freq = 5000, save_file = model_path, verbose = 1)
# callback = MyCallback(env, save_freq = 10, verbose = 1)
# set up model in learning mode with goal number of timesteps to complete
# model.learn(total_timesteps=10000)
model.learn(total_timesteps=args.step, callback=callback)
# save model
model.save(model_path)
print("stert testing...")
obs = env.reset()
prev_done_count = 0
for i in range(test_step):
action, _states = model.predict(obs)
obs, rewards, dones, info = env.step(action)
# print(f"cnt : {i} rewards : {rewards[0]} dones : {dones[0]} pos : {info[0]['pos']}, CrossTrackError : {info[0]['cte']}, speed : {info[0]['speed']}")
# print(f"+++ info: {info} +++")
env.render()
if (dones) :
print(f'dones flag detected : {i - prev_done_count} ({i})')
prev_done_count = i
print("done testing")
except KeyboardInterrupt:
print("stopping run...")
env.close()
とりあえず学習。
リモートマシンでDonkeyCar シミュレータを実行しておき、以下のコマンドを実行します。
--sim
オプションにremote
を、実行マシンのIPアドレスを--host
オプションに指定します。
cd examples/reinforcement_learning
python ppo.py --sim=remote --host=192.168.78.200
[!NOTE] 学習回数を指定するには
--step
オプションで指定します。
指定する回数はエピソード数ではなく、アクション数。
例えば、--step=100000
[!NOTE] ローカルマシンでシミュレータを実行する場合は
--sim
オプションにDonkeyCar シミュレータ起動コマンドをフルパスで指定します。
このとき、--host
オプションは不要です。
シミュレータをあらかじめ起動しておく必要はなく、自動的に起動されます。
モデルの保存間隔はMyCallback
のインスタンス生成時にsave_freq = 5000
で指定していますので、必要なら変更してください。
学習のときのコマンドに--test
を指定するだけです。
テストを実行するステップ数を変更したい場合は--test_step
オプションで指定します。
python ppo.py --sim=remote --host=192.168.78.200 --test
途中で止めたい場合はCTRL-Cで止めてください。
ほど複雑じゃないので省略。