DRL在Unity自行車環境中配置與實踐
- 2019 年 11 月 21 日
- 筆記
深度強化學習報道
來源:CSDN Blog(洛陽羊肉湯-真香)
編輯:DeepRL
在強化學習的發展中,遊戲領域無疑是最好的研究環境,而最近強化學習在無人駕駛等決策等相關領域也有了廣泛的研究。本文講述則用深度強化學演算法(DRL)在unity環境中製作完全基於物理引擎的無人駕駛自行車學習以及相關流程。文章主要面向研究強化學習的人,而不是unity開發者。因此無人駕駛自行車的環境會貼出gym環境的形式而不是unity工程的形式供大家把玩。
首先說句題外話,有很多哥們問我:「你這個做的有點意思,但是有啥用呢?」 是的,我做的這個是虛擬環境中實現的,但是目前很多強化學習在現實中的應用都是先在虛擬環境加速訓練,再遷移到現實,比如anymal機器狗,樹枝做的機器人等等。甚至,像openai的Dactyl機械手,直接虛擬環境訓練好都不用微調,就能在現實中用,簡直天秀。其思想就是:與其儘可能的模擬真實,不如在與真實環境較為相似的隨機環境中訓練,就能適應真實環境。
一、環境搭建
雖然環境的搭建是用unity和C#寫的,只有後端是python訓練的,但只有了解了環境才好設置狀態,動作,獎勵函數,所以這部分內容也是非常重要的。
1.1
基於物理的自行車
DRL研究者不一定熟悉unity引擎,我下面首先對unity環境的搭建進行簡要介紹。unity是一個專業的3d遊戲引擎,有完整的物理系統。那麼對於無人駕駛自行車,我們首先需要一輛基於物理運動的車。unity有一個內置的用於賽車遊戲的車輪組件:Wheel Collider,它的參數除了圖中截到的車輪重量,車輪半徑,懸架彈力設置等等,還有前向,側向摩擦等設置,該組件達到的效果就是能較為真實的模擬各種車輪。對於自行車,對各個參數設置一個較為合理的數值,比如車輪半徑0.5m。

而這只是一個輪子的,自行車有兩個輪子,那就需要兩個Wheel Collider,在unity項目中,也就是front(前輪)和rear(後輪)

如果你使用的是其他遊戲引擎,一般都會有相應的賽車製作方法,方法大同小異。甚至如果真沒有相關支援,可以自己寫一個賽車物理系統,雖然這很麻煩(雖然不用從袁隆平說起,至少也要從發動機,一系列計算後得到車輪的扭矩),如果你真的需要在一些小眾的虛擬環境中自己擼出來整個賽車物理,可以參考gameinstitute 的GI racing教程。在unity中,做好的自行車長下圖這個樣子:

其中腳蹬沒有實際用處,只是我抖個機靈;車身用一個板子簡單的模擬,並且用這個板子來判斷與其他物體的碰撞;而兩個車輪各有一個wheel collider組件。然後,我們還需要一個定義自行車屬性的腳本,如圖中:設定了前輪(圖中是Left wheel)勾選了steering(可以轉向),後輪勾選了驅動(自行車當然是後輪驅動)。

如果你對如何製作自行車物理仍然聽的有點暈,沒關係,unity官網有一個教你五分鐘搭建一個四輪車的教程,而你把四輪車拆掉兩個輪子,餘下兩個輪子挪到中間,就成了自行車。
1.2
人體重心模擬
但是這樣自行車就能不倒嗎?我們人類騎車的話,身體的重心是在不停調整的,與車速,車傾斜角度等配合,達到了車不倒的效果,因此,在這個無人駕駛自行車中,除了車,還需要一個可移動的重心。當然,如果不用和人騎車類似的控制方式,也不是說就騎不了,比如Google在愚人節發布的吹牛逼版自行車,清華學霸發明的真 · 無人駕駛,還有泰國學霸版的,只不過我想玩這種靠重心的哈哈。這個重心的移動應該是靠力矩(就像人的腰部關節的扭矩力,讓人身體各種傾斜),因此我在車上加了一個類似於機械臂的物體,該物體模擬人的傾斜,從而改變人與車整體的重心。機械臂以底端為關節點旋轉,只能在圖中紅線所畫的圓錐範圍內(與垂直軸夾角不超過45度)靠扭矩力移動。機械臂利用了unity的configurable joint(可配置關節)實現,可配置關節比較複雜,我就不講了,反正作用就和mujoco里的機械臂一樣一樣的,靠扭矩力(底端的關節點)移動。

1.3
其他
自行車和模擬人體重心的部分都做好了,剩下的就是車要交互的場景了,如果你看了開頭的影片,裡面展示了兩個環境,一個是直線加速環境,另一個是訓練轉彎的環境。兩個環境中都有一個target(直線環境中為位於終點的黃色物體,彎道環境中為派大星)。而自行車需要保持不倒,並且儘快碰到target。由於彎道環境比直線環境訓練起來更加困難的多得多得多,因此我在彎道環境多次使用了遷移學習,所以gym版環境就不包含彎道環境了(不用遷移,直接訓練彎道環境到達成目標應該是做不到的),只有直線的。
二、定義DRL的輸入與輸出
DRL演算法從神經網路的角度看,還是給輸入,神經網路吐出輸出。輸入就是狀態,也就是環境中你需要了解的東西,吐出來的就是動作,也就是你在這種狀態下該咋辦。在無人駕駛自行車這個小項目中,我設置的狀態如圖:

圖中標號1,代表在世界坐標系下車目前離目標的距離的向量(狀態+3),並且用1500m來歸一化(因為直線賽道我設置了1500米長,注釋里的100m請無視),因為3d環境,所以向量是三維的,也就是已經有三個輸入了。
圖中標號2,代表車子到賽道中心(1500m長的板子的中心位置,750m處)的向量(+3)。需要說明的是,在後面的彎道環境中,並沒有採用這個向量,只有車與目標間的相對位置,角度等。訓練時與絕對坐標點無關,車子才能在任意地方都到達target。
圖中標號3,當前時刻車速,也是向量(+3)。
圖中標號4,當前車的角速度,也是向量(+3),考慮角速度是怕自行車逮蝦戶(漂移),在真的不確定某個狀態需不需要時,我一般都會加上,多加個狀態,結果又不會變多差,我覺得這不是造原型時需要過度考慮的。
圖中標號5,車身的前向在世界坐標中的三維向量(也就是人坐車上臉超前的方向)(+3),該向量與標號1的向量做點積,可以得到兩向量的夾角,這個資訊是很有用的。
圖中標號6,車子自身坐標的上方向,在世界坐標下的向量值(也就是車坐下面那根棍子,方向朝著車座,的單位向量)(+3)
圖中標號7,車頭相對於車身的旋轉角度(左右旋轉我都限制了不超過45度)(+1)。
圖中標號8,當前時刻車子是否碰著地面(+1)
圖中標號9,當前自行車後輪的扭矩力大小(動力,我設置的扭矩不超過80N)(+1)
圖中標號10,11,14共同表示了重心的當前的狀態(+3)。簡單解釋的話,圖中紅色和黃色代表了人在以車身為坐標系下的旋轉,兩個方向都只能左右傾斜45度,也就是每個方向有90度活動空間,0和1說明是歸一化了,因此,圖中正坐在車上時,狀態10和11都是0.5. 而狀態14代表了機械臂的扭矩力的大小(圖中柱子的底端用力)。
圖中標號12表示機械臂的在世界坐標下的旋轉角度(由四元數表示,因此+4)
圖中標號13,表示機械臂當前的角速度(+3)
以上,共31個狀態。我猜看到這,大家疑問最多的問題就是:你幹嘛不用卷積,直接把影像當輸入。(⊙﹏⊙)因為,我這是遊戲引擎,我能直接得到上面這些資訊,我幹嘛還要用影像再去推斷我車子現在傾斜多少,距離目標多遠這些資訊呢,對於車速等資訊,還要將多幀圖片堆疊,還要用RNN來表徵,這使問題更複雜了。在做這個車時,我自己也沒譜能否訓練好,所以自然會去選最容易訓練好的狀態資訊。現在我確信車子能訓練好了,倒是可以用CNN+RNN當輸入狀態試試看(但是我懶?)
輸入說完了,那麼神經網路的輸出動作呢?這就比較直覺了,輸出動作共5個:
1.車頭旋轉角度的增量(即該時刻下車頭應該增加的角度,限制了最大增量一秒100度(-100到+100),才真實嘛)
2.後輪扭矩的增量(一秒最多增加80N),動作3,4,5共同決定了該時刻人的腰部準備往哪個方向用力,腰部用多大的力。動作3代表了圖(7)中紅色扇形中的某個位置(神經網路估計出的),相應的,動作4代表了黃色扇形中的一個位置,3,4共同確定了一個人想在此時到達的目標位置,而動作5表示了為了到該位置,人腰部目前用的力的大小(這可真真實?)
三、獎勵函數的設置
環境搭好了,輸入輸出也都設置好了,那麼就到了比較有意思又見仁見智的獎勵函數設置環節啦。不管是直線加速環境還是拐彎訓練環境,如果都只在碰到終點時給予獎勵,倒了就失敗,那真的是打死它它都訓練不好,這是標準的稀疏獎勵問題。那麼一個比較常用的手段就是reward shaping,也就是除了最終獎勵之外,我們造一些獎勵引導AI達到目標。在直線環境中,我給AI的附加附加獎勵是當車與目標的距離變短了,就給它獎勵,這樣它就會想盡辦法盡量靠近目標。其他獎勵就是,如果掉出板子,就-1,直接結束;如果車身傾斜角度大於45度,就-1,直接結束;如果碰到目標,就+1,結束。
而彎道環境更加複雜,只用reward shaping都不行,我還多次用了課程學習(其實就是遷移學習),如圖:

在彎道環境中,如果一開始就讓目標在車一定半徑內隨機位置出現,訓練自行車,基本上訓練不好,因為獎勵太稀疏了,自行車需要不倒,並控制拐彎,還要拐好角度,最終才能碰到目標。因此我將整個訓練分成了多個階段性任務。首先,目標只會出現在紅色扇形區域,並且給車一定的初速度,這樣車比較容易碰到目標;訓練好之後(可以為訓練了足夠輪數,或者reward達到了目標值),將目標區域擴大為藍色區域,並減小初始車速;訓練好後再逐個變到紫,黑,黃…區域,直至360度,同時半徑也在增大,初始車速在減小。最終,一定半徑內,從0車速出發,360度內,車都能碰到目標。
四、環境訓練
我其實只是自己做著玩的,所以演算法部分就直接用的unity自帶的ppo演算法訓練了。由於動作是連續值,dqn類不適合這個任務,如果你喜歡,可以用其他rl演算法來訓練。我把環境封裝了下(可視化的呦,可以看到自己訓練好後的無人駕駛自行車),你可以從兩種方式中任選一種來自己訓練自行車:
4.1
unity自帶的訓練方式
該方式需要下載github中的BikeScene_train壓縮包。先安裝mlagents(unity的機器學習工具包),安裝方式很簡單,建立一個新的python3.6的虛擬環境,然後pip install mlagents就完了,或者你也可以看官方的安裝教程。在該環境下,讀取環境,獲取狀態和動作的邏輯都在nogym.ipynb里,你可以添加自己的演算法來訓練自行車。官方參考腳本 需要注意的是,在訓練時,請保持ipynb中train_mode為True,表示處於訓練模式,訓練模式下遊戲畫面只有80*80像素,是為了加速訓練而不是為了讓人看;而訓練好後,請設置train_mode為False,表示處於推理階段,推理階段的畫面會自動為1280*720解析度,並且時間也是正常速度,可以把玩自己的自行車。
4.2
gym環境
該方式同樣需要安裝mlagents(建立一個新的python3.6的虛擬環境,然後pip install mlagents就完了,或者你也可以看官方的安裝教程。)。在gym環境下需要同時下載github中的BikeScene_train壓縮包和BikeScene_inferer壓縮包。該環境比較適合熟悉gym的童鞋,參考腳本為github中的gym.ipynb. 至於你想用openai的演算法還是dopamine的演算法還是自己寫的演算法,都可以,可以參考官方示例。在訓練時載入BikeScene_train里的exe(80*80解析度),訓練好後,修改路徑為BikeScene_inferer里的exe(1280*720解析度),就可以看到訓練好的自行車了。部分過程和結果如下:

影片連接:
https://www.bilibili.com/video/av57357339
最後,可以自己玩的自行車:
我還做了個可以玩的環境,就是github(地址見本段末尾)里的BikeScene_playable壓縮包,解壓後,裡面的exe是可以雙擊運行的。按數字鍵0是我訓練了500萬步的AI自己控制的自行車(其實沒訓練好,有時會跌倒);按數字鍵1是玩家自己控制自行車:w為蹬的力最大,A和D控制車頭的左右,上下左右箭頭控制中心(中間那個棍子)的移動;按數字鍵2是固定了車子的Z軸旋轉之後,玩家控制自行車,控制按鍵同模式2. 因為其實人類是玩不轉模式2的,所以我加了模式3ㄟ( ▔, ▔ )ㄏ 這估計是我做過的最辣雞的遊戲了( ̄_ ̄|||) 對了,我忘加退出遊戲的選項了,請點 × 退出?。
github地址:
https://github.com/xunyiljg/DRL_Auto-driving_bicycle
CSDN部落格:
https://blog.csdn.net/qq_38258350