玩转JetBot自动驾驶 (五)通过采集数据教JetBot如何认识危险

原版翻译notebook可在Github下载:
https://github.com/ling3ye/jetbot/blob/master/notebooks/collision_avoidance/data_collection.ipynb

如果你学会了通过基础移动的notebook实现jetbot的行走,那就太了不起了。
但其实更了不起的是让jetbot独自行走。
这将会是一个超级难的任务,有许多不同的处理,但所有的问题通常会分解成更容易的子问题。
而最重要的解决的问题是防止jetbot发生危险的情况,我们称之为避障。

在这一套notebook,我们将会使用深度学习和一个非常通用的传感器:摄像头,来解决问题。
你将学会如何使用神经网络,摄像头和NVIDIA Jetson Nano教会JetBot学习一个非常有用的行为——避障!

我们想象一个虚拟的安全罩(范围),在这个安全罩内机器人能够旋转一圈而不会碰撞到任何物体(或者其他情况,例如从桌面上掉落)。

当然,JetBot会受到视野的限制,我们无法防止物体被放置在JetBot后面等问题。但我们可以防止JetBot进入这些地方或场景。

我们的方式非常简单: 首先,我们会手动地把JetBot放置在违反安全罩的地方或场景中,把这些情景拍照并标记为blocked(阻塞)。 其次,我们会手动地把JetBot放置放置在符合安全罩的地方或者场景中,把这些情景拍照并标记为free(通畅)。

这就是我们在这个notebook所做的一切:数据采集。一旦我们有了大量的图像和标签,我们会把数据上传到支持GPU运算的主机上,训练一个神经网络,然后根据JetBot所看到的图像通过这个神经网络来判断安全罩是否被侵犯。最后,我们将使用这个神经网络来实现一个简单的避障行为!

重要提示:当JetBot旋转的时候,它事件上是围绕着两个轮子之间的中心点旋转,而不是JetBot地盘的中心旋转。当你尝试预估JetBot旋转时安全罩是否被侵犯了的时候,这是一个重要的参考细节。但也不用太担心,不必太过于准确。如果有不放心的,最好就往更谨慎的方向做(例如虚拟一个更大的安全罩)。我们要确保JetBot不会进入一个无法转向而又无法离开的情况。

实时显示摄像头

那么,我们就开始了。首先,让我们像在notebook中初始化摄像头,并显示所看到的画面。

我们的神经网络采用224×224像素的图像作为输入。因此我们将摄像头设置为该大小,以最小化文件大小,而最小化数据集。(我们已经通过测试此像素适用于此任务) 在某些情况下,收集数据时最好用较大的图像尺寸,然后做处理的时候缩小到需要的大小。

import traitlets
import ipywidgets.widgets as widgets
from IPython.display import display
from jetbot import Camera, bgr8_to_jpeg

camera = Camera.instance(width=224, height=224)

image = widgets.Image(format='jpeg', width=224, height=224)  # this width and height doesn't necessarily have to match the camera

camera_link = traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg)

display(image)

运行完上面的代码块后,就可以实时的看到摄像头拍摄到的画面。

接下来让我们创建一些目录存储数据。我们将会建立一个叫dataset的文件夹,里面有两个子文件夹,分别是 freeblocked,用于分类放置每一个场景的图片。

import os

blocked_dir = 'dataset/blocked'
free_dir = 'dataset/free'

# we have this "try/except" statement because these next functions can throw an error if the directories exist already
try:
    os.makedirs(free_dir)
    os.makedirs(blocked_dir)
except FileExistsError:
    print('Directories not created becasue they already exist')

运行完上面的代码块后,你现在刷新左侧的Jupyter文件浏览器,你应该会见到这些目录。

创建分类按钮

接下来,我们将创建一些按钮用来保存不同标签的快照。我们还将创建一些文本框,用于显示到目前位置我们每个标签收集到的图像数量。这很重要,因为我们要确保采集到的free图像要和blocked图像一样多。还有助于我们了解整体收集了多少图像。

button_layout = widgets.Layout(width='128px', height='64px')
free_button = widgets.Button(description='add free', button_style='success', layout=button_layout)
blocked_button = widgets.Button(description='add blocked', button_style='danger', layout=button_layout)
free_count = widgets.IntText(layout=button_layout, value=len(os.listdir(free_dir)))
blocked_count = widgets.IntText(layout=button_layout, value=len(os.listdir(blocked_dir)))

display(widgets.HBox([free_count, free_button]))
display(widgets.HBox([blocked_count, blocked_button]))

到此为止,那些按钮是不会做任何事的。我们需要把保存图像的函数附加到每一个按钮的on_click 事件中。我们会通过Image部件,把这些经过压缩处理的JPEG格式图像保存到对应的分类文件夹里。

为了确保我们不重复的任何文件名(甚至跨越不同的机器!)我们将在python中使用uuidpackage,它的作用是可以定义uuid方法,生成唯一标识符。该标识符是根据当前机器地址和时间生成的。

from uuid import uuid1

def save_snapshot(directory):
    image_path = os.path.join(directory, str(uuid1()) + '.jpg')
    with open(image_path, 'wb') as f:
        f.write(image.value)

def save_free():
    global free_dir, free_count
    save_snapshot(free_dir)
    free_count.value = len(os.listdir(free_dir))
    
def save_blocked():
    global blocked_dir, blocked_count
    save_snapshot(blocked_dir)
    blocked_count.value = len(os.listdir(blocked_dir))
    
# attach the callbacks, we use a 'lambda' function to ignore the
# parameter that the on_click event would provide to our function
# because we don't need it.
free_button.on_click(lambda x: save_free())
blocked_button.on_click(lambda x: save_blocked())

太棒了,现在上面的按钮应该可以把图片保存在free 或者 blocked 文件夹里。你可以使用Jupyter Lab 的文件浏览器去查看这些文件。

现在开始动手收集一些数据

1,请把JetBot放在阻挡的情况下,并按下add blocked按钮 2,请把JetBot放在通畅的情况下,并按下add free按钮 3.重复1,2

提示:你可以把那些按钮部件输出在新的窗口,这样方便你的操作。我们也将执行下面的代码把它们显示在一起。

以下是一些数据标记的提示

  1. 尝试不同方向
  2. 尝试不同的照明
  3. 尝试不同的对象/碰撞类型:例如墙壁,物体等
  4. 尝试不同纹理的平面/物体:例如图案,不同光滑度,玻璃等

最终,JetBot在现实世界中越多的场景数据其防撞行为就越好,所以得到各种各样的数据很重要,而不仅仅是大量的数据。可能每个分类都需要100个图像(这不一定是一个科学的做法,仅仅是一个有用的提示)。收集这么多数据其实不用担心,当你开始收集的时候,就会变得很快完成。

display(image)
display(widgets.HBox([free_count, free_button]))
display(widgets.HBox([blocked_count, blocked_button]))

下一步

当你收集足够的数据的时候,我们需要把这些数据复制到我们的GPU平台上进行训练。首先,我们可以调用terminal(命令行模式又或者叫终端)命令,进行数据打包压缩成一个*.zip文件。

! 表示我们要将使用shell命令运行 -r 表示包含所有包含子文件夹文件。-q 表示zip命令不输出任何信息

!zip -r -q dataset.zip dataset

您应该在Jupyter Lab文件浏览器中看到名为dataset.zip的文件。你可以右键点击该文件进行下载操作。

接下来,我们需要把这些数据上传到我们的GPU平台或者云计算机来训练我们的防撞神经网络。

而Jetson Nano是支持GPU的,所以接下来,我们直接在Jetson Nano上训练,期待下一篇的玩转JetBot将一步一步地说明,这将会非常简单。

发表评论

BACK TO TOP