--

行为模仿 (behavior cloning)

无人驾驶的实现,在算法上有大致两种:

1. 端到端的深度学习,也就是从摄像头或者其他的直接的输入原始数据,训练出一个转向、油门、刹车的结果。
2. 每一步都用确定的规则来实现,从感知、定位到控制都有明确的规则。


而这里要介绍的是前者,端到端的深度学习无人驾驶技术

以下内容来自 Udacity 的 Self-Driving Car 的 Behavior Cloning 的相关资料




1. 资料



没有实车,我们需要有一款仿真器
simulator

相关的起步代码
behavior cloning code repo

以及一个训练数据集
dataset



2. 数据采集



尽管已经有一些训练数据提供了,你还是需要采集更多的控制数据集,同时,在你的 自动驾驶遇到困难的时候,也需要有针对性做一些采集工作。

尤其是,你在转弯的时候过不了,就要在转弯的阶段多跑几次,多采集样本。 同时,不仅要有跑偏了的样本,也要多采集跑回来的样本。

当然在这个模型中我们用单个的图片进行作为输入,没有考虑时序的信息。

数据的 X 是原始照片,数据的标签 Y 是转向角度,转向角度的范围在 -1 到 +1 之间。



3. 数据增强



在‘record’ 模式下,会生成一个 IMG 文件夹,另外有一个 driving_log.csv 的文件,记录了每个图片对应的转向角,油门,以及刹车等数据。

有三个摄像头, 位于左中右不同的位置。

为了增强数据的训练集,以及减小问题的不必要的复杂度, 我们需要采用这三个摄像头的图片,把左边的图片的偏转角度 调整为要右打方向盘一定角度,中间不变,右边调整为左打方向盘一定角度。

同时可以看到,摄像头的整张图片中,上面一部分是背景,包括天空之类,是属于无用的信息,如果都考虑进来,会极大影响结果。 而最底下也有一部分是车头的一部分,也是无用的,需要截去。

下面是左中右三个摄像头分别拍到的画面





4. 数据读入



我们需要来读入跑出来的数据。
import csv
import numpy as np
import cv2

lines = []
with open('driving_log.csv') as csvfile:
    reader = csv.reader(csvfile)
    for line in reader:
        lines.append(line)

images = []
measurements = []
for line in lines:
    source_path = line[0]
    # switch from absolute path to relative path
    # filename = source_path.split('/')[-1]
    filename = os.path.basename(source_path)
    current_path = os.path.join('../data/IMG/', filename)
    image = cv2.imread(current_path)
    images.append(image)
    measurement = float(line[3])
    measurements.append(measurement)

X_train = np.array(images)
Y_train = np.array(measurements)


5. 搭建网络



这里我们用Keras 来写,Keras 的参考

中文   english

from keras.models import Sequential
from keras.layers import Flatten, Dense

model = Sequential()
model.add(Flatten(input_shape = (160, 320, 3)))
model.add(Dense(1))

model.compile(loss = 'mse', optimizer = 'adam')
model.fit(X_train, Y_train, validation_split)

这个 model 当然是非常简单的,估计效果也不会很好。不过可以从这么一个简单的模型开始,进一步改进。


6. 进一步改善

1. 截图,去掉无关紧要的顶部和底部像素
from keras.layers import Cropping2D
model = Sequential()
model.add(Cropping2D(cropping=((50,20), (0,0)), input_shape=(3,160,320)))


这里利用 Keras 的 Cripping2D 函数,cropping 的参数分别是 (上,下),(左,右)

我们采用Keras 的 Cropping2D 而不是手动写一个循环来做,是因为 Keras 能够并行处理,速度较快。

2. 更加复杂的网络

请参考另外一篇博客 Nvidia model

7. 数据增强

除了利用左中右三个摄像头的数据之外,我们还可以采用各种多类型的训练数据类型,
比如,换个方向跑几圈, 将图像左右翻转一下, 在转弯的地方多跑几圈, 从偏离状态恢复到正常也跑几圈。

8. 训练拟合

Keras 提供了 API 用来进行 fit,以及 fit_generator() 来保存 fit 的中间过程。 这里 train_generator 是一个 生成器,用来迭代的获取数据。

为什么要怎么做?
因为数据实在是太大了,现在一张图片的大小是 160 x 320 x 3 的 float,10000 张就需要1.5 G 的大小,不可能完全一次全部吃进到内存中去。
from keras.models import Model
import matplotlib.pyplot as plt

history_object = model.fit_generator(train_generator, samples_per_epoch =
    len(train_samples), validation_data = 
    validation_generator,
    nb_val_samples = len(validation_samples), 
    nb_epoch=5, verbose=1)

### print the keys contained in the history object
print(history_object.history.keys())

### plot the training and validation loss for each epoch
plt.plot(history_object.history['loss'])
plt.plot(history_object.history['val_loss'])
plt.title('model mean squared error loss')
plt.ylabel('mean squared error loss')
plt.xlabel('epoch')
plt.legend(['training set', 'validation set'], loc='upper right')
plt.show()


我们可以这样定义 train_generator:

def generator(samples, batch_size=32):
    num_samples = len(samples)
    while 1: # Loop forever so the generator never terminates
        shuffle(samples)
        for offset in range(0, num_samples, batch_size):
            batch_samples = samples[offset:offset+batch_size]

            images = []
            angles = []
            for batch_sample in batch_samples:
                name = './IMG/'+batch_sample[0].split('/')[-1]
                center_image = cv2.imread(name)
                center_angle = float(batch_sample[3])
                images.append(center_image)
                angles.append(center_angle)

            # trim image to only see section with road
            X_train = np.array(images)
            y_train = np.array(angles)
            yield sklearn.utils.shuffle(X_train, y_train)
train_generator = generator(train_samples, batch_size=32)
validation_generator = generator(validation_samples, batch_size=32)