这个项目是关于什么的?

  • 我们将学习基本但关键的知识,构建你的自动驾驶汽车(虚拟显示)。
  • 通过60行Python构建自动驾驶汽车,了解运动、轨迹检测、方向检测的基本原理,并且一切都是免费的。
  • 你会变得更聪明,就像埃隆·马斯克(Elon Musk)那样(你的朋友会嫉妒羡慕你的)。

必要条件

你不需要真正关注跟踪马斯克。但有两个简单的必要条件

  1. 在您的系统中安装最新版本的Python。
  2. 选择一个你熟悉的代码编辑器(PyCharm或VS code,我认为都很酷)。

自动驾驶汽车的核心特征

  • 首先,它可以自动驾驶,而无需任何人为干预。
  • 其次,如果道路上有任何障碍物,道路尽头或停车信号,它可以停止行驶。
  • 第三,它可以检测道路并进行相应地转弯。
  • 第四,可以自己到达目的地。

了解这四个核心功能后,您就可以完成这个项目。

让我们跳过无聊的部分

我们需要创建一个有道路、汽车、转弯和障碍的项目。这几乎就像设计一款游戏。

Python提供了一个名为pygame的第三方模块,它是一组用于编写电子游戏的Python模块。这允许你用Python语言创建功能齐全的游戏和多媒体程序。

因为它是第三方模块,所以在使用它之前,我们需要先安装它。打开终端,输入以下命令安装pygame:

linuxmi@linuxmi:~/www.linuxmi.com$ pip3 install pygame

安装好之后,我们可以通过简单地将该模块导入到我们的项目中来访问该模块提供的各种功能:

import pygame

让我们从有趣的代码开始

我们将通过五个简单的步骤来完成此项目。

步骤1、启动pygame

获取相关资源。

资源包含汽车将在其上行驶的六种不同类型的赛道。 第六部分有些困难,但是使用它会很有趣。

第一条路很简单:我们要走一条直线路,我们必须在那儿开车。

从第一个赛道开始,我们需要做三件事:

  1. 初始化pygame模块
  2. 创建所需大小的窗口
  3. 加载轨道图像并将其添加到窗口

我们将从导入pygame模块开始:

import pygame

在使用模块之前先初始化它:

pygame.init():

接下来,我们使用display() 方法创建基本的pygame窗口。 display方法将简单地创建一个空窗口,我们可以在其中放置元素。 但是我们需要定义我们需要创建的窗口的大小。 我们可以使用set_mode() 方法做到这一点。

在set_mode() 方法中,我们将窗口的大小(以像素为单位)传递为表示该窗口的XY坐标的Python元组。 Python元组是用括号括起来的有效Python数据类型的逗号分隔元素的集合,例如:() 。

有了窗口后,就可以使用pygame提供的图像模块加载图像并创建图像表面对象。 图像表面在字节缓冲区内共享数据。 没有图像类。 图像作为表面对象加载:

window = pygame.display.set_mode((1200, 400))
track = pygame.image.load("track1.png")

图像模块是pygame的必需依赖项,它提供了load()方法,该方法通过创建图像表面对象来加载传递的图像。

方法blit() — blit表示块图像传输 – 将一个表面的内容复制到另一表面。 我们需要传递表面图像对象以及我们要将图像放置在窗口中的位置。 最后,我们可以使用update()方法更新空白窗口。

上面的代码产生以下输出:

现在我们有了轨道,让我们再来添加汽车。我们将遵循与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()

添加汽车图像时,我们必须调整其大小以满足我们的要求,因为默认图像尺寸可能不适合。 我们可以使用转换模块的scale()方法来调整图像的大小,并将图像对象和新的大小作为元组作为参数传递。

运行上面的代码,输出如下:

步骤1完成。在第二步,我们将驾驶汽车在赛道上,并在需要时停止它。

步骤2、驾驶和控制汽车

在赛道上驾驶赛车超级容易。

我们只需要改变汽车的Y坐标,它就会开始移动。简单的逻辑是将Y值减少2个或3个点,这样它就会沿着Y轴向上移动,让我们觉得汽车在移动:

import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽车游戏')
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 ‘的值作为汽车在blit()方法中的位置传递,而不是像前面那样直接硬编码这些值。

另外,要控制pygame中元素的移动并使其可见,我们需要在每次迭代中都稍加延迟。可以使用timepygame中的模块来完成。

就这样。如果您现在运行代码,则汽车将开始行驶。

但是有一个问题。汽车驶出赛道并驶出屏幕。一旦到达道路/赛道的尽头,我们需要停车。为此,我们的汽车需要检测周围环境并采取相应措施。

我们将在我们的汽车上创建一个圆形,作为一个相机:

import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽车游戏')
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()

我们只使用了一些数学运算,并使用drawPython模块创建了圆圈。

该circle()方法需要几个参数:首先是窗口对象,然后是RGB格式的颜色,圆的位置以及圆的大小。

在上面的代码中,我们在两条直线上计算并指定了圆的坐标-也就是说,我们需要在汽车前方的圆,因此我们在汽车位置的XY坐标上添加了固定值15:

cam_x = car_x + 15
cam_y = car_y + 15

上面更新的代码产生以下结果:

我们现在有相机cam。当您运行代码时,您会发现相机粘在汽车上并且工作正常。

现在的最后一部分是使汽车检测到赛道的尽头。既然有了相机,我们需要使它向前看一点。如果白色轨道结束,我们将停止驾驶汽车。

为此,我们声明一个变量focus_dis并将其赋值为25。这意味着它将负责查看前面的25个步骤。然后我们做一些数学运算:

up_px = window.get_at((cam_x, cam_y - focal_dis))[0]
    if up_px == 255:
        car_y = car_y - 2

通过使用当前位置值并检查向前的轨道是否为白色(255代表白色),我们可以得到向前的轨道的位置。如果是这样,那么只有我们才能移动汽车。

另外,如果您注意到,当我们得到pygame窗口时,我们的光标仍在加载。我们将使用以下代码摆脱它:

for event in pygame.event.get():
        if event.type == pygame.QUIT:
            drive = False

现在我们的车停在了赛道的尽头。这是整个更新的代码:

import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽车游戏')
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()

输出如下图:

车子停在赛道尽头

棒不!那是我们自己的自动驾驶汽车。

这是有趣的部分:上面的代码不仅能够检测道路的尽头,而且还可以检测是否有障碍物(如下图所示的第2赛道图像):

我们的赛道2有一个障碍。但是我们的特斯拉可以检测到它。它看到前方的轨道是红色而不是白色,因此它停止了行驶。

在上面的代码中,只需将赛道中的图像更改为‘track2.png’:

track = pygame.image.load("track2.png")

现在,让我们再次执行代码。它产生以下输出:

看,我们的车停下来了!

步骤3、处理转弯

让我们进一步前进。让我们来尝试一下‘track3.png’:

track = pygame.image.load(“track3.png”)

执行代码将产生以下输出:

它工作正常。但是现在,这对我们来说是另一个挑战:以一种能够转弯的方式驾驶汽车。

哦,请明白这一点:在转弯之前,我们的汽车应该知道在特定的转弯之后有一条道路/赛道。因此,无论何时汽车停下来,我们都需要在这种情况下看向右侧。

我们的汽车停在必须水平行进的特定点上,即在X轴方向上,而我们的Y轴将保持不变。为此,我们将定义另一个变量‘right_px’,该变量将负责查看右侧,就像我们使用了‘up_px’:

right_px = window.get_at((cam_x + focal_dis, cam_y))[0]

因此,X轴+焦距和Y轴将保持不变。

让我们定义一个变量‘direction’,以跟踪汽车的行驶方向,并将其初始化为‘up’:

direction = 'up'

现在,我们将有一个if条件来防止up_px等于255-也就是说,它不应是白色轨迹。如果right_px等于255,则意味着我们在右侧有一条白色的轨道,需要转弯:

# 改变方向(转弯)
    if up_px != 255 and right_px == 255:
        direction = 'right'

而已。我们if有条件检查a是否up_px为白色,如果是,它将使汽车沿Y轴移动,还记得吗?现在,让我们简单地添加一个elif条件,使我们的汽车向右转:

# 开车
    if up_px == 255:
        car_y = car_y - 2
    elif direction == 'right' and right_px == 255:
        car_x = car_x + 2

该代码现在产生以下输出:

赛车在右转结束时停止,但角度错误(见上图)

这在一定程度上达到了目的,并且汽车向右行驶。但是我们也需要旋转汽车,对吗?

我们可以使用该rotate()方法来做到这一点。它带有两个参数:需要旋转的对象和角度。

car = pygame.transform.rotate(car, -90)

但是当您这样做时,您会发现汽车上的摄像头会粘在后面,而我们需要将其放在前面。为了解决这个问题,让我们定义一个‘cam_x_offset = 0’初始化为零的变量。由于我们稍后将更改此变量的值,因此我们只需将该变量定义为0。此变量将负责摄像机的位置。因此,轮到我们时,我们还需要更改此变量的值。

另外,让我们检查条件下的方向值。以下是更新的条件语句:

# 改变方向(转弯)
    if direction == 'up' and up_px != 255 and right_px == 255:
        direction = 'right'
        cam_x_offset = 30
        car = pygame.transform.rotate(car, -90)

我们完了。现在应该可以正常工作了。这是到目前为止的全部更新代码:

import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽车游戏')
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]
# 改变方向(转弯)
    if direction == 'up' and up_px != 255 and right_px == 255:
        direction = 'right'
        cam_x_offset = 30
        car = pygame.transform.rotate(car, -90)
# 开车
    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()

让我们看一下输出:

完成的游戏,赛车在右转弯的尽头停下来了,并且车头转正了!

步骤4、新赛道-下移

现在,让我们继续前进并检查赛道4:

track = pygame.image.load("track4.png")

执行代码:

如上图,它工作得很好,但这一次,我们需要让相机向下看(在输出中可以看到,我们有一个向下的轨道),我们的汽车需要再次转弯。现在你知道怎么做了吧?

  1. 转向的车。
  2. 创建down_px变量向下看。
  3. 设置cam_y偏移设置相机在y轴上的偏移量。

很简单,是吧?

这是经过上述更改后的更新代码:

import pygame
pygame.init()
pygame.display.set_caption('Linux迷 www.linuxmi.com 特斯拉汽车游戏')
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]
# 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 特斯拉汽车游戏')
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.event.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]
# 改变方向(转弯)
if direction == 'up' and up_px != 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)
# 开车
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()

执行它,输出如下:

好极了!我们的自动驾驶汽车已经在学习如何自动驾驶。

步骤5、新路线-最终道路

现在,让我们继续前进并检查赛道5:

track = pygame.image.load("track5.png")

让我们看一下输出:

在我们的车左转之前,它一直运转良好。我们的车可以上下移动,现在,我们需要做同样的更改,并处理使汽车左转的情况。为什么离开了?假设你在车里(绿点是前面的摄像头)。

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)​

这将使我们的汽车向左转。让我们看一下输出:

我们做到了。现在,最后一件事是重新确定方向up并处理汽车和摄像头的位置。

这是相同的条件:

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)

让我们执行代码以查看输出:

有用!我们已经处理了所有方向,现在我们的汽车将能够在任何道路上自动行驶。尝试‘track6’进行验证。

track = pygame.image.load("track6.png")

最终执行,输出如下:

OK,我们做到了。您有自己的自动驾驶汽车了。

完整资源及代码请关注Linux迷公众号,回复PYTT即可得到下载链接。

来自:medium.com 作者:Shajedul Karim 译者:XXVI.AI

发表评论