JetBot系列教程之手柄遥控

摘要: 以前说到智能小车,可能大家第一想到的会是遥控。在上一章节中其实我们已经用到了遥控,通过web创建工具组件来控制小车。本章节我们介绍如何使用游戏手柄控制小车 ... ... ... ...
游戏手柄
在JetBot AI 套件中,我们为各位配了一个游戏手柄。游戏手柄需要装电池使用,使用前请先自行购买两节5号电池(为了方便替换,你不妨多买几个)。
打开游戏手柄背面的电池盒,里面有一个小的USB适配器,注意不要弄丢这个适配器。请将USB适配器插入到你电脑的USB接口。将电池装入电池盒然后盖好。将开关拨到ON。
观察手柄前面显示面板,如果面板指示灯没有亮的话,按下HOME按键。
手柄有两种工作模式,可以按下HOME键来切换,当只有一个指示灯的时候,摇杆输出的值为0, 1。如果面板显示为两个指示灯的时候,摇杆可以输出模拟值(这个功能主要是后面章节做数据收集的时候会用到)

Teleoperation程序

Jetbot的项目中提供了一个手柄控制的程序,打开Notebooks/teleoperation路径下的teleoperation.ipynb文件。


第一步,先不要急着运行程序。很多用户直接打开就运行程序,然后到后面发现手柄完全没有效果,以为是手柄有问题。在此,我只想说,请认真看注释!请认真看注释!请认真看注释!重要的事情我们要说三遍。但是,看不懂注释怎么办╮(╯_╰)╭,好吧,且听我道来。

首先,请检查是否把无线手柄的USB适配器插到了电脑上,再检查手柄是否已经正常上电且处于工作状态。然后打开下面这个网页:https://html5gamepad.com/ 

打开之后,如果手柄通信正常,你会看到这样的画面,按下手柄上的按键试一下按键是否被识别到了,如果被正常识别就可以了。


这里特别 特别 特别需要注意这个INDEX的值。有些用户识别到的INDEX值是0,这个也是手柄的INDEX值,并不是没有识别到的意思。如果没有检测到手柄,网页会提示No gamepad detected。

链接手柄

如果你已经记住了手柄的INDEX值的话,那就让我们回到例程。

你需要把Index的值改成你手柄的实际值,比如这里我的手柄检测到的INDEX是0,所以我就改成了0。运行单元2(实际上这是第一个单元,因为我运行了两次,所以变成了单元2,本章节后面包括之后的章节我们不再就这个情况解释了╭(╯^╰)╮)


运行后会显示controller的示意图,按下手柄上的按键或者摇杆,这里会显示手柄状态。(不要偷懒,每个键都按一下看看)。示意图我们可以看到分为滑条和方格按键。对应到程序里面呢,就是axes[]和buttons[]. 比如按下按键X,你可以看到方格3有反应,说明X按键对应的是buttons[3]。拨动左边摇杆,可以看到滑条0,1有反应,说明左摇杆对应的是axes[0]和axes[1]。不同的手柄可能对应到的实际值不一样,这个以实际情况为准。

下一单元运行,可以看到程序将会创建两条链接,分别将axes[1]和axes[3]的值链接到小车左右电机的值。所以为什么叫你们每个按键都按一下呢,这里就用到了,如果你测试过了,就会知道axes[1]是对应着左摇杆的值,而axes[3]没有对应的摇杆或者按键, 如果你没有测试,那就现在测试一下吧。如果你直接运行的话,你就会发现小车完全不受控,就是因为链接的按键有问题。


为了方便操控,我们将他改成buttons[]控制。因为是控制左右的,所以我们用X按键控制左轮,用B按键控制右轮。当然,你也可以自行试一下摇杆的效果。经测试发现,X按键对应buttons[3], B按键对应buttons[1]。修改之后运行程序,测试一下手柄吧

测试的时候你会发现,小车转弯的动作非常的大,这里是因为创建链接中,直接将按键的值传给了电机,也就是要么是0,要么是满值1。为了让小车温和一点,我们将这个值改小,将速度降低一半。然后再试试吧。


如果你不满足于左转右转,你也可以在后面加入程序,让它控制小车前进和后退。

这里我们加入了四条语句,分别是如果检测到buttons[0]或者buttons[2],就设置小车的左右电机运转(前进和后退运转的方向刚好是相反的)


链接摄像头

前面说了很多都是电机,车轮。这里终于到摄像头了。(*^▽^*)

运行单元7,创建一个图像窗口,图像格式为jpeg,分辨率为300x300,并显示在当前页面。这里我们只是创建了一个图像窗口,但是图像内容我们还没有定义,所以显示的结果是空图像(大家不要急,不是摄像头坏了╮(╯_╰)╭)。为了方便后面查看,这里我们右键让窗口单独显示。(不要问我怎么操作,你是不是没有看前面的章节╭(╯^╰)╮)


运行单元8,我们引入Camera类,并同时调用摄像头。


运行单元9。调用bgr8_to_jpg类,将摄像头的图像实时解码转换成jpeg格式,并链接到我们刚刚创建的图像窗口上。这里采用的是dlink链接。这个我们前面就有讲过了,dlink方法创建的是一个单向链接,可以显示小车的状态。

再次查看刚刚创建的图像窗口,是否有图像输出了呢。(看看我的JetBot2号^_^)。可能有小伙伴会疑惑,为什么你的画面看起来跟我的图像不一样。那是因为我用的摄像头跟你们不一样呀(所以不要在意这种细节╮(╯_╰)╭)

这时候你就可以使用你的手柄,控制小车到处浪了。赶紧试一下吧。


心跳处理

没有错,又是心跳程序。

运行单元10,让小车在网络断开的时候停下。我们控制小车的时候,如果突然间断开了网络连接,小车会保持最后一个状态一直运行。为了避免小车在断开链接的时候刚好在运动出现卡死或者撞墙的情况,这里我们通过心跳程序来处理,在检测到心跳停止(小车和网页端失去同步的时候),将小车和网页的链接全部断开,并将小车设置为停止状态。


后面一个单元是如果你的小车电机和摄像头如果断开链接了,可以重新运行程序链接上。请注意我们改过电机链接的方法,这里也要跟着改,不然运行后控制效果会不一样。如果前面你都正常跑,并且也没有处于好奇测试小车断开网络的时候会不会停下,你也可以忽略这一单元(你看我就没有运行它,改都懒得改╮(╯_╰)╭)。


那如果你试了断网效果,然后想重新链接怎么办。你可以简单粗暴,重新shutdown再来一遍,也可以运行单元11让他重新链接一下。(好吧,还是要演示一遍的╮(╯_╰)╭)


截图

在带着小车到处浪的时候,如果遇到好看的画面,不截图一下都对不起前面的折腾。这一单元就是让你能够利用手柄上的按键一键截图的。

首先,创建一个命名为snapshots的文件夹在本地目录下。运行完你就会发现了。

然后写一个save_snapshot函数,保存截图。如果按键事件触发,就把当前的图片保存到snashots目录下。而截图键呢,程序默认用的是buttons[5],我试了试,这个按键很合意,所以就保留默认设置了。试试吧。


题外话

今日份的题外话在最后。

题外话是关于心跳程序。这里其实应该在上一章节讲的,笔者偷懒了,这里补充回来吧_(:з」∠)_。

在单元10中我们利用心跳程序来判断小车是否处于联网状态,程序在检测到heatbeat的状态是dead的时候会运行处理程序,那在什么情况下heatbeat的状态会是dead呢?

打开jetbot/jebot路径下的heatbeat.py文件。这里我们主要注意的是两个部分,一个是初始化中对于pulseout和pulsein值的处理,这里两者都是获取当前的时间值。同时我们注意都这里设置了一个widegets链接,链接方式是jsdlink.创建javascript,单向连接。将pulsein和pulseout的值同步。结合这里和run里面的设置你会发现,pulsein是只在这里做了一次初始化,然后pulseout在run中每隔一个period的时间会做一次更新,更新当前的时间值。这里的链接就是用来同步pulsein和pulseout,因为这个链接是依托于web的链接的,如果说小车wifi断开的话,那么链接就会失效,也就意味着pulsein和pulseout的同步会停止,那么pulsein和pulseout的值就会出现差值。

另一个需要注意的就是关于heatbeat状态的判定。heatbeat的状态是通过pulsein和pulseout的差值来判断的。如果小车断开wifi连接的话,pulseout和pulsein会失去同步。这时候差值就会在一次period之后大于设定的period值。这种情况下我们就把heatbeat的状态判定为dead。


补充:关于widgets的链接方法,可以参考这个页面(点我跳转