原版翻译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
的文件夹,里面有两个子文件夹,分别是 free
和blocked
,用于分类放置每一个场景的图片。
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中使用uuid
package,它的作用是可以定义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
提示:你可以把那些按钮部件输出在新的窗口,这样方便你的操作。我们也将执行下面的代码把它们显示在一起。
以下是一些数据标记的提示
- 尝试不同方向
- 尝试不同的照明
- 尝试不同的对象/碰撞类型:例如墙壁,物体等
- 尝试不同纹理的平面/物体:例如图案,不同光滑度,玻璃等
最终,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将一步一步地说明,这将会非常简单。