本博客同時發佈於個人主頁:www.doctorsrn.cn
內容說明
主要介紹一篇使用深度學習方法去實現Motion Planning的論文,並將作者開源的實現代碼在本地進行復現,復現過程中涉及到Docker的使用,Colaboratory和Kaggle Kernel環境的使用等內容。主要內容包括兩部分:
- 介紹論文Motion Planning Networks的主要內容
- 在本地Docker pytorch環境中利用作者的開源代碼和提供的Simple 2D情況的數據集,訓練模型,並測試模型在2維情況下Motion Planning的效果,此外該部分還包括如下內容:
- Docker容器中GUI程序的運行
- 在Google Colaboratory環境中訓練該模型
- 在Kaggle Kernel環境中訓練該模型
測試主機的環境是:
- 系統:Ubuntu16.04
- 顯卡:1060 6G
Paper: Motion Planning Networks(arxiv,github)
摘要
隨着運動規劃維度的增加,RRT*、A*、D*等算法效率都將降低,本文提出了一種不論障礙物形狀,端對端生成無碰撞路徑的網絡–Motion Planning Networks,簡稱MPNet
MPNet包含如下結構:
- 一個Contractive Autoencoder(收縮式自動編碼器,CAE),對工作空間進行編碼
- 一個前饋神經網絡,該神經網絡使用編碼的工作空間、起始和目標點配置,端對端生成可行的運動軌跡,供機器人進行跟蹤
作者測試了質點、剛體、7自由度機械臂在2D和3D環境下的規劃問題,結果表明MPNet效率很高,且泛化能力很強。MPNet的運算時間始終小於1秒,明顯低於現有的最先進的運動規劃算法。此外,在一個場景訓練的MPNet,可以藉助少量數據通過遷移學習,快速適應新場景。
簡介
機器人運動規劃意義重大,應用廣泛。開發了很多計算效率高的基於採樣的規劃算法–RRT,RRT*,P-RRT*。
本文提出了一種基於深度神經網絡(DNN)的迭代運動規劃算法–MPNet。
MPNet由兩部分構成:
- 障礙物空間編碼器----Contractive Autoencoder:將障礙物點雲編碼至低維隱空間(latent space)
- 路徑規劃器----前饋神經網絡:在給定t時刻機器人的配置、目標點配置和障礙物編碼後的隱空間時,預測t+1時刻機器人的配置
經過訓練後,MPNet可以與新的雙向迭代算法結合使用,來生成可行的軌跡。
神經網絡在Motion Planning相關工作
- Hopfield網絡
- Deep Reinforcement Learning
- Lightning Framework
問題定義
設表示長度爲的的有序表,序列表示從到中第個元素的映射。並且論文中和分別表示集合的最後一個元元素和集合中元素的個數。
設表示給定的狀態空間,是狀態空間的維數,且,障礙物空間和非障礙物空間分別定義爲, 。設初始狀態爲,目標區域爲
設有序表表示一條非負和非零長度的路徑。如果路徑連接起和,則該路徑是可行路徑,即,且都位於非障礙物空間中。
基於上述定義,運動規劃問題描述爲:給定三元組,出事狀態點和目標區域,找到一條可行路徑滿足, 。
MPNet
MPNet是一個基於神經網絡的運動規劃器,由兩個階段組成。第一階段對應於神經模型的離線訓練。 第二個對應於在線路徑生成。
離線訓練
兩個神經網絡模型:
- Contractive AutoEncoder (CAE)
- Deep Multi-Layer Perceptron(DMLP)深度多層感知器
- Contractive AutoEncoder (CAE)
將障礙物空間編碼至特徵空間,編碼函數,解碼函數,編碼器目標函數 - Deep Multi-Layer Perceptron(DMLP)
給定,預測下一時刻狀態
使用RRT*算法在不同環境下生成的可行路徑數據來訓練DMLP,路徑的形式是元組。
目標函數是誤差均方差函數(MSE)
在線路徑規劃
提出了一種啓發式增量雙向路徑生成算法,生成連接起始狀態和目標狀態的端到端可行路徑,路徑生成算法:
- Obstacles Encoder
離線階段訓練的CAE - DMPL
離線階段訓練的DMPL - Lazy States Contraction (LSC)
冗餘點收縮。在給定路徑時,對路徑進行優化,將未連接但可以直接相連的路徑點進行連接。 - Steering
steerTo函數在給定兩個狀態點作爲輸入下,檢查由兩個狀態點連接成的路徑是否位於無障礙物空間。障礙物檢測的路徑點可以寫成:. - isFeasible
給定路徑,判斷該路徑是否可行 - Neural Planner
一種基於DMLP的啓發式雙向增量路徑生成方法,即圖中的算法2 - Replanning
圖中的算法3.給定路徑,若檢查出該路徑中存在連續兩點是不可連接的,使用下面的方法重新對這兩點進行規劃:- Neural Replanning
- Hybrid Replanning
實現細節
-
數據集的生成
將一些四邊形方塊作爲障礙物隨機放置在40x40或者40x40x40的區域中成爲工作空間,分別用來生成2維或3維的數據集。障礙物放置位置不同,就會生成不同的工作空間。
起始點和終止點的生成: 在工作空間的無障礙物空間中隨機採樣個()狀態點組成一個表,再從該表中隨機取出一對狀態點組成起始點和終止點對。
路徑生成: 利用RRT*算法生成起始點和終止點之間的可行路徑,用於訓練和測試。具體細節:針對s2D、c2D、c3D情況,均生成110個不同的工作空間,在每個工作空間中再使用RRT*算法生成5000個無障礙物路徑。
訓練集:取100個工作空間,將每個空間中4000個路徑作爲訓練集。
測試集:有兩種:第一種,取訓練集100個工作空間,將每個空間中未用於訓練的200個路徑作爲測試集;第二種,取10個未用於訓練的工作空間,將每個工作空間中2000個未用於訓練的路徑作爲測試集。
對於Baxter的訓練,使用簡單環境,不包含障礙物,即不需要障礙物編碼,直接使用50000個可行路徑訓練DMLP模型。 -
模型結構
-
Contractive AutoEncoder (CAE)結構:編解碼函數均由三個線性層和一個輸出層構成,線性層激活函數爲PReLU,解碼單元和編碼單元的結構剛好相反,下面主要介紹編碼單元結構:
- 輸入:大小的點雲向量,1400是指每個維度的點數,是指工作空間的維度。
- 對於2D工作空間,輸入爲1400x2維,三個線性層分別包含512、256、128個隱神經元,輸出爲28維。即編碼後的障礙物表示爲
- 對於3D工作空間,輸入爲1400x3維,三個線性層分別包含786、512、256個隱神經元,輸出爲60維。即編碼後的障礙物表示爲
- 2D情況CAE模型構建代碼:
self.encoder = nn.Sequential(nn.Linear(2800, 512),nn.PReLU(),nn.Linear(512, 256),nn.PReLU(),nn.Linear(256, 128),nn.PReLU(),nn.Linear(128, 28))
-
Deep Multi-layer Perceptron (DMLP)結構:是一個12層的深度神經網絡。
- 輸入: 質點或者剛體情況,輸入值爲編碼後的障礙物、起始點和終止點配置向量三者的拼接。2D質點、3D質點和剛體的配置維度分別爲2、3、3,對於Baxter機器人沒考慮障礙物,所以輸入只包含起始點和終止點配置向量的拼接,Baxter的配置空間維度爲7。
- 中間層:前9層每一層均爲線性層,激活函數爲PReLU,使用Dropout,每一層神經元個數分別爲:1280, 1024, 896, 768, 512, 384, 256, 256, 128;第10層和第11層不使用Dropout,神經元個數分別爲64和32;第12層,即輸出層,其輸出的維度是配置空間的維度,比如2D質點配置空間的維度爲2。
- 2D情況DMLP模型構建代碼:
self.fc = nn.Sequential( nn.Linear(input_size, 1280),nn.PReLU(),nn.Dropout(), nn.Linear(1280, 1024),nn.PReLU(),nn.Dropout(), nn.Linear(1024, 896),nn.PReLU(),nn.Dropout(), nn.Linear(896, 768),nn.PReLU(),nn.Dropout(), nn.Linear(768, 512),nn.PReLU(),nn.Dropout(), nn.Linear(512, 384),nn.PReLU(),nn.Dropout(), nn.Linear(384, 256),nn.PReLU(), nn.Dropout(), nn.Linear(256, 256),nn.PReLU(), nn.Dropout(), nn.Linear(256, 128),nn.PReLU(), nn.Dropout(), nn.Linear(128, 64),nn.PReLU(), nn.Dropout(), nn.Linear(64, 32),nn.PReLU(), nn.Linear(32, output_size))
其中input_size和output_size值分別爲32,2。
- 超參數的設置
使用Adagrad優化器,學習率0.1,Dropout參數爲0.5,動量項參數爲0.001。用於訓練的CAE模型的工作空間有30000個。
實驗結果
MPNet計算時間的平均值小於1s,與Informed-RRT*和BIT*算法相比,MPNet速度分別是它們的40和20倍。
討論
-
Dropout導致的隨機性
對於DL,在測試階段或者在線執行階段,通常做法是關閉Dropout。但是Dropout對路徑在線生成s是有利的,因爲Dropout顯著提升了本模型的性能。
出現上述情況的原因分析:DMLP在重規劃階段,由Dropout引入的隨機性使DMPL可以生成與之前不同的路徑,下圖就說明了這種特徵:
同樣的起點和終點,DMLP生成了多條不同的路徑,這種能力有助於路徑重規劃的成功,因此添加Dropout對MPNet的性能是有利的。 -
遷移學習
-
完備性
因爲MPNet先使用DMPL進行規劃得到粗略的路徑,若該路徑不可行則調用重規劃方法。所以MPNet的完備性取決於重規劃方法是否完備,而重規劃方法採用了混合經典規劃方法,所以進一步可得MPNet的完備性取決於所採用的經典方法的完備性。如果經典方法採用A*算法,則MPNet是完備的;因爲本文采用的是RRT*算法,所以MPNet是概率完備的。 -
計算複雜度
。。。
結論和未來工作
未來工作:
- 將MPNet應用於動態環境,因爲其有良好的泛化能力
- 增加對工作空間編碼的神經注意(neural attention)
代碼實現和復現
論文作者開源的代碼可在github上下載,開源代碼使用python2和pytorch實現,沒有提供訓練好的模型。
搭建本地開發環境
開源代碼使用pytorch實現,使用Docker快速搭建pytorch環境,當然也可以通過其他方式安裝pytorch環境。
Docker的使用,包括安裝、gui的支持等可以參考另一篇博客:Moveit Docker的安裝與配置過程
使用Docker建立pytorch環境,步驟如下:
- 拉取pytorch官方Docker鏡像:
docker pull pytorch/pytorch:latest
pytorch鏡像略大,需要等一會才能下載完,下載結束後通過docker image ls
可以看到下載完成的鏡像 - 創建pytorch鏡像的Docker容器:
建議新建bash文件,如run.bash,將以上命令存入bash文件中,並給定執行權限xhost +local:root nvidia-docker run -it \ -v /home/srn/SRn/MPNet:/workspace \ --env="DISPLAY" \ --env="QT_X11_NO_MITSHM=1" \ --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \ pytorch/pytorch:latest bash
chmod +x run.bash
,然後運行./run.bash
。
注意: 以上命令將本地MPNet代碼目錄掛載到Docker容器中,根據自己代碼位置需要修改MPNet代碼目錄。 - 進入容器後安裝matplotlib庫:
apt-get updata pip install matplotlib -i https://pypi.douban.com/simple apt-get install python3-tk
對開源代碼進行一些修改
本次主要測試MPNet部分代碼,對訓練數據集生成部分的代碼之後在測試。由於MPNet代碼使用python2編寫,docker建立的環境是python3,所以要做一些修改,主要有下面的一些修改:
- 修改print函數: 使用python的
2to3
函數進行修改:2to3 -w python_file
- 修改整除符號
/
爲//
- 修改包含zip函數的部分,比如data_loader.py中的
data=zip(dataset,targets)
,修改爲data=list(zip(dataset,targets))
以上修改主要是因爲python2和python3之間的差異導致的。
此外要註釋掉某些python文件中的import nltk
語句,這條語句導入的庫並沒有用上,docker容器中也沒有安裝這個庫,所以註釋掉。
訓練模型
MPNet主要有兩部分構成:
- Contractive AutoEncoder (CAE)
對應CAE.py - Deep Multi-Layer Perceptron(DMLP)
訓練函數爲train.py
所以要分別訓練兩個模型。主要針對2維情況下MPNet的工作進行測試,訓練用的數據集使用論文作者分享的simple2D數據集,當然你也可以通過數據集生成代碼自己去生成訓練集,包括3維空間的規劃,這是之後的工作。
將simple2D數據集下載到本地,並解壓。這是本機的文件結構圖:
目錄中除了原始文件,其他文件有些是2to3
生成的,有些是訓練生成的模型文件。其中data/dataset
目錄是解壓數據集後的目錄。
訓練CAE
按照上面的數據集修改MPNet/AE/data_loader.py
中的數據路徑,也可以修改CAE.py
中的訓練參數,然後開始訓練CAE模型:
python MPNET/AE/CAE.py
這一步訓練耗時較短,大概15分鐘。
訓練DMLP模型
修改MPNet/data_loader.py
中的數據路徑和上一步生成的CAE模型數據的路徑,也可以修改train.py
中的訓練參數,然後開始訓練DMLP模型:
python MPNET/train.py
這一步耗時較久,訓練300個epoch在本機需要13個小時。在300個epoch後,手動終止了訓練。
測試MPNet的效果
完成上面兩步訓練後就可以測試MPNet的效果了,修改neuralplanner.py
中模型的路徑參數。由於原代碼中沒有可視化函數,自己寫了一個2維情況的可視化函數,主要是繪製出障礙物點雲、RRT*生成的路徑和MPNet生成的路徑。可視化函數內容爲:
def visualize_function(i, j, g_path, a_path, a_path_length):
'''
i: i-th obstacle
j: j-th path generated by rrt*
g_path: path generated by mpnet
a_path_length: actual path length
'''
dataPath = '/workspace/data'
#繪製障礙物點雲圖
temp=np.fromfile(dataPath+'/dataset/obs_cloud/obc'+str(i)+'.dat') #len=2800
ob = temp.reshape(len(temp)//2,2)
#plt.plot([x[0] for x in ob], [x[1] for x in ob])
plt.scatter([x[0] for x in ob], [x[1] for x in ob])
#繪製生成的路徑
plt.plot([p[0] for p in g_path], [p[1] for p in g_path],color='red',linewidth=2.5,linestyle='-')
#繪製實際路徑
plt.plot([a_path[a][0] for a in range(a_path_length)],
[a_path[a][1] for a in range(a_path_length)])
plt.show()
下面是測試顯示的效果圖:
圖中藍色塊是障礙物點雲,藍色路徑是原始RRT*算法生成的路勁,紅色路徑是MPNet生成路勁,可以看到效果還不錯,更多數據之後再測試。
以上修改後的代碼可以參看上傳到github的代碼。
調試遇到的bug彙總
- mpnet的python代碼使用Tab縮進,如果自己修改代碼使用空格縮進,混用空格和Tab縮進會導致python代碼運行出現如下錯誤:
IndentationError: unindent does not match any outer indentation level
解決辦法: 統一使用空格縮進,在vscode中使用ctrl+shift+p調出命令窗口,輸入關鍵字“space”,找到“Convert Indentation to Spaces”功能,使用該功能就可以將當前python代碼中的Tab縮進轉換爲空格縮進。
- Docker使用pytorch官方提供的鏡像,在安裝matplotlib時(
pip install matplotlib -i https://pypi.douban.com/simple
),出現如下錯誤:
import _tkinter if this fails your python may not be configured for tk
ImportError: libX11.so.6: cannot open shared object file: No such file or directory
大概原因還是docker中gui顯示的問題,google之後使用下面命令解決:
apt-get install python3-tk
重要參考網站:
https://stackoverflow.com/questions/5459444/tkinter-python-may-not-be-configured-for-tk
https://stackoverflow.com/questions/25281992/alternatives-to-ssh-x11-forwarding-for-docker-containers
https://linuxmeerkat.wordpress.com/2014/10/17/running-a-gui-application-in-a-docker-container/