雷锋网按:本文作者赵智沉,Google软件工程师。来自知乎专栏:赵智沉的作坊。雷锋网获授权转载。
从买第一个Arduino套装开始,我接触机器人有好几年了,但直到最近才开始做完整的课题。期间有两项技能为我打开了新世界的大门:Python和Linux。他们背后,是强大的开源社区。掌握了这两样工具的工具(元工具),你感觉网上遍地是趁手的兵器。
上周在公司内部编程培训时,有一句话深得我心:我们是软件工程师,不是程序员。我们的工作不是写程序,而是合理使用工具解决问题。在Google,如果你觉得自己不得不从零开始写某项功能,只是你还没有找到相应的工具罢了。在开源社区更是如此。
这是一个遥控小车,通过红外遥控或无线键盘可以控制小车的行动和摄像头的角度。TensorFlow实时监测摄像头拍摄到的画面,语音读出它识别出的物体。所有代码都放在我的GitHub上。
这个想法不是我的原创,来自Lukas Biewald去年九月写的这篇博客。核心部分,TensorFlow识别摄像头图像并语音输出,是我司人工智能工程师Pete Warden的开源工作。和原博客不同的是,制作过程中我加入了Arduino作为机械总控,也了解了Arduino和树莓派对话的方法(串口通信)。期间用到了许多有用的技能和工具,在这里整理一下,欢迎同好们留言交流!
整个课题在命令行环境完成,没有图形界面。如果你不懂Linux系统,可能有些吃力。但是,你都开始玩机器人了,怎么能不学Linux呢?我是通过《鸟哥的Linux私房菜》自学Linux的,后来又尝试从源代码搭建Linux,终于克服在Windows系统环境长大产生的对命令行的抗拒心理。相信我,克服这个障碍,你将打开新世界的大门。何况,用命令行工作才显得更酷更极客,不是吗?除了Linux,你还要懂C++和Python来完成这个课题。
另外,这篇文章主要介绍电子部分,不讲机械和美工。如你看到的,这个小车丑破了我的审美底线,我没花心思在外观上。我希望以后做一些兼具美学和功能的电子课题,或许会和设计师朋友们合作!
首先,你需要一个最新款的Raspberry Pi,安装好定制的Linux系统,连上无线网。你还需要一个官配摄像头,并在树莓派中设置为可用。你可以将树莓派通过HDMI连接到显示器,但更方便的做法是ssh远程登录,这样你就不用在调试过程中反复地把树莓派从小车上拔线、取下、连屏幕、然后安装回小车了,你可以实时远程修改小车的内核。甚至,我的Arduino程序也是通过树莓派编写、上传、通信的,于是也免去了电脑连接Arduino的步骤,让一切更流畅无缝。
树莓派的Linux系统支持图形桌面,你可以使用RealVNC(用于Windows)或TightVNC(用于Mac)远程登录图形桌面。(这个课题里不需要)
这是课题的核心部分,反而操作起来最简单,因为一切都在这里写清楚了,按部就班就行。运行代码在这里。
注意:这里用了训练好的模型,即TensorFlow中预先给定了训练好的参数集,训练图片库是ImageNet。也就是说,小车识别出的物体只能是图片库里包含的labels,也没有“学习”的过程。
小车套件(robot chassis)很多,选你喜欢的一款。标准的套件包括一个基座,两组马达+轮子,一个万向轮,一个电池盒。这个课题不需要四驱,而且之后要用到的马达控制器可能只支持两个马达。我用的是张尧姐送给我的第一个DIY套件:一个戳了很多洞的木板和3D打印出来的轮子和连接部件。这个恐怕是萝卜太辣最早的套件,来自硅谷的创客空间。
现在,萝卜太辣正式出品的“起源”套件已很完善,线上也有成熟的教学资源。这个课题里用到的舵机和金属连接部件都来自尧姐送给我的第二个套件——“起源”套件。但从感情上来讲,那套粗糙的木板套件让我更亲近,符合“用最简单的材料实现原型”的理念。
电源:树莓派需要5V、2A电源,放在小车上的话需要一个电流足够大的充电宝。连接树莓派和Arduino的连接线同时也为Arduino供电。但是,马达我用了外部电源(电池盒)。你会发现即使没有外部电源,充电宝依然可以带动马达(尽管很慢)。但是,好的习惯是机械部分独立供电;逻辑电路部分由充电宝提供。
下一步,操控小车。这里有两个方案,第一个不需要Arduino。我使用的是第二个。
我认为单片机的精髓,不是尺寸小,而是丰富的GPIO(General Purpose Input-Output),它们是程序与外部世界对话的窗口。你看到的各种电子部件、探头、焊接、面包板,都是在和GPIO打交道。你需要了解基本的电路知识,也需要知道它们在单片机上的排布。树莓派有一个非常好用的GPIO Python库:gpiozero,使用方法一目了然。
通常用四个端口控制马达,分别连接两个马达的正负级,通过每个马达的正向/逆向旋转来实现小车的前进/后退/转向。实现双向电流的标准电路模型是H桥接。你可以选购一款最基本的H-bridge模块。
因为我手头没有H桥接,所以这个方案我没有实现。
我没有H桥接,但有一个用于Arduino的Motor stacking shield,即Arduino上的H桥接。于是我干脆用Arduino负责机械(马达+舵机),相当于身体;树莓派只负责图像识别,相当于大脑。
Arduino不是Linux系统,不能直接ssh进去写程序,需要在外写好后编译上传。我用数据线连接了树莓派和Arduino,在树莓派上写好程序后上传。我发现一个非常好用的命令行IDE:PlatformIO(也有很棒的图形界面编辑器)。Linux上的安装过程基于Python 2.7。你需要一些初始化,如果像我一样是Arduino Uno主板,输入以下命令即可:
pio init -b uno
Arduino的C++源代码在这里。进入这个文件夹后,输入以下命令即可上传:
pio run –target upload
后来我发现PlatformIO对于Arduino主板好像不支持C++11,如果你有这个需要,可以考虑inotool。
同样有两个方案:无线键盘,红外遥控。两个方案我都实现了。
如果你在上一步用了3.1,无线键盘操控模块就可以直接嵌入到机械操控代码中(我没有实现)。如果你在上一步用了3.2,那么需要在树莓派上将按键操作转为机械控制信号(文本形式),通过串口通信(Serial Port)操控Arduino。
python代码在这里,用到了我自己写的库,用来检测键盘按键。这个库将单次按键匹配到前进/后退/转/停止等行为;但我希望实现的是长按键前进/后退/转,不按键时停止。但我始终没有找到现成的库(Update:据说在PyGame里有)。
后来我试着通过背景线程(threading)和系统延时的方法写了一个库,但效果不太理想,系统延时和程序运行时间带来的误差总是匹配不好,就放弃了。现在代码里用的是单次按键行动/停止的方案。如果读者有好的库,请推荐!
有一点要注意,使用串口通信前需要disable login(既然你已经ssh远程登录了),这篇解释比较清楚。
红外的长按返回的是一个单独的值(REPEAT),这点就可以让我很容易实现“长按-车动,不按-车停”。此外,红外遥控的代码直接写在Arduino的C++代码里,不需要通过树莓派和串口通信,更符合Arduino作为机械总控的设计原则。
PlatformIO不自带红外的库,我用的是这个。PlatformIO使用第三方库实在太简单了,不需要下载安装,直接在配置里加上GitHub链接即可,参考我的配置文件。
还有一点,每一个红外遥控都不一样。家里的电视机、音响、空调遥控都可以用,你只需要在使用前匹配好按键和对应的码。我在代码里define的一堆KEY只适用于我的遥控器。你可以用这个代码来获得键码。注意:红外遥控有几种模式,我的遥控用的是最常见的NEC模式,如果你匹配出来一堆乱码,可以考虑库里的其他几个模式。
对了,如果你用红外遥控,你还需要在车上装一个IR Receiver。我装在Arduino上,用8号端口。
如果你用了方案3.1,你也可以直接将IR Receiver装在树莓派的GPIO上。
这些已经够你开车上路了。我在车上装了舵机(Servo),控制摄像头的上下转动。操作很直观,看代码就能理解。我没有装超声探头,这个可以帮助你检测障碍,在撞墙前强行停止。
如果你想远程看摄像头的实时画面,VNC不能胜任。可以考虑这个方案。但这样的话TensorFlow就不能再用摄像头了。应该有一个共用的方案,我没有探究。
差不多就这些了,我的代码没有太多注释,等有空往上加。如果你有疑问,可以留言问我。
福利:这里有一个延时摄影的简单程序,我在crontab里设置为每隔一分钟拍一张照,然后每天半夜将当天拍的照转成录像。下周打算带到公司里,找个风景好的位置,放几天,拍纽约的24小时风景。