Skip to content

Latest commit

 

History

History
606 lines (453 loc) · 23.1 KB

README.md

File metadata and controls

606 lines (453 loc) · 23.1 KB

PAIA 賽車機器學習平台

PAIA 賽車機器學習平台是一個可以用人工智慧去玩賽車遊戲的平台。

gameshot

目錄

  1. 遊戲下載及設定
  2. 概覽
  3. PAIA 平台使用方法
  4. 資料格式 Spec
  5. PAIA 工具
  6. 資料收集(Demo Recorder)
  7. 常見問題 FAQ

1. 遊戲下載及設定

Release:PAIA Kart v1.0.1

解壓縮後,需要在 PAIA 資料夾底下進行:

  • 修改 .env.template 檔,調整設定,並儲存成 .env 在同樣的目錄下(PAIA 資料夾底下)
    • Linux 或是 Mac 的用戶要記得先顯示隱藏的檔案
  • 新增 ml 資料夾(或自訂名稱),並在底下加入:
    • ml_play.py:訓練用的檔案
    • inferencing.py:比賽或是使用訓練結果的檔案
    • 其他相關檔案
    • 請將 .env 檔底下的 PLAY_BASE_DIR 設為 ml 資料夾路徑(或剛剛自訂的名稱)
    • 如果要進行訓練
      • 請將 .env 檔底下的 PLAY_SCRIPT 設為 "${PLAY_BASE_DIR}/ml_play.py"
    • 如果要進行比賽或是使用訓練結果
      • 請將 .env 檔底下的 PLAY_SCRIPT 設為 "${PLAY_BASE_DIR}/inferencing.py"
  • 記得安裝 Python 環境,參考下面的 環境建置
    • 在 PAIA 資料夾下執行 pip install -r requirements.txt
  • 其他設定請參閱以下文件

2. 概覽

2.1. 遊戲控制

如果使用手動模式,可以用方向鍵控制遊戲:

  • 向上方向鍵:加速
  • 向下方向鍵:減速或倒退
  • 向左或向右方向鍵:左轉或右轉
  • 方向鍵可以同時使用

2.2. 道具

油料(Gas) 輪胎(Wheel) 氮氣(Nitro) 烏龜(Turtle) 香蕉(Banana)
gas wheel nitro turtle banana

在 .env 的 PLAY_PICKUPS 可以設定要不要使用道具。

補充類道具

補充類道具如果用完就跑不動了,如果快沒了速度會變慢,所以要適時的補充。

  • 油料:補充油料
  • 輪胎:補充油料

效果類道具

效果類道具會維持一段時間,有不同效果。

  • 氮氣:加速
  • 烏龜:減速
  • 香蕉:打滑(影響轉動的量)

3. PAIA 平台使用方法

3.1. 主要的部分

將你所寫的 MLPlay 類別放在 ml/ml_play.py(訓練腳本)、 ml/ml_play.py(比賽或使用訓練結果的腳本),可以改檔名(但是記得更新 PLAY_SCRIPT 環境變數),如下:

import logging # you can use functions in logging: debug, info, warning, error, critical, log
from config import int_ENV
import PAIA
from demo import Demo

class MLPlay:
    def __init__(self):
        self.demo = Demo.create_demo() # create a replay buffer
        self.step_number = 0 # Count the step, not necessarily
        self.episode_number = 1 # Count the episode, not necessarily

    def decision(self, state: PAIA.State) -> PAIA.Action:
        '''
        Implement yor main algorithm here.
        Given a state input and make a decision to output an action
        '''
        # Implement Your Algorithm
        # Note: You can use PAIA.image_to_array() to convert
        #       state.observation.images.front.data and 
        #       state.observation.images.back.data to numpy array (range from 0 to 1)
        #       For example: img_array = PAIA.image_to_array(state.observation.images.front.data)
        MAX_EPISODES = int_ENV('MAX_EPISODES', -1)

        self.step_number += 1
        logging.info(f'Epispde: {self.episode_number}, Step: {self.step_number}')

        img_suffix = f'{self.episode_number}_{self.step_number}'
        logging.debug(PAIA.state_info(state, img_suffix))

        if state.event == PAIA.Event.EVENT_NONE:
            # Continue the game
            # You can decide your own action (change the following action to yours)
            action = PAIA.create_action_object(acceleration=True, brake=False, steering=0.0)
            # You can save the step to the replay buffer (self.demo)
            self.demo.create_step(state=state, action=action)
        elif state.event == PAIA.Event.EVENT_RESTART:
            # You can do something when the game restarts by someone
            # You can decide your own action (change the following action to yours)
            action = PAIA.create_action_object(acceleration=True, brake=False, steering=0.0)
            # You can start a new episode and save the step to the replay buffer (self.demo)
            self.demo.create_episode()
            self.demo.create_step(state=state, action=action)
        elif state.event != PAIA.Event.EVENT_NONE:
            # You can do something when the game (episode) ends
            want_to_restart = True # Uncomment if you want to restart
            # want_to_restart = False # Uncomment if you want to finish
            if (MAX_EPISODES < 0 or self.episode_number < MAX_EPISODES) and want_to_restart:
                # Do something when restart
                action = PAIA.create_action_object(command=PAIA.Command.COMMAND_RESTART)
                self.episode_number += 1
                self.step_number = 0
                # You can save the step to the replay buffer (self.demo)
                self.demo.create_step(state=state, action=action)
            else:
                # Do something when finish
                action = PAIA.create_action_object(command=PAIA.Command.COMMAND_FINISH)
                # You can save the step to the replay buffer (self.demo)
                self.demo.create_step(state=state, action=action)
                # You can export your replay buffer
                self.demo.export('kart.paia')
        
        logging.debug(PAIA.action_info(action))

        return action

    def autosave(self):
        '''
        self.autosave() will be called when the game restarts,
        You can save some important information in case that accidents happen.
        '''
        pass

修改 decision function,由 State 產生 Action。

請在 .env 的 PLAY_SCRIPT 指定以上腳本所在路徑。

注意路徑的問題,如果要取得相對於以上腳本的資料夾位置,

可以使用 os.path.dirname(os.path.abspath(__file__))

可以參考 Get Path of the Current File in Python,用 __file__ 來獲取目前 Python script 所在的位置,

如果是一般相對的路徑,會以終端機所在的位置為準,兩者可能不同(腳本要假設可以放在任意資料夾下)。

3.2. 環境建置

3.2.1. 環境需求

  • Python 3
    • mlagents==0.26.0
    • pytorch(安裝方式參考 官網
    • numpy
    • Pillow
    • opencv-python
    • paramiko
    • ffmpeg-python
    • python-dotenv

如果沒有自己的虛擬環境,建議使用 Anaconda。

在 PAIA 資料夾下執行 pip install -r requirements.txt 來安裝這些套件。

如果要啟動錄影功能,系統要再額外加裝 ffmpeg

3.2.2. 環境變數(Environment Variables)

Windows 設定:

SET PAIA_ID=小組的識別號碼
SET PAIA_HOST=小組的SSH主機IP
SET PAIA_PORT=小組的SSH主機port
SET PAIA_USERNAME=小組的SSH帳號
SET PAIA_PASSWORD=小組的SSH密碼

其他作業系統(Linux、macOS)設定:

export PAIA_ID=小組的識別號碼
export PAIA_HOST=小組的SSH主機IP
export PAIA_PORT=小組的SSH主機port
export PAIA_USERNAME=小組的SSH帳號
export PAIA_PASSWORD=小組的SSH密碼

如果沒有特別設定,在關閉終端機之後環境變數的設定會失效。

如果覺得每次都要設定環境變數很麻煩,或者覺得太複雜,「建議」使用 .env 設定檔進行設置, Python 執行之前都會先匯入.env 設定檔中的環境變數。

請修改 .env.template 中的內容,並且存成 .env,可以參考 dotenv 的格式來做設定。

另外加上了一個 REQUIRE 變數,可以匯入其它 .env 檔。

在 Python 中獲取環境變數的方法:

from config import ENV, bool_ENV, int_ENV, float_ENV
ENV['環境變數名稱'] # 取得環境變數
ENV['環境變數名稱'] = "值" # 設定環境變數(值一定要先轉換為字串!)
bool_ENV('環境變數名稱', 預設值) # 轉換環境變數為布林值,預設值可加可不加,不加的話是 None
int_ENV('環境變數名稱', 預設值) # 轉換環境變數為整數,預設值可加可不加,不加的話是 None
float_ENV('環境變數名稱', 預設值) # 轉換環境變數為浮點數,預設值可加可不加,不加的話是 None

ENV 用法和一般的 Python dict ㄧ樣,而且 ENV 的值「必須」為「字串」型態!

因此,提供了 bool_ENV, int_ENV, float_ENV 三個函數來方便作轉換。

有時候程式執行不起來是因為安全性設定,請先檢查一下下載下來的執行檔是否可以執行(詳見:常見問題 FAQ)。

3.3. 執行方式

藉由修改 .env 中的 RUNNING_MODE 環境變數,我們可以切換遊戲模式。

所以可以統一用以下簡單的指令執行:

python ml.py

如果要頻繁切換遊戲模式,可以考慮用下面的做法:

3.3.1. 線上(Online)模式

線上模式是指遊戲端和玩家端分別在不同的電腦跑。

遊戲端(與 Unity 在同一台機器上):

設定 RUNNING_MODE=ONLINE,或是執行

python ml.py online

玩家端(用來做 training 或 inferencing 的):

設定 RUNNING_MODE=PLAY,或是執行

python ml.py play

開啟順序: 開啟伺服器端 -> 執行玩家端(可以開始連進來)

3.3.2. 離線(Offline)模式

線上模式是指遊戲端和玩家端在同一台電腦跑。

自動(training 或 inferencing),為預設模式:

設定 RUNNING_MODE=OFFLINE,或是執行

python ml.py offline

手動(可以用來收集資料):

設定 RUNNING_MODE=MANUAL,或是執行

python ml.py manual

3.3.3. 比賽模式

修改 game/players.json,會進行錄影。

跑比賽:

設定 RUNNING_MODE=GAME,或是執行

python ml.py game

3.3.4. 附註

環境變數 MAX_EPISODES 是最多跑幾回合遊戲就要結束,可以 from config import int_ENV,再用 int_ENV('MAX_EPISODES', -1) 取得。

因為有時候訓練會出現意想不到的錯誤,或是要中途開啟道具,所以平台設有中斷點,PLAY_CONTINUE 是說要不要從前一次中斷點繼續執行,PLAY_AUTOSAVE 是說要不要自動儲存中斷點。

如果想要錄製遊戲畫面,可以把 RECORDING_ENABLE 設為 true。

如果使用 Unity Editor,在 Restart 指令之後要自己重開遊戲,Build 版的就不用,會自動開啟。

4. 資料格式 Spec

參考 communication/protos/PAIA.proto 檔案

4.1. 狀態資訊

事件(PAIA.Event)定義,用法可以參考上面主要的部分的範例:

enum Event { // 事件
	EVENT_NONE; // 一般狀態
	EVENT_FINISH; // 結束(其他狀況)
	EVENT_RESTART; // 重新開始回合
	EVENT_WIN; // 結束(有在時限內完成)
	EVENT_TIMEOUT; // 超時
	EVENT_UNDRIVABLE; // 不能動了(用完油料或輪胎)
}

狀態資訊(PAIA.State)定義:

struct State { // 狀態資訊
	struct Observation { // 觀測資訊
		struct Ray { // 單一雷達資訊
			bool hit; // 是否在觀測範圍內
			float distance; // 距離(把最大觀測範圍當作 1)
		}
		struct RayList { // 所有雷達資訊
			Ray F; // 前方
			Ray B; // 後方
			Ray R; // 右方
			Ray L; // 左方
			Ray FR; // 前方偏向右方 30 度
			Ray RF; // 前方偏向右方 60 度
			Ray FL; // 前方偏向左方 30 度
			Ray LF; // 前方偏向左方 60 度
			Ray BR; // 後方偏向右方 45 度
			Ray BL; // 後方偏向左方 45 度
		}
		struct Image { // 影像資料
			bytes data; // 位元資訊
			int height; // 高(預設為 112)
			int width; // 寬(預設為 252)
			int channels; // 頻道數(預設為 RGB = 3)
		}
		struct ImageList { // 影像資料們
			Image front; // 前方的影像
			Image back; // 後方的影像
		}
		struct Refill { // 補充類道具
			float value; // 剩餘量
		}
		struct RefillList { // 補充類道具們(其中之一用完就動不了)
			Refill wheel; // 輪胎
			Refill gas; // 油料
		}
		struct Effect { // 效果類道具
			int number; // 作用中的道具數量
		}
		struct EffectList { // 效果類道具們
			Effect nitro; // 氮氣(加速)
			Effect turtle; // 烏龜(減速)
			Effect banana; // 香蕉(打滑)
		}
		RayList rays; // 雷達資料們
		ImageList images; // 影像資料們
		float progress; // 進度(把全部完成當作 1)
		float usedtime; // 經過的時間(秒)
		float velocity; // 速度
		RefillList refills; // 補充類道具們
		EffectList effects; // 效果類道具們
	}
	string api_version; // API 版本
	string id; // 使用者名稱
	Observation observation; // 觀察資料
	Event event; // 事件
	float reward; // 獎勵(由 Unity 端提供的)
}

4.2. 動作資訊

動作指令(PAIA.Command)定義,用法可以參考上面主要的部分的範例:

enum Command { // 想要做的指令
	COMMAND_GENERAL; // 一般動作
	COMMAND_START; // 開始
	COMMAND_FINISH; // 結束
	COMMAND_RESTART; // 重新開始
}

動作資訊(PAIA.Action)定義:

struct Action { // 動作資訊
	string api_version; // API 版本
	string id; // 使用者名稱
	Command command; // 動作指令
	bool acceleration; // 是否加速
	bool brake; // 是否減速
	float steering; // 轉彎(-1.0 ~ 1.0,0 是不轉,偏向 -1 式左轉,偏向 1 是右轉)
}

5. PAIA 工具

PAIA 套件裡面有附一些工具,像是狀態(State)和動作(Action)的處理,可以使用。

下面會列舉一些會用到的,其他部分可以自行參考 PAIA.py 檔。

5.1. 影像資料轉換

PAIA.State 所提供的影像格式為 bytes 形式的 PNG,存放於影像類別觀察資料的 data 欄位中。 使用 PAIA.image_to_array(data) 可以轉換影像資料為 Numpy array 的形式:

例如:

import PAIA

img_front = PAIA.image_to_array(state.observation.images.front.data)
img_back = PAIA.image_to_array(state.observation.images.back.data)

注意轉換後的影像為三維度的 Numpy array,值的範圍在 0 到 1 之間。

5.2. 省略圖片的位元資訊

因為在印出 State 或是 Action 時也會把當中的 image 所有位元都印出來,造成很大的不方便,

這裡提供了 state_info() 還有 action_info() 這兩個 function,會省略圖片,

或是如果 .env 裡面 IMAGE_ENABLE 環境變數有設為 true 的話,會將圖片存到指定資料夾。

用法:

import PAIA

s = PAIA.State()
# 後綴檔名可以是圖片的編號(例如當前的回合、第幾步),也可以不加
text = str(PAIA.state_info(s, 後綴檔名)) # 把 s 的去影像化資訊轉成字串
print(text) # 顯示 s

a = PAIA.Action()
print(PAIA.action_info(a)) # 顯示 a

5.3. 產生動作 Action 物件

用法:

import PAIA

a0 = PAIA.create_action_object(acceleration=False, brake=False, steering=0) # 不動
a1 = PAIA.create_action_object(acceleration=True, brake=False, steering=0.0) # 往前走,不轉彎
a2 = PAIA.create_action_object(acceleration=True, brake=False, steering=-1.0) # 往前走,左轉
a3 = PAIA.create_action_object(acceleration=True, brake=False, steering=1.0) # 往前走,右轉
a4 = PAIA.create_action_object(acceleration=False, brake=True, steering=0.0) # 往後走或減速,不轉彎
a5 = PAIA.create_action_object(acceleration=False, brake=True, steering=-1.0) # 往後走或減速,左轉
a6 = PAIA.create_action_object(acceleration=False, brake=True, steering=1.0) # 往後走或減速,右轉

6. 資料收集(Demo Recorder)

Demo 除了記錄手動玩的結果,用來進行 Imitation Learning(模仿學習,像是 Behavior Cloning)以外,也可以用來儲存遊戲資訊。

DEMO_ENABLE 是空至曜不要錄製 Demo 的環境變數。

首先看到「步(Step)」的定義,類型為 PAIA.Step

struct Step { // 一步的資訊
	State state; // 狀態資訊
	Action action; // 動作資訊
}

我們不論是在手動或自動跑遊戲時,都可以用 demo 套件來儲存 Step(State 和 Action)的資訊。 使用 demo.Demo 中的初始化可以讀入 .paia 檔。 使用 demo.Demo 中的 export() function 可以匯出成 .paia 檔,方便日後更快速讀入資料。

使用 Demo 類別讀取 / 匯出錄製的資料:

from demo import Demo

# 在初始化時匯入資料
demo = Demo('.paia 檔的路徑')

# 在初始化時匯入資料(List 版本)
demo = Demo(['.paia 檔的路徑1', '.paia 檔的路徑2', ...])

# 匯入資料到最後
demo.load('.paia 檔的路徑')

# 匯入資料到最後(List 版本)
demo.load(['.paia 檔的路徑1', '.paia 檔的路徑2', ...])

# 匯出成 .paia 檔
demo.export('.paia 檔的路徑')

# 下面會產生一個新的空 Demo 物件
demo = Demo.create_demo()

# 下面會產生一個新的回合
demo.create_episode()

# 下面會產生一個新的 Step(包含 State 和 Action 的資訊)
demo.create_step(state=state, action=action)

# 使用 show() 函數可以顯示 demo 中的資訊
demo.show()

# 可以像 list 一樣存取資料(list 有的功能都可以),
# 例如下面取出 index 為 0 的回合,為一個 list
episode = demo[0]
# 例如下面取出 index 為 0 的回合的第 3 步,為一個 PAIA.Step 物件
step = demo[0][3]
print(step) # 印出 step 資訊,不省略圖片

# 除了用中括號 [],也可以用小括號 (),依據環境變數 IMAGE_ENABLE 決定是否要儲存照片,
# 並且修改成方便轉換成字串顯示的形式(但圖片資料會被省略),
# 第一個參數為回合的 index,第二個參數為 Step 的 index
# 如果 index 是整數就是一般的 index,是 list 則是很多 index,是 None 代表全部
# 例如下面取出 index 為 0 的回合的所有 Step
step = demo(0)
# 例如下面取出 index 為 0 的回合的第 3 步
step = demo(0, 3)
print(step) # 印出 step 資訊,且省略圖片
# 例如下面取出 index 為 0, 1 的回合的各自的第 3、7 步
steps = demo([0, 1], [3, 7])

7. 常見問題 FAQ

Q:能不能使用相對路徑?

A:建議不使用,因為會發生執行環境當前路徑和腳本路徑不同的問題,所以建議使用 os.path.dirname(os.path.abspath(__file__)) 來取得相對於腳本的資料夾。

Q:遊戲程式沒有跳出來怎麼辦?或是程式明明路徑是對的卻跳出 Provided filename does not match any environments?

A:可以看看是否為安全性問題,先是能不能手動開啟 App(終端機會顯示遊戲程式所在位置),每個系統有不同的解決方法,Windows 請參考:將排除項目新增到 Windows 安全性,Mac 請參考:在 Mac 上安全地開啟 App

Q:出現如下錯誤訊息?

TypeError: Invalid first argument to `register()`. typing.Dict[mlagents.trainers.settings.RewardSignalType, mlagents.trainers.settings.RewardSignalSettings] is not a class.

A:Python 3.9.10 以上目前與 mlagents-learn 套件不相容,請降級至3.9.9。參考:Python 3.9.10 causes mlagents-learn not to work

Q:有一些 Module 不能使用,或是遇到 ModuleNotFoundError?

A:請先照前面的 環境需求 安裝套件,記得要先切換到 PAIA 目錄底下(裡面才有 requirements.py 檔)。

Q:遇到 pytorch 的問題時?

A:看看是不是下錯指令,pip 要裝 torchconda 才是裝 pytorch,請參考 官網 說明。

Q:如果遇到如以下的錯誤訊息該怎麼處理?

ERROR: Could not find a version that satisfies the requirement torch<1.9.0,>=1.8.0; platform_system != "Windows" and python_version >= "3.9" (from mlagents==0.26.0->-r requirements.txt (line 1)) (from versions: none)
ERROR: No matching distribution found for torch<1.9.0,>=1.8.0; platform_system != "Windows" and python_version >= "3.9" (from mlagents==0.26.0->-r requirements.txt (line 1))

A:建議可以升級 mlagents 到較新版本,例如 pip install mlagents==0.27.0 或是 pip install mlagents==0.28.0,如果還是不行的話可以考慮降級 pytorch 版本到他所建議的版本(像上面的錯誤訊息是說大於等於 1.8.0,小於 1.9.0 的版本),可以參考 INSTALLING PREVIOUS VERSIONS OF PYTORCH

Q:出現如下錯誤訊息?

ImportError: cannot import name 'resolve_types' from 'attr' (/opt/anaconda3/lib/python3.8/site-packages/attr/__init__.py)

A:試試安裝 pip install cattrs==1.0.0

Q:python ml.py 不能執行?

A:請先檢查是否已經經切換工作目錄到 PAIA 資料夾底下再執行。如果是使用虛擬環境(venv)或是 Anaconda 等,請確定是否切換到正確環境。

Q:我用 Command line 執行程式,但是遊戲好像有開起來不過一直黑畫面?

A:請檢查你的 PLAY_SCRIPT 環境變數對應到的路徑存不存在,像預設值會是 ml/ml_play.py(如果有自己指定的路徑可以自行修改成你要跑的腳本路徑)。

Q:出現像是下面的錯誤訊息?

self.actions[self.behavior_names[action.id]] = action
KeyError: 'xxx87'

A:把 .env 裡面的 PLAYER_ID 改成數字試試看。