玩转JetBot自动驾驶 (五)通过采集数据教JetBot如何认识危险¶
如果你学会了通过基础移动的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 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)
运行完上面的代码块后,就可以实时的看到摄像头拍摄到的画面。
接下来让我们创建一些目录存储数据。我们将会建立一个叫dataset
的文件夹,里面有两个子文件夹,分别是 free
和blocked
,用于分类放置每一个场景的图片。
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
print('Directories not created becasue they already exist')
创建分类按钮¶
接下来,我们将创建一些按钮用来保存不同标签的快照。我们还将创建一些文本框,用于显示到目前位置我们每个标签收集到的图像数量。这很重要,因为我们要确保采集到的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中使用uuid
package,它的作用是可以定义uuid
方法,生成唯一标识符。该标识符是根据当前机器地址和时间生成的。
def save_snapshot(directory):
image_path = os.path.join(directory, str(uuid1()) + '.jpg')
with open(image_path, 'wb') as f:
global free_dir, free_count
free_count.value = len(os.listdir(free_dir))
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
提示:你可以把那些按钮部件输出在新的窗口,这样方便你的操作。我们也将执行下面的代码把它们显示在一起。
以下是一些数据标记的提示
- 尝试不同方向
- 尝试不同的照明
- 尝试不同的对象/碰撞类型:例如墙壁,物体等
- 尝试不同纹理的平面/物体:例如图案,不同光滑度,玻璃等
最终,JetBot在现实世界中越多的场景数据其防撞行为就越好,所以得到各种各样的数据很重要,而不仅仅是大量的数据。可能每个分类都需要100个图像(这不一定是一个科学的做法,仅仅是一个有用的提示)。收集这么多数据其实不用担心,当你开始收集的时候,就会变得很快完成。
display(widgets.HBox([free_count, free_button]))
display(widgets.HBox([blocked_count, blocked_button]))
下一步¶
当你收集足够的数据的时候,我们需要把这些数据复制到我们的GPU平台上进行训练。首先,我们可以调用_terminal_(命令行模式又或者叫终端)命令,进行数据打包压缩成一个*.zip文件。
! 表示我们要将使用shell命令运行 -r 表示包含所有包含子文件夹文件。-q 表示zip命令不输出任何信息
您应该在Jupyter Lab文件浏览器中看到名为!zip -r -q dataset.zip dataset
dataset.zip
的文件。你可以右键点击该文件进行下载操作。
接下来,我们需要把这些数据上传到我们的GPU平台或者云计算机来训练我们的防撞神经网络。
而Jetson Nano是支持GPU的,所以接下来,我们直接在Jetson Nano上训练,期待下一篇的玩转JetBot将一步一步地说明,这将会非常简单。