場景足夠豐富,操做足夠簡單,有大量的交通工具和駕駛視角可供選擇,遊戲《Grand Theft Auto 5》是一個相對廉價且適合初級人工智能探索的自動駕駛試驗場。數組
本AI(暫且稱之爲ScooterV2)借鑑了美國死宅Harrison Kinsley的Charles方案,只使用截圖捕捉的畫面以模擬攝像頭數據做爲AI的輸入,並無真實的智能駕駛所涉及的傳感器與雷達數據。AI的決策過程目前只停留在CNN(AlexNet)對單張圖片進行分類選擇操做的階段,還沒有引入記憶,沒法處理時間序列數據,於是相比於引入循環神經網絡,目前的ScooterV2任然須要大量的數據進行fit訓練(目前已完成的ScooterV3採用了強化學習,不須要任何訓練數據集,可是因爲駕駛場景過於複雜,還沒有設計出完美的獎勵機制,雖然下降了訓練成本可是效果不如目前的V2版本)。網絡
但因爲機能限制和存儲能力限制(實際上是由於不想花太多訓練時間,以及常常改方案、丟數據、丟模型),ScooterV2相對於Charles作了一些簡化:app
Charles的駕駛載體爲民用車(GTA5搶劫神車Kuroma裝甲轎車),視角爲引擎蓋視角(爲了模擬真實的攝像頭),設計目標爲保證在當前道路上保持車道行駛的同時儘可能避開障礙物(因爲Kuroma裝甲車速度很快,避開障礙物主要以變道的形式完成),且仿製出了許多真實的智能駕駛模塊(前碰撞預警、障礙物探測、行人檢測)。dom
我作的ScooterV2駛載體爲摩托車(DoubleT),視角爲第三人稱視角(爲了看到更大的場景區域,爲了捕捉到的車道線斜率範圍更大,指望以更小的樣本量在更少的時間訓練出足夠好的效果),設計目標爲在當前道路上保持車道行駛(使用Mod屏蔽了全部交通和行人),且沒有設計其餘模塊(因爲機能限制,串聯其它CNN模塊會成倍增長單幀處理時間,使得模型的測試效果不美觀)。函數
模型的訓練分爲三個部分:工具
導入依賴庫:學習
import numpy as np import cv2 import time from grabscreen import grab_screen from getkeys import key_check import os
cv2進行圖像處理;time用來記錄單幀的處理時間;grab_screen從現有的py文件中導入,做用是截取屏幕上的必定區域;key_check用來處理當前幀的操做按鍵,將其轉化爲向量。測試
定義屏蔽函數:人工智能
vertices = np.array([[1,60],[1,89],[159,89],[159,60],[80,35],], np.int32) def roi(img, vertices): mask = np.zeros_like(img) cv2.fillPoly(mask, vertices, 255) masked = cv2.bitwise_and(img, mask) return masked
爲了減小圖像無用區域對訓練過程的影響,須要將圖像上方天空區域以及兩側街景進行塗黑屏蔽。
vertices定義了一個區域,roi函數將此區域外的像素用255灰度塗黑。設計
將操做轉化爲標籤向量:
def keys_to_output(keys): output = [0,0,0] if 'A' in keys: output[0] = 1 elif 'D' in keys: output[2] = 1 else: output[1] = 1 return output
若按鍵A,則標籤向量爲[1,0,0];
若按鍵d,則標籤向量爲[0,0,1];
不然,則標籤向量爲[0,1,0];
數據集錄制:
file_name = 'training_data_X.npy' if os.path.isfile(file_name): print('File exists, loading previous data!') training_data = list(np.load(file_name)) else: print('File does not exist, starting fresh!') training_data = []
初始化空數據集,其中'training_data_X.npy'中的X應用數字表示當前錄製批次。
def collect(): for i in list(range(10))[::-1]: print(i+1) time.sleep(1) last_time = time.time() paused = False while True: if not paused: screenshot = grab_screen(region=(0,32,1280,752)) print('Frame took {} seconds'.format(time.time()-last_time)) last_time = time.time() screen = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) screen = cv2.resize(screen, (160,90)) screen = roi(screen, [vertices]) keys = key_check() output = keys_to_output(keys) training_data.append([screen,output]) cv2.imshow('window2',screen) if cv2.waitKey(25) & 0xFF == ord('q'): cv2.destroyAllWindows() break if len(training_data) % 5000 == 0: print(len(training_data)) np.save(file_name,training_data) keys = key_check() if 'T' in keys: if paused: paused = False print('unpaused!') time.sleep(1) else: print('Pausing!') paused = True time.sleep(1)
平衡數據:
import numpy as np import pandas as pd from collections import Counter from numpy.random import shuffle train_1 = np.load('training_data_1.npy') print('done1') train_2 = np.load('training_data_2.npy') print('done2') train_3 = np.load('training_data_3.npy') print('done3') train_4 = np.load('training_data_4.npy') print('done4') train_5 = np.load('training_data_5.npy') print('done5') train = np.concatenate([train_1,train_2,train_3,train_4,train_5]) lefts = [] rights = [] forwards = [] shuffle(train) for data in train: img = data[0] choice = data[1] if choice == [1,0,0]: lefts.append([img,choice]) elif choice == [0,1,0]: forwards.append([img,choice]) elif choice == [0,0,1]: rights.append([img,choice]) else: print('no matches') forwards = forwards[:8*len(lefts)][:8*len(rights)] lefts = lefts[:len(forwards)] rights = rights[:len(forwards)] final_data = forwards + lefts + rights shuffle(final_data) np.save('training_data_after_shuffle.npy', final_data)
將5批數據集合並在一塊兒後進行隨機排序,並依據不一樣的標籤劃分爲3個數組,按必定的比例進行截取後合併再進行隨機排序,最後保存爲training_data_after_shuffle.npy文件