-
我們將學(xué)習(xí)基本但關(guān)鍵的知識(shí),構(gòu)建你的自動(dòng)駕駛汽車(chē)(虛擬顯示)。
-
通過(guò)60行Python構(gòu)建自動(dòng)駕駛汽車(chē),了解運(yùn)動(dòng)、軌跡檢測(cè)、方向檢測(cè)的基本原理,并且一切都是免費(fèi)的。
-
你會(huì)變得更聰明,就像埃隆·馬斯克(Elon Musk)那樣(你的朋友會(huì)嫉妒羨慕你的)。
必要條件
你不需要真正關(guān)注跟蹤馬斯克。但有兩個(gè)簡(jiǎn)單的必要條件
-
在您的系統(tǒng)中安裝最新版本的Python。
-
選擇一個(gè)你熟悉的代碼編輯器(PyCharm或VS code,我認(rèn)為都很酷)。
自動(dòng)駕駛汽車(chē)的核心特征
-
首先,它可以自動(dòng)駕駛,而無(wú)需任何人為干預(yù)。
-
其次,如果道路上有任何障礙物,道路盡頭或停車(chē)信號(hào),它可以停止行駛。
-
第三,它可以檢測(cè)道路并進(jìn)行相應(yīng)地轉(zhuǎn)彎。
-
第四,可以自己到達(dá)目的地。
了解這四個(gè)核心功能后,您就可以完成這個(gè)項(xiàng)目。
讓我們跳過(guò)無(wú)聊的部分
我們需要?jiǎng)?chuàng)建一個(gè)有道路、汽車(chē)、轉(zhuǎn)彎和障礙的項(xiàng)目。這幾乎就像設(shè)計(jì)一款游戲。
Python提供了一個(gè)名為pygame的第三方模塊,它是一組用于編寫(xiě)電子游戲的Python模塊。這允許你用Python語(yǔ)言創(chuàng)建功能齊全的游戲和多媒體程序。
因?yàn)樗堑谌侥K,所以在使用它之前,我們需要先安裝它。打開(kāi)終端,輸入以下命令安裝pygame:
linuxmi@linuxmi:~/www.linuxmi.com$ pip3 install pygame
安裝好之后,我們可以通過(guò)簡(jiǎn)單地將該模塊導(dǎo)入到我們的項(xiàng)目中來(lái)訪問(wèn)該模塊提供的各種功能:
import pygame
讓我們從有趣的代碼開(kāi)始
我們將通過(guò)五個(gè)簡(jiǎn)單的步驟來(lái)完成此項(xiàng)目。
步驟1、啟動(dòng)pygame
獲取相關(guān)資源。
資源包含汽車(chē)將在其上行駛的六種不同類(lèi)型的賽道。 第六部分有些困難,但是使用它會(huì)很有趣。
第一條路很簡(jiǎn)單:我們要走一條直線路,我們必須在那兒開(kāi)車(chē)。
從第一個(gè)賽道開(kāi)始,我們需要做三件事:
-
初始化pygame模塊
-
創(chuàng)建所需大小的窗口
-
加載軌道圖像并將其添加到窗口
我們將從導(dǎo)入pygame模塊開(kāi)始:
import pygame
在使用模塊之前先初始化它:
pygame.init():
接下來(lái),我們使用display() 方法創(chuàng)建基本的pygame窗口。 display方法將簡(jiǎn)單地創(chuàng)建一個(gè)空窗口,我們可以在其中放置元素。 但是我們需要定義我們需要?jiǎng)?chuàng)建的窗口的大小。 我們可以使用set_mode() 方法做到這一點(diǎn)。
在set_mode() 方法中,我們將窗口的大小(以像素為單位)傳遞為表示該窗口的XY坐標(biāo)的Python元組。 Python元組是用括號(hào)括起來(lái)的有效Python數(shù)據(jù)類(lèi)型的逗號(hào)分隔元素的集合,例如:() 。
有了窗口后,就可以使用pygame提供的圖像模塊加載圖像并創(chuàng)建圖像表面對(duì)象。 圖像表面在字節(jié)緩沖區(qū)內(nèi)共享數(shù)據(jù)。 沒(méi)有圖像類(lèi)。 圖像作為表面對(duì)象加載:
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")
圖像模塊是pygame的必需依賴項(xiàng),它提供了load()方法,該方法通過(guò)創(chuàng)建圖像表面對(duì)象來(lái)加載傳遞的圖像。
方法blit() — blit表示塊圖像傳輸 - 將一個(gè)表面的內(nèi)容復(fù)制到另一表面。 我們需要傳遞表面圖像對(duì)象以及我們要將圖像放置在窗口中的位置。 最后,我們可以使用update()方法更新空白窗口。
上面的代碼產(chǎn)生以下輸出:
現(xiàn)在我們有了軌道,讓我們?cè)賮?lái)添加汽車(chē)。我們將遵循與track相同的方法:加載圖像并使用blit()將其傳送到窗口:
import pygame
pygame.init()
pygame.display.set_caption('www.linuxmi.com 游戲之旅')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")
while True:
window.blit(track, (0, 0))
pygame.display.update()
添加汽車(chē)圖像時(shí),我們必須調(diào)整其大小以滿足我們的要求,因?yàn)槟J(rèn)圖像尺寸可能不適合。 我們可以使用轉(zhuǎn)換模塊的scale()方法來(lái)調(diào)整圖像的大小,并將圖像對(duì)象和新的大小作為元組作為參數(shù)傳遞。
運(yùn)行上面的代碼,輸出如下:
步驟1完成。在第二步,我們將駕駛汽車(chē)在賽道上,并在需要時(shí)停止它。
步驟2、駕駛和控制汽車(chē)
在賽道上駕駛賽車(chē)超級(jí)容易。
我們只需要改變汽車(chē)的Y坐標(biāo),它就會(huì)開(kāi)始移動(dòng)。簡(jiǎn)單的邏輯是將Y值減少2個(gè)或3個(gè)點(diǎn),這樣它就會(huì)沿著Y軸向上移動(dòng),讓我們覺(jué)得汽車(chē)在移動(dòng):
import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
clock = pygame.time.Clock()
while True:
clock.tick(60)
car_y = car_y - 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.display.update()
這里,我們只是將變量' car_x '和' car_y '的值作為汽車(chē)在blit()方法中的位置傳遞,而不是像前面那樣直接硬編碼這些值。
另外,要控制pygame中元素的移動(dòng)并使其可見(jiàn),我們需要在每次迭代中都稍加延遲。可以使用timepygame中的模塊來(lái)完成。
就這樣。如果您現(xiàn)在運(yùn)行代碼,則汽車(chē)將開(kāi)始行駛。
但是有一個(gè)問(wèn)題。汽車(chē)駛出賽道并駛出屏幕。一旦到達(dá)道路/賽道的盡頭,我們需要停車(chē)。為此,我們的汽車(chē)需要檢測(cè)周?chē)h(huán)境并采取相應(yīng)措施。
我們將在我們的汽車(chē)上創(chuàng)建一個(gè)圓形,作為一個(gè)相機(jī):
import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
clock = pygame.time.Clock()
while True:
clock.tick(60)
cam_x = car_x + 15
cam_y = car_y + 15
car_y = car_y - 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.draw.circle(window, (0, 255, 0), (cam_x, cam_y), 5, 5)
pygame.display.update()
我們只使用了一些數(shù)學(xué)運(yùn)算,并使用drawPython模塊創(chuàng)建了圓圈。
該circle()方法需要幾個(gè)參數(shù):首先是窗口對(duì)象,然后是RGB格式的顏色,圓的位置以及圓的大小。
在上面的代碼中,我們?cè)趦蓷l直線上計(jì)算并指定了圓的坐標(biāo)-也就是說(shuō),我們需要在汽車(chē)前方的圓,因此我們?cè)谄?chē)位置的XY坐標(biāo)上添加了固定值15:
cam_x = car_x + 15
cam_y = car_y + 15
上面更新的代碼產(chǎn)生以下結(jié)果:
我們現(xiàn)在有相機(jī)cam。當(dāng)您運(yùn)行代碼時(shí),您會(huì)發(fā)現(xiàn)相機(jī)粘在汽車(chē)上并且工作正常。
現(xiàn)在的最后一部分是使汽車(chē)檢測(cè)到賽道的盡頭。既然有了相機(jī),我們需要使它向前看一點(diǎn)。如果白色軌道結(jié)束,我們將停止駕駛汽車(chē)。
為此,我們聲明一個(gè)變量focus_dis并將其賦值為25。這意味著它將負(fù)責(zé)查看前面的25個(gè)步驟。然后我們做一些數(shù)學(xué)運(yùn)算:
up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
if up_px == 255:
car_y = car_y - 2
通過(guò)使用當(dāng)前位置值并檢查向前的軌道是否為白色(255代表白色),我們可以得到向前的軌道的位置。如果是這樣,那么只有我們才能移動(dòng)汽車(chē)。
另外,如果您注意到,當(dāng)我們得到pygame窗口時(shí),我們的光標(biāo)仍在加載。我們將使用以下代碼擺脫它:
for event in pygame.event.get():
if event.type == pygame.QUIT:
drive = False
現(xiàn)在我們的車(chē)停在了賽道的盡頭。這是整個(gè)更新的代碼:
import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
focal_dis = 25
drive = True
clock = pygame.time.Clock()
while drive:
for event in pygame.event.get():
if event.type == pygame.QUIT:
drive = False
clock.tick(60)
cam_x = car_x + 15
cam_y = car_y + 15
up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
if up_px == 255:
car_y = car_y - 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.draw.circle(window, (0, 255, 0), (cam_x, cam_y), 5, 5)
pygame.display.update()
輸出如下圖:
車(chē)子停在賽道盡頭
棒不!那是我們自己的自動(dòng)駕駛汽車(chē)。
這是有趣的部分:上面的代碼不僅能夠檢測(cè)道路的盡頭,而且還可以檢測(cè)是否有障礙物(如下圖所示的第2賽道圖像):
我們的賽道2有一個(gè)障礙。但是我們的特斯拉可以檢測(cè)到它。它看到前方的軌道是紅色而不是白色,因此它停止了行駛。
在上面的代碼中,只需將賽道中的圖像更改為‘track2.png’:
track = pygame.image.load("track2.png")
現(xiàn)在,讓我們?cè)俅螆?zhí)行代碼。它產(chǎn)生以下輸出:
看,我們的車(chē)停下來(lái)了!
步驟3、處理轉(zhuǎn)彎
讓我們進(jìn)一步前進(jìn)。讓我們來(lái)嘗試一下‘track3.png’:
track = pygame.image.load(“track3.png”)
執(zhí)行代碼將產(chǎn)生以下輸出:
它工作正常。但是現(xiàn)在,這對(duì)我們來(lái)說(shuō)是另一個(gè)挑戰(zhàn):以一種能夠轉(zhuǎn)彎的方式駕駛汽車(chē)。
哦,請(qǐng)明白這一點(diǎn):在轉(zhuǎn)彎之前,我們的汽車(chē)應(yīng)該知道在特定的轉(zhuǎn)彎之后有一條道路/賽道。因此,無(wú)論何時(shí)汽車(chē)停下來(lái),我們都需要在這種情況下看向右側(cè)。
我們的汽車(chē)停在必須水平行進(jìn)的特定點(diǎn)上,即在X軸方向上,而我們的Y軸將保持不變。為此,我們將定義另一個(gè)變量‘right_px’,該變量將負(fù)責(zé)查看右側(cè),就像我們使用了‘up_px’:
right_px = window.get_at((cam_x + focal_dis, cam_y))[0]
因此,X軸+焦距和Y軸將保持不變。
讓我們定義一個(gè)變量‘direction’,以跟蹤汽車(chē)的行駛方向,并將其初始化為‘up’:
direction = 'up'
現(xiàn)在,我們將有一個(gè)if條件來(lái)防止up_px等于255-也就是說(shuō),它不應(yīng)是白色軌跡。如果right_px等于255,則意味著我們?cè)谟覀?cè)有一條白色的軌道,需要轉(zhuǎn)彎:
# 改變方向(轉(zhuǎn)彎)
if up_px != 255 and right_px == 255:
direction = 'right'
而已。我們if有條件檢查a是否up_px為白色,如果是,它將使汽車(chē)沿Y軸移動(dòng),還記得嗎?現(xiàn)在,讓我們簡(jiǎn)單地添加一個(gè)elif條件,使我們的汽車(chē)向右轉(zhuǎn):
# 開(kāi)車(chē)
if up_px == 255:
car_y = car_y - 2
elif direction == 'right' and right_px == 255:
car_x = car_x + 2
該代碼現(xiàn)在產(chǎn)生以下輸出:
賽車(chē)在右轉(zhuǎn)結(jié)束時(shí)停止,但角度錯(cuò)誤(見(jiàn)上圖)
這在一定程度上達(dá)到了目的,并且汽車(chē)向右行駛。但是我們也需要旋轉(zhuǎn)汽車(chē),對(duì)嗎?
我們可以使用該rotate()方法來(lái)做到這一點(diǎn)。它帶有兩個(gè)參數(shù):需要旋轉(zhuǎn)的對(duì)象和角度。
car = pygame.transform.rotate(car, -90)
但是當(dāng)您這樣做時(shí),您會(huì)發(fā)現(xiàn)汽車(chē)上的攝像頭會(huì)粘在后面,而我們需要將其放在前面。為了解決這個(gè)問(wèn)題,讓我們定義一個(gè)‘cam_x_offset = 0’初始化為零的變量。由于我們稍后將更改此變量的值,因此我們只需將該變量定義為0。此變量將負(fù)責(zé)攝像機(jī)的位置。因此,輪到我們時(shí),我們還需要更改此變量的值。
另外,讓我們檢查條件下的方向值。以下是更新的條件語(yǔ)句:
# 改變方向(轉(zhuǎn)彎)
if direction == 'up' and up_px != 255 and right_px == 255:
direction = 'right'
cam_x_offset = 30
car = pygame.transform.rotte(car, -90)
我們完了。現(xiàn)在應(yīng)該可以正常工作了。這是到目前為止的全部更新代碼:
import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track3.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
cam_x_offset = 0
direction = 'up'
focal_dis = 25
drive = True
clock = pygame.time.Clock()
while drive:
for event in pygame.event.get():
if event.type == pygame.QUIT:
drive = False
clock.tick(60)
cam_x = car_x + cam_x_offset + 15
cam_y = car_y + 15
up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
right_px = window.get_at((cam_x + focal_dis, cam_y))[0]
# 改變方向(轉(zhuǎn)彎)
if direction == 'up' and up_px != 255 and right_px == 255:
direction = 'right'
cam_x_offset = 30
car = pygame.transform.rotte(car, -90)
# 開(kāi)車(chē)
if direction == 'up' and up_px == 255:
car_y = car_y - 2
elif direction == 'right' and right_px == 255:
car_x = car_x + 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.draw.circle(window, (0, 255, 0), (cam_x, cam_y), 5, 5)
pygame.display.update()
讓我們看一下輸出:
完成的游戲,賽車(chē)在右轉(zhuǎn)彎的盡頭停下來(lái)了,并且車(chē)頭轉(zhuǎn)正了!
步驟4、新賽道-下移
現(xiàn)在,讓我們繼續(xù)前進(jìn)并檢查賽道4:
track = pygame.image.load("track4.png")
執(zhí)行代碼:
如上圖,它工作得很好,但這一次,我們需要讓相機(jī)向下看(在輸出中可以看到,我們有一個(gè)向下的軌道),我們的汽車(chē)需要再次轉(zhuǎn)彎。現(xiàn)在你知道怎么做了吧?
-
轉(zhuǎn)向的車(chē)。
-
創(chuàng)建down_px變量向下看。
-
設(shè)置cam_y偏移設(shè)置相機(jī)在y軸上的偏移量。
很簡(jiǎn)單,是吧?
這是經(jīng)過(guò)上述更改后的更新代碼:
import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track3.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
cam_x_offset = 0
direction = 'up'
focal_dis = 25
drive = True
clock = pygame.time.Clock()
while drive:
for event in pygame.event.get():
if event.type == pygame.QUIT:
drive = False
clock.tick(60)
cam_x = car_x + cam_x_offset + 15
cam_y = car_y + 15
up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
right_px = window.get_at((camx + focal_dis, cam_y))[0]
# change direction (take turn)
if direction == 'up' and up_px != 255 and right_px == 255:
direction = 'right'
cam_x_offset = 30
car = pygame.transform.rotate(car, -90)
# drive
if direction == 'up' and up_px == 255:
car_y = car_y - 2
elif direction == 'right' and right_px == 255:
car_x = car_x + 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.draw.circle(window, (0, 255, 0), (cam_x, cam_y), 5, 5)
pygame.display.update()import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽車(chē)游戲')
window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track4.png")
car = pygame.image.load("tesla.png")
car = pygame.transform.scale(car, (30, 60))
car_x = 150
car_y = 300
cam_x_offset = 0
cam_y_offset = 0
direction = 'up'
focal_dis = 25
drive = True
clock = pygame.time.Clock()
while drive:
for event in pygame.vent.get():
if event.type == pygame.QUIT:
drive = False
clock.tick(60)
cam_x = car_x + cam_x_offset + 15
cam_y = car_y + cam_y_offset + 15
up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
right_px = window.get_at((cam_x + focal_dis, cam_y))[0]
down_px = window.get_at((cam_x, cam_y + focal_dis))[0]
# 改變方向(轉(zhuǎn)彎)
if direction == 'up' and up != 255 and right_px == 255:
direction = 'right'
cam_x_offset = 30
car = pygame.transform.rotate(car, -90)
elif direction == 'right' and right_px != 255 and down_px == 255:
direction = 'down'
car_x = car_x + 30
cam_x_offset = 0
cam_y_offset = 30
car = pygame.transform.rotate(car, -90)
# 開(kāi)車(chē)
if direction == 'up' and up_px == 255:
car_y = car_y - 2
elif direction == 'right' and right_px == 255:
car_x = car_x + 2
elif direction == 'down' and down_px == 255:
car_y = car_y + 2
window.blit(track, (0, 0))
window.blit(car, (car_x, car_y))
pygame.draw.circle(window, (0, 255, 0), (cam_x, cam_y), 5, 5)
pygame.display.update()
執(zhí)行它,輸出如下:
好極了!我們的自動(dòng)駕駛汽車(chē)已經(jīng)在學(xué)習(xí)如何自動(dòng)駕駛。
步驟5、新路線-最終道路
現(xiàn)在,讓我們繼續(xù)前進(jìn)并檢查賽道5:
track = pygame.image.load("track5.png")
讓我們看一下輸出:
在我們的車(chē)左轉(zhuǎn)之前,它一直運(yùn)轉(zhuǎn)良好。我們的車(chē)可以上下移動(dòng),現(xiàn)在,我們需要做同樣的更改,并處理使汽車(chē)左轉(zhuǎn)的情況。為什么離開(kāi)了?假設(shè)你在車(chē)?yán)?綠點(diǎn)是前面的攝像頭)。
elif direction == 'down' and down_px != 255 and right_px == 255:
direction = 'right'
car_y = car_y + 30
cam_x_offset = 30
cam_y_offset = 0
car = pygame.transform.rotate(car, 90)
這將使我們的汽車(chē)向左轉(zhuǎn)。讓我們看一下輸出:
我們做到了。現(xiàn)在,最后一件事是重新確定方向up并處理汽車(chē)和攝像頭的位置。
這是相同的條件:
elif direction == 'right' and right_px != 255 and up_px == 255:
direction = 'up'
car_x = car_x + 30
cam_x_offset = 0
car = pygame.transform.rotate(car, 90)
讓我們執(zhí)行代碼以查看輸出:
有用!我們已經(jīng)處理了所有方向,現(xiàn)在我們的汽車(chē)將能夠在任何道路上自動(dòng)行駛。嘗試‘track6’進(jìn)行驗(yàn)證。
track = pygame.image.load("track6.png")
最終執(zhí)行,輸出如下:
OK,我們做到了。您有自己的自動(dòng)駕駛汽車(chē)了。
審核編輯 :李倩
-
編輯器
+關(guān)注
關(guān)注
1文章
805瀏覽量
31163 -
python
+關(guān)注
關(guān)注
56文章
4792瀏覽量
84628 -
自動(dòng)駕駛
+關(guān)注
關(guān)注
784文章
13784瀏覽量
166394
原文標(biāo)題:Python 代碼實(shí)現(xiàn)汽車(chē)自動(dòng)駕駛技術(shù)
文章出處:【微信號(hào):Linux迷,微信公眾號(hào):Linux迷】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論