GTAV智能駕駛源碼詳解(一)——製做數據集

項目介紹

場景足夠豐富,操做足夠簡單,有大量的交通工具和駕駛視角可供選擇,遊戲《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模塊會成倍增長單幀處理時間,使得模型的測試效果不美觀)。函數

模型的訓練分爲三個部分:工具

  1. 數據集製做:監督式學習,數據集分爲input data和label,其中輸入數據爲通過灰度處理、區域屏蔽和大小縮放的圖像數據,截取自1280720分辨率的GTA5遊戲畫面,縮放爲16090的大小;標籤數據爲每個圖像樣本對應的1*3規格的操做向量,分別表明向左、向右、前進(Press A/W/D)。在遊戲中人爲駕駛5小時,將每一幀圖片以及其所對應的操做向量記錄在數據集張量中。數據集分5批錄製完成,對不一樣操做所對應的圖片進行數量平衡(W:A:D = 8:1:1)打亂數據集後取1000張圖片做爲測試集,其餘的爲訓練集。
  2. 訓練模型:建立AlexNet初始網絡,對保存下來的數據集進行擬合。總共進行了約240000次權值更新,學習耗時3天左右。
  3. 測試模型,用getkey函數與keycheck函數定義操做向量與按鍵聯繫,用訓練好的AlexNet對捕捉到的圖片進行分類預測,執行當前類別對應的按鍵操做以進行駕駛。

製做數據集


導入依賴庫:學習

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)
  1. 加入倒計時,在執行程序與開始錄製數據集之間留下10秒空餘,用以調整姿態與視角。
  2. 截取大小爲1280*720的遊戲區域,並對其進行縮小、灰化和屏蔽操做。
  3. 用key_check提取當前操做按鍵,並用函數轉化爲標籤向量,與處理過的圖片一塊兒append到數據集中。
  4. 每5000幀保存一次數據集;設置T爲暫停鍵。

平衡數據:

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文件

相關文章
相關標籤/搜索