Airsim是一款基于Unreal Engine構(gòu)建的無(wú)人機(jī)、汽車等模擬器的開源平臺(tái),并且可以跨平臺(tái)的通過(guò)PX4飛行控制器進(jìn)行仿真控制,在物理和視覺上逼真的模擬環(huán)境使得它成為一款很好的平臺(tái)。不僅模擬了汽車無(wú)人機(jī)等動(dòng)力學(xué)模型,甚至對(duì)天氣效果燈光控制也做出了非常好的模擬。并且官方發(fā)布了很多測(cè)試環(huán)境,諸如森林、平原、鄉(xiāng)村等。Airsim公開了API,可以通過(guò)Python等語(yǔ)言與仿真程序中的車輛無(wú)人機(jī)進(jìn)行交互,可以使用這些API進(jìn)行圖像檢索,獲取狀態(tài),控制車輛等。相較于現(xiàn)實(shí)中的自動(dòng)駕駛等實(shí)際應(yīng)用,Airsim提供了更好的對(duì)于數(shù)據(jù)集的收集,相較于一些汽車公司動(dòng)輒幾百PB的數(shù)據(jù)資源,通過(guò)模擬器顯然可以更好的構(gòu)建網(wǎng)絡(luò)學(xué)習(xí)而不用受制于數(shù)據(jù)收集的問題。端到端的深度學(xué)習(xí)是一種建模策咯,是對(duì)深度神經(jīng)網(wǎng)絡(luò)的成功響應(yīng)。隨著近些年硬件的升級(jí)進(jìn)步(如GPU、FPGA等),使我們批量處理大量數(shù)據(jù)成為可能,相較于傳統(tǒng)的ML,端到端深入學(xué)習(xí)更接近人類的學(xué)習(xí)方式,因?yàn)樗试S神經(jīng)網(wǎng)絡(luò)將原始的數(shù)據(jù)直接映射到輸出。本文的目的是將Airsim作為AI研究平臺(tái),收集數(shù)據(jù)進(jìn)行深度學(xué)習(xí),運(yùn)用較少的數(shù)據(jù)集完成模型的建立,進(jìn)而可以在Airsim平臺(tái)上模擬運(yùn)行自動(dòng)駕駛汽車。
#Airsim仿真軟件結(jié)構(gòu)
Airsim結(jié)構(gòu)圖
Airisim中的API接口都保存在AirLib文件下,由以下幾部分組成:
l 物理引擎:旨在快速,可擴(kuò)展的實(shí)現(xiàn)不同車輛
l 傳感器模型:氣壓計(jì),IMU,GPS和磁力計(jì)等
l 車輛模型:用于車輛配置的模型
l 控件庫(kù):提供抽象的API接口,還具有RPC客戶端和服務(wù)器的類
可見,Airsim提供了不同形式不同接口的傳感器,本文將運(yùn)用汽車前方的單目攝像頭對(duì)前方圖像進(jìn)行采樣訓(xùn)練,以得到一個(gè)簡(jiǎn)單的自動(dòng)駕駛模型,設(shè)計(jì)方案如下:
第一部分是在人為操作下,讓車輛隨機(jī)在地圖內(nèi)行走,通過(guò)攝像頭和傳感器記錄圖像和汽車動(dòng)力學(xué)模型;
第二部分是進(jìn)行模型訓(xùn)練,將得到的圖片分為兩個(gè)子集——訓(xùn)練集和驗(yàn)證集,將在訓(xùn)練集訓(xùn)練好的模型放在驗(yàn)證集進(jìn)行測(cè)試,如果準(zhǔn)確率高于一定閾值,則模型擬合程度較好,如果準(zhǔn)確率較低,則重復(fù)一二部分;
第三部分是將訓(xùn)練好的模型連接到Airsim軟件,通過(guò)學(xué)習(xí)出來(lái)的結(jié)果控制車輛前進(jìn)轉(zhuǎn)彎,檢測(cè)和分析仿真結(jié)果。
#數(shù)據(jù)收集
在AirsimNeibourhood中運(yùn)行汽車,記錄汽車運(yùn)行時(shí)前方'0’號(hào)攝像頭記錄的圖片和汽車姿態(tài)速度轉(zhuǎn)角等自身信息,分別存入/images文件夾和airsim_rec.txt文件。并獲得有關(guān)圖像相對(duì)應(yīng)的汽車各個(gè)參數(shù)標(biāo)簽:
通過(guò)數(shù)據(jù)收集,共得到294張圖片及標(biāo)簽,我們隨機(jī)將34張圖片取出作為驗(yàn)證集,將其余260張圖片打上標(biāo)簽作為數(shù)據(jù)喂入。我們將標(biāo)簽設(shè)定為一個(gè)三分類問題,即steering=0/0.5/-0.5,分別對(duì)應(yīng)直行左轉(zhuǎn)和右轉(zhuǎn)。在測(cè)試的時(shí)候,我們始終控制汽車的行進(jìn)速度為5m/s,因此需要控制的變量只有轉(zhuǎn)角一個(gè)。下面是收集到數(shù)據(jù)的形式:
l 隨機(jī)改變圖像的亮暗程度、對(duì)比度以及顏色等
基于Airsim軟件優(yōu)秀的仿真能力,可以在不同天氣不同光照條件進(jìn)行仿真,因此對(duì)圖片進(jìn)行亮暗程度、對(duì)比度、以及顏色等處理顯得尤為重要。通過(guò)對(duì)照片的隨機(jī)處理可以提高模型的適應(yīng)能力,比如不光在訓(xùn)練集場(chǎng)景上可以良好運(yùn)行,也可以在其他測(cè)試集上有較好表現(xiàn)。
l 隨機(jī)反轉(zhuǎn)
可以發(fā)現(xiàn)在該數(shù)據(jù)集中照片具有高度對(duì)稱性,具有垂直翻轉(zhuǎn)容差,因此我們可以將圖片按照中心Y軸翻轉(zhuǎn),同時(shí)對(duì)角度的標(biāo)志取反,以生成新的數(shù)據(jù)集,以提高模型對(duì)轉(zhuǎn)彎操作的識(shí)別能力。
l 在處理中選取ROI區(qū)域——即我們感興趣的一小部分區(qū)域
我們拋去了原本照片中位于上方的像素點(diǎn),由于偏上的圖像的一般構(gòu)成為樹木房子天空,對(duì)我們需要的轉(zhuǎn)向關(guān)系不大,因此合理將它們丟棄,提高模型訓(xùn)練速度,并且提高了模型的泛化能力和準(zhǔn)確性。
可見我們將原圖像256144的像素裁剪出ROI為25664的像素。并隨機(jī)做填充、裁剪、縮放、翻轉(zhuǎn)處理,可以得到:
基于上述介紹的卷積網(wǎng)絡(luò)結(jié)構(gòu),本文將采用卷積&最大池化層的標(biāo)注組合來(lái)處理圖像。然后將每張圖像所對(duì)應(yīng)的車輛模型信息喂入網(wǎng)絡(luò),進(jìn)行參數(shù)模型的建立。
VGG是當(dāng)前流行的CNN模型之一,在2014年由Simonyan和Zisserman提出。VGG模型通過(guò)一系列
的卷積核和池化層取得了較好的效果,總共由13層卷積和3層全連接層,在每層VGG中使用ReLU激活函數(shù),在全連接層之后添加dropout來(lái)抑制過(guò)擬合現(xiàn)象。當(dāng)采用兩層
卷積核時(shí),其等效是得到了感受野為
的特征圖,而這樣做比
需要更少的參數(shù),加快了模型的計(jì)算訓(xùn)練速度。并且由于卷積核的尺寸比較小,可以堆疊更多的卷積層,加深網(wǎng)絡(luò)深度,提取更多特征。VGG結(jié)構(gòu)如下圖所示:
model = Sequential() model.add(Conv2D(64, (3, 3),activation='relu', strides=(1,1), padding = 'same', use_bias = True, data_format='channels_first', input_shape = (3, 256,64))) model.add(Conv2D(64, (3, 3), activation='relu', strides=(1,1), padding = 'same', use_bias = True)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.5)) model.add(Conv2D(128, (3, 3),activation='relu', strides=(1,1), padding = 'same', use_bias = True)) model.add(Conv2D(128, (3, 3), activation='relu', strides=(1,1), padding = 'same', use_bias = True)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.5)) model.add(Flatten()) model.add(Dense(512, use_bias=True, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(512, use_bias=True, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(3, use_bias=True, activation='softmax')) adam = keras.optimizers.Adam(lr=0.0000005, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False) #編譯模型 model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) #creat a history history = LossHistory() model.summary()
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 64, 256, 64) 1792
conv2d_2 (Conv2D) (None, 64, 256, 64) 36928
max_pooling2d_1 (MaxPooling2 (None, 64, 128,
32) 0
dropout_1 (Dropout) (None, 64, 128, 32) 0
conv2d_3 (Conv2D) (None, 128, 128, 32) 73856
conv2d_4 (Conv2D) (None, 128, 128, 32) 147584
max_pooling2d_2 (MaxPooling2 (None, 128, 64,
16) 0
dropout_2 (Dropout) (None, 128, 64, 16) 0
conv2d_5 (Conv2D) (None, 256, 64, 16) 295168
conv2d_6 (Conv2D) (None, 256, 64, 16) 590080
conv2d_7 (Conv2D) (None, 256, 64, 16) 65792
max_pooling2d_3 (MaxPooling2 (None, 256, 32,
8) 0
dropout_3 (Dropout) (None, 256, 32, 8) 0
flatten_1 (Flatten) (None, 65536) 0
dense_1 (Dense) (None, 512) 33554944
dropout_4 (Dropout) (None, 512) 0
dense_2 (Dense) (None, 512) 262656
dropout_5 (Dropout) (None, 512) 0
dense_3 (Dense) (None, 3) 1539
=================================================================
Total params: 35,030,339
Trainable params: 35,030,339
Non-trainable params: 0
最后輸出的是一個(gè)三分類問題,因此采取softmax函數(shù)進(jìn)行處理??梢娫谠摼矸e神經(jīng)網(wǎng)絡(luò)中有足夠多的參數(shù)對(duì)圖片進(jìn)行處理,進(jìn)而訓(xùn)練出有效的模型。訓(xùn)練過(guò)程分為10個(gè)epochs,每個(gè)epoch中batchsize=10,總共訓(xùn)練260個(gè)batches,訓(xùn)練過(guò)程中的loss和acc如圖所示:
class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs={}): self.losses = {'batch':[], 'epoch':[]} self.accuracy = {'batch':[], 'epoch':[]} self.val_loss = {'batch':[], 'epoch':[]} self.val_acc = {'batch':[], 'epoch':[]} def on_batch_end(self, batch, logs={}): self.losses['batch'].append(logs.get('loss')) self.accuracy['batch'].append(logs.get('acc')) self.val_loss['batch'].append(logs.get('val_loss')) self.val_acc['batch'].append(logs.get('val_acc')) def on_epoch_end(self, batch, logs={}): self.losses['epoch'].append(logs.get('loss')) self.accuracy['epoch'].append(logs.get('acc')) self.val_loss['epoch'].append(logs.get('val_loss')) self.val_acc['epoch'].append(logs.get('val_acc')) def loss_plot(self, loss_type): iters = range(len(self.losses[loss_type])) plt.figure() # acc plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc') # loss plt.plot(iters, self.losses[loss_type], 'g', label='train loss') if loss_type == 'epoch': # val_acc plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc') # val_loss plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss') plt.grid(True) plt.xlabel(loss_type) plt.ylabel('acc-loss') plt.legend(loc='upper right') plt.show()
#Airsim上平臺(tái)的訓(xùn)練和測(cè)試
將訓(xùn)練好的模型在Airsim仿真平臺(tái)上運(yùn)行,得到的結(jié)果如下:
如圖可見,在Airsim引擎上表現(xiàn)達(dá)到預(yù)期,汽車可以在轉(zhuǎn)彎處識(shí)別并進(jìn)行轉(zhuǎn)向,同時(shí),在筆直路的行進(jìn)前遇到障礙物也可以自行避開,并迅速調(diào)整運(yùn)動(dòng)姿態(tài)回到原來(lái)的運(yùn)行狀態(tài)。不過(guò)仍有一些問題出現(xiàn),在筆直路面行駛過(guò)程中汽車模型的左右抖動(dòng)比較大。在實(shí)際應(yīng)用中,路面上不可能存在單一的汽車,如果多臺(tái)汽車并行駕駛的話很有可能發(fā)生碰撞。因此要改變?cè)谄椒€(wěn)路線上的穩(wěn)定性,也是接下來(lái)研究側(cè)重的方向。
由此,通過(guò)深度學(xué)習(xí)平臺(tái)搭建網(wǎng)絡(luò),在Airsim仿真軟件中可以實(shí)現(xiàn)一種單一攝像頭進(jìn)行避障的模型。由于PC設(shè)備的原因,我們無(wú)法實(shí)現(xiàn)對(duì)幾萬(wàn)張數(shù)據(jù)集和多分類輸出方式的訓(xùn)練,但是本文設(shè)計(jì)出來(lái)模型驗(yàn)證結(jié)果可用,則提供了一種接下去深入學(xué)習(xí)的可能。
聯(lián)系客服