基于树莓派与神经网络的智能小车

树莓派 + Arduino + 神经网络

智能小车项目介绍 & 源码分享

几乎每一位EECS工科少年的大学生涯都会有一段叫作“智能小车”的回忆。

这不,远在欧洲的歪果仁也是。近日,在法国图卢兹所举行的Robot Race 2017竞赛中(类似于我国的全国大学生智能车竞赛),两位参赛者利用Raspberry Pi与Arduino,通过引入神经网络进行图像处理的方式,打造了一台基于计算机视觉的智能循迹小车,成功应对了赛道环境光照不稳定所带的挑战。

本文将对该作品的视觉部分做重点分享*。

1

Demo视频

下面,就让我们以一段作品的demo视频来开始今天的分享吧:

注:此项目不需要机器学习方面的知识,但可以帮助理解图像处理方法的细节。

2

赛道概况与挑战

有必要先对比赛的赛道做一个简单的介绍:

比赛采用的是一根110米长的环形自主计时赛道,其四周由木板搭建构成,在赛道中央贴有黑白相间的轨迹。规则允许参赛选手通过任意方式来实现智能小车的自主行驶完赛

相比于采用“超声波传感器”,实际比赛中作者决定使用中线视觉循迹的策略来完成比赛。而其中,最大的挑战是需要克服现场赛道不同位置光线环境的变化所造成的小车视觉循迹的不稳定性。因此,作者最终决定将机器学习应用到这一项目中来帮助探测赛道的中线,即使用神经网络,通过训练一个模型来从所获得的图像输入中预测出赛道中心的准确位置。这也是这一项目最有意思的部分。

3

图像处理部分

在该策略下,小车需要能够自行探测线路并能及时预测转弯,从而既能够在直线赛道全速行驶同时能够在转弯之前降低速度。上述功能作者通过对树莓派摄像头采集的图像进行处理分两步来实现完成(下文通过第一人称来做介绍):

01. 预测赛道曲率:

为了实现预测转弯并减少计算时间,我们通过摄像头对三个感兴趣区域点(ROI)进行探测捕捉,并对所捕获的图像进行了裁剪。然后,我们将一条线(下图中的绿线)拟合到所获得的三个点上,并计算此线与参考线(图中的蓝线)之间的角度,从而估算出弯道的转弯程度。若此时是一条笔直赛道,则绿色与蓝线会重合。

图中右半部分为三个感兴趣区域以及所预测的赛道中心(红色圆圈标出)。左半图为针对输入图像所计算出的结果 ,其中绿线为拟合线, 蓝线是对应于小车在笔直赛道情况下的参考线。

02. 探测确定赛道中心:

最初,我们准备使用基于颜色的方法让小车来探测赛道的中心。 基本的想法是找到给定赛道颜色的最大区域,然后计算该区域的几何中心(即赛道中心)。

这里先对“颜色检测法”做一些简单介绍:首先在HSV颜色空间中转换图像,然后根据预定义的阈值计算颜色遮罩,所获得的几何中心即为赛道中心。

上图显示的是仿真环境下所获得的结果,红圈表示找到的赛道中心,绿色线条对应于颜色遮罩的轮廓。现实中,只需要30行Python代码,就能够实现上述:

from __future__ import division

import cv2

import numpy as np

# Input Image

image = cv2.imread("my_image.jpg")

# Convert to HSV color space

hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)

# Define range of white color in HSV

lower_white = np.array([0, 0, 212])

upper_white = np.array([131, 255, 255])

# Threshold the HSV image

mask = cv2.inRange(hsv, lower_white, upper_white)

# Remove noise

kernel_erode = np.ones((4,4), np.uint8)

eroded_mask = cv2.erode(mask, kernel_erode, iterations=1)

kernel_dilate = np.ones((6,6),np.uint8)

dilated_mask = cv2.dilate(eroded_mask, kernel_dilate, iterations=1)

# Find the different contours

im2, contours, hierarchy = cv2.findContours(dilated_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Sort by area (keep only the biggest one)

contours = sorted(contours, key=cv2.contourArea, reverse=True)[:1]

if len(contours) > 0:

M = cv2.moments(contours[0])

# Centroid

cx = int(M['m10']/M['m00'])

cy = int(M['m01']/M['m00'])

print("Centroid of the biggest area: ({}, {})".format(cx, cy))

else:

print("No Centroid Found")

然而,这一方法最主要的缺点是在赛道不同区域环境光变化下所带来的检测不稳定性。我们尝试了直方图均衡法来克服这个问题,但发现效果并不理想,并且后者的计算量很大。最终我们决定引入神经网络,通过训练一个模型来从所获得的图像输入中预测出赛道中心的准确位置,使用纯粹的numpy和python代码实现上述想法并不难。

4

机器学习部分

项目中,我们想要预测摄像头所输入图像的赛道中心的坐标。为简化问题,针对所输入的图像区域,我们仅预测赛道中心的x坐标(沿着宽度),也就是说这里我们假定中心位于裁剪图像高度的一半处。为了更好地评估我们的模型,我们选择均方误差(MSE)作为目标:我们取预测的x坐标与真正的赛道中心之间的平方误差,并在所有的训练样本上取平均值。

图像标记

首先,我们让小车在远程控制模式下录制了赛道的视频,然后我们手动标记了3000张图像(在约25分钟内完成,即1张标签/秒)。 为了达到目的,我们创建了自己的标记工具:每个训练图像逐一显示,在每一张图像上点击白色赛道的中心,然后必须按任意键才能进入下一张图像。

预处理和数据增强

在将我们的学习算法应用到数据之前,我们还要完成以下几步:先调整所输入图像的大小以减小输入尺寸(减少4倍),从而大大减少学习参数的数量。这样可以简化问题,加快训练和预测时间。此外,为避免学习问题的产生并加快训练,需要对数据进行规范化。 在我们的例子中,我们将输入图像统一在[-1,1]范围内,并将输出(赛道中心的预测x坐标)缩放为[0,1]。预处理脚本如下:

def preprocessImage(image, width, height):

"""

Preprocessing script to convert image into neural net input array

:param image: (cv2 RBG image)

:param width: (int)

:param height: (int)

:return: (numpy array)

"""

image = cv2.resize(image, (width, height), interpolation=cv2.INTER_LINEAR)

x = image.flatten() # create a big 1D-array

# Normalize

x = x / 255. # values in [0, 1]

x -= 0.5 # values in [-0.5, 0.5]

x *= 2 # values in [-1, 1]

return x

最后,为了增加训练样本的数量,我们将图像垂直翻转,以快速且简单的方式将训练集的大小乘以2。

神经网络架构

我们所使用的是一个前馈神经网络,由两个隐藏层组成,分别具有8个和4个单位。实际过程中,我们尝试了不同的架构,包括CNN(卷积神经网络),但我们发现前馈神经网络架构取得了最好的效果,可以以超过60 FPS的速度实时运行!

超参数的选择

我们选择的超参数包括网络架构,学习率,小批量大小...等。为了验证超参数的选择,我们将数据集分成3个子集:一个训练集(60%),一个验证集(20%)和一个测试集(20%)。 我们保持模型在验证集上的最低误差,并使用测试集估计我们的泛化误差。

最终,我们用了不到20分钟的时间,在一台8核笔记本电脑的CPU上对这一神经网络进行了训练。

5

智能小车的控制与其它

项目的其它亮点与完整源码,包括:

在完成图像处理部分并计算出偏离中线的误差之后,小车的控制策略;

项目的软硬件清单与程序框图;

树莓派与Arduino之间建立的串行通信协议;

作者使用Blender和Python所创建的一个仿真环境测试所得的基于计算机视觉算法的仿真结果等...

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171220G0X7FD00?refer=cp_1026

同媒体快讯

相关快讯

扫码关注云+社区