200美元“造”出一台可由语音控制外加能进行人脸识别的自主飞行无人机
结合深度学习和便宜硬件的更多探奇

在“造”出能进行物体识别的机器人后,下一步就很清晰了,我要“造”一个能飞的东西!我决定搞一个能自主飞行的无人机,并要它能识别人脸和响应语音指令。

选择一台成品无人机

开始“黑入”一台无人机的最难的部分就是如何开始。我最初的选择是用零件组装一台无人机。但和我完成的大部分的DIY项目一样,从零开始一般都耗费了太多的时间,还不如直接买一个成品。实话实说,我自己组装的无人机从来没有正常的飞行过。而买一台成品机就既省事又省钱。

绝大部分无人机厂商都宣称提供API接口,但对于业余玩家来说就没有一个明确的最佳选择。大部分能提供貌似可用的API接口的无人机都要超过1000美元。这对于入门级的玩家来说就太贵了。

但经过搜索,我发现Parrot AR 无人机2.0版(见图1)是一个低端可“黑”入无人机的极佳选择。新的售价是200美元,不过很多人在买了以后就不怎么再玩了,所以在eBay上花130美元或更少就能买到一个不错的二手货。

image01-09009d8c12c503ade7a8ec4da3e6a199

图1:我车库里的无人机收藏。最左边的就是Parrot AR无人机。图片由Lukas Biewald授权使用

论飞行稳定性,Parrot AR型不如更贵的Parrot Bebop 2型(550美元)。但是Parrot AR型所带的叫node-ar-drone的node.js客户端库是非常好用的。

Parrot AR型的另外一个优点是它非常皮实、耐摔。在测试自主飞行代码的时候,我的无人机频繁地撞到墙、家具、植物甚至是我们家的客人,并坠落。但它还是依旧能正常飞行。

比起“造”能在地上跑的机器人,“造”无人机最不爽的一点就是它的电池可用时间太短了。基本上是充电几小时,飞行十几分钟。所以我建议多买两块电池,循环充电使用。

给我的无人机开发程序

我经过实践发现,Javascript内在的事件驱动的特性使得它成为一种非常好的控制无人机的语言。请相信我,无人机飞行的时候,是有非常多的异步事件发生。我写Node.JS的时间并不长,但在这个项目过程中,我对它的印象非常深刻。上一次我正儿八经地为机器人写程序用的是C语言。处理C语言的线程和异常是一件非常让人头疼的事,所以我尽量避免再使用它。我希望有人能为其他的机器人平台开发出JavaScript的接口,因为这会让为机器人开发程序(完全无法预知会发生什么事)变得更简单更有趣。

架构设计

我决定在我的笔记本上运行控制逻辑,同时在云端运行机器学习的部分。这种架构设计比在树莓派板上直接运行神经网络的延迟要低。我认为这种架构对于业余无人机项目而言是合适的。

微软、谷歌、IBM和亚马逊都提供快速且便宜的机器学习API。最终我选择了微软的认知服务API来完成这个项目,因为它是唯一提供定制化的人脸识别功能的API。

图2里展示了整个无人机项目的系统架构。

image02-25ab841b0c7c3ae831e5bf4ca0d0488b

图2:智能无人机的系统架构。图片由Lukas Biewald授权使用

开始动手

默认地,Parrot AR无人机2.0版自己可以提供一个无线网络,供客户端接入,但这个是功能却极度烦人。每次你要实验点东西,你都需要从本地网络断开,再连到无人机的无线网里。好消息是,有一个叫ardrone-wpa2的项目,非常有用,用它你可以“黑”进无人机,并让无人机连到你自己的无线网络里。

Telnet到无人机操作系统上并游荡一番是挺有趣的。Parrot无人机使用的是一个简化版的Linux操作系统。你上次Telnet到某个系统上是什么时间哪?下面的命令例子就演示了如何打开一个终端并直接登录到无人机的操作系统上。

% script/connect “The Optics Lab” -p “particleorwave” -a 192.168.0.1 -d 192.168.7.43

% telnet 192.168.7.43

通过命令行来控制飞行

在安装了node库之后,下一步就是生成一个node.js的命令行运行环境,然后就可以开始控制你的无人机了:

var arDrone = require(‘ar-drone’);

var client = arDrone.createClient({ip: ‘192.168.7.43’});

client.createRepl();

drone> takeoff()

true

drone> client.animate(‘yawDance, 1.0)

如果你按照我上面所说的一步一步地实验到这里,你的无人机肯定已经坠落过了——至少好几次。我已经无数次地把我的无人机的保护外壳给它粘回机身上,直到它彻底解体,随后我只好再买了一个新的。我不得不说,其实Parrot AR型在没有保护外壳的时候飞行得更好。但这种方式会使无人机变得很危险。因为没有保护外壳的话,一旦无人机撞到东西,它的螺旋桨就会直接打到物体上,并留下刮痕。

从网页上控制无人机飞行

为无人机开发一个基于网页的控制页面挺简单且效果不错。用如下所示的express.js框架就可以搭建一个很小巧的网页服务器。

var express = require(‘express’);

app.get(‘/’, function (req, res) {

res.sendFile(path.join(__dirname + ‘/index.html’));

});

app.get(‘/land’, function(req, res) {

client.land();

});

app.get(‘/takeoff’, function(req, res) {

client.takeoff();

});

app.listen(3000, function () {

});

我用下面的代码来通过一个按钮发送AJAX请求。

<html>

<script language=’javascript’>

function call(name) {

var xhr = new XMLHttpRequest();

xhr.open(‘GET’, name, true);

xhr.send();

}

</script>

<body>

<a onclick=”call(‘takeoff’);”>Takeoff</a>

<a onclick=”call(‘land’);”>Land</a>

</body>

</html>

从无人机上导出视频流

我发现把无人机上的摄像头拍摄的视频导出的最佳方法就是:建立一个持续的连接,并把摄像头拍摄的PNG图片发送到我的网站的网页上。通过使用AR无人机的库(见下面的代码),网页服务器不断地把无人机摄像头拍摄的PNG画面拉取出来。

var pngStream = client.getPngStream();

pngStream

.on(‘error’, console.log)

.on(‘data’, function(pngBuffer) {

sendPng(pngBuffer);

}

function sendPng(buffer) {

res.write(‘–daboundary\nContent-Type: image/png\nContent-length: ‘ + buff

er.length + ‘\n\n’);

res.write(buffer);

});

对从无人机获取的图像进行人脸识别

微软的Azure Face API系统很容易上手,且功能强大。你上传你朋友的照片给它,这个系统就能识别出他们是谁。它也能猜测人物的年龄和性别。我发现这两个功能的识别准确率是惊人的高。整个识别的延迟大概是200毫秒。识别1000次请求花费1.5美元。对我而言,这个价格对于这种应用是相当合理的。下面是我的代码,它实现了发送图片给API来做人脸识别的功能。

var oxford = require(‘project-oxford’),

oxc = new oxford.Client(CLIENT_KEY);

loadFaces = function() {

chris_url = “https://media.licdn.com/mpr/mpr/shrinknp_400_400/AAEAAQAAAAAAAALyAAAAJGMyNmIzNWM0LTA5MTYtNDU4Mi05YjExLTgyMzVlMTZjYjEwYw.jpg”;

lukas_url = “https://media.licdn.com/mpr/mpr/shrinknp_400_400/p/3/000/058/147/34969d0.jpg”;

oxc.face.faceList.create(‘myFaces’);

oxc.face.faceList.addFace(‘myFaces’, {url => chris_url, name=> ‘Chris’});

oxc.face.faceList.addFace(‘myFaces’, {url => lukas_url, name=> ‘Lukas’});

}

oxc.face.detect({

path: ‘camera.png’,

analyzesAge: true,

analyzesGender: true

}).then(function (response) {

if (response.length > 0) {

drawFaces(response, filename)

}

});

我用了ImageMagick库来对我收集的PNG图片做打标签,效果相当好。对于这个部分其实可以有很多的扩展可能。比如用一个情感API来识别人脸所表现出来的情感。

运用语音来控制无人机

进行语音识别部分开发的难点并不是识别本身,而是如何把语音流从运行在我本地服务器上的网页里转换成微软Speech API可以使用格式。下面的代码就是实现这个功能的。一旦你能把语音保存成单声道和以正确的采样频率采样后,这个语音识别API就能很方便地识别语音内容。这个API的花费是1000次请求4美元。对于业余应用来说,基本相当于是免费了。

RecordRTC是一个很好的库,可以用来作为以网页为客户端的语音采集的新手入门的工具。在客户端,我就加入了保存语音文件的代码。

app.post(‘/audio’, function(req, res) {

var form = new formidable.IncomingForm();

// 设定允许客户在一个请求里上传多个文件

form.multiples = true;

form.uploadDir = path.join(__dirname, ‘/uploads’);

form.on(‘file’, function(field, file) {

filename = “audio.wav”

fs.rename(file.path, path.join(form.uploadDir, filename));

});

// 记录发生的错误日志

form.on(‘error’, function(err) {

console.log(‘An error has occured: \n’ + err);

});

// 一旦所有文件上传完成,才给客户端发相应

form.on(‘end’, function() {

res.end(‘success’);

});

// 解析出请求里包含的表单数据

form.parse(req)

speech.parseWav(‘uploads/audio.wav’, function(text) {

console.log(text);

controlDrone(text);

});

});

我使用FFmpeg工具来降低音频的采样率,并把多声道合并成单声道,以供微软API使用。

exports.parseWav = function(wavPath, callback) {

var cmd = ‘ffmpeg -i ‘ + wavPath + ‘ -ar 8000 -ac 1 -y tmp.wav’;

exec(cmd, function(error, stdout, stderr) {

console.log(stderr); // command output is in stdout

});

postToOxford(callback);

});

尽管我开发的功能就是这些,但是还是可以继续扩展。比如用微软的文字变语音的API来让无人机说话!

开发自主搜索路径

我使用ardrone-autonomy库来为无人机开发自主搜索路径。在此过程中,我无数次地把无人机弄得撞到了客厅的家具和植物上。最后,我妻子很“客气”地建议我去车库里继续我的项目,因为那里没多少可以撞的东西。但是车库的地方有点小,使得操控空间有限。

image03-3c9a696f00e8775d000ada1c2e4ff311

图3:在我的“实验室”里试飞无人机。图片由Lukas Biewald授权使用

在我能有一个更大的实验空间后,我会尝试更智能的搜索算法。不过,现在我还是只会让无人机做起飞和旋转的动作,以此来搜索发现人,并识别是敌还是友。

var autonomy = require(‘ardrone-autonomy’);

var mission = autonomy.createMission({ip: ‘10.0.1.3’, frameRate: 1, imageSize: ‘640:320’});

console.log(“Here we go!”)

mission.takeoff()

.zero()         // 把当前状态作为参考基准

.altitude(1)

.taskSync(console.log(“Checkpoint 1”))

.go({x: 0, y: 0, z: 1, yaw: 90})

.taskSync(console.log(“Checkpoint 2”))

.hover(1000)

.go({x: 0, y: 0, z: 1, yaw: 180})

.taskSync(console.log(“Checkpoint 3”))

.hover(1000)

.go({x: 0, y: 0, z: 1, yaw: 270})

.taskSync(console.log(“Checkpoint 4”));

.hover(1000)

.go({x: 0, y: 0, z: 1, yaw: 0

.land()

全都搞定后的效果

看下面的视频。我让无人机起飞并去找我的朋友Chris:

结论

在一切都配置妥当之后,就可以通过API来控制无人机,获得拍摄到的视频图片,这一切都爽爆了!随着新的图像识别技术可供使用,可能的应用必将越来越多。比如,让无人机根据房屋平面图来刷墙。虽然Parrot无人机并不是设计来为在狭小空间(比如我的房子)里安全飞行的,但随着无人机越来越皮实,价格变得更低,我相信真正有用的应用将会进入爆发期。

微软的认知服务云API是相当得好用且便宜。最初我比较担心无人机所用的广角摄像头所拍摄的图片会影响人脸识别的准确度,另外螺旋桨的噪声可能会对语音识别产生干扰。但整体而言,这两个API的表现远超我的期望。同时处理延迟也低于我的预期。从架构设计角度来看,在云端运行机器学习实时图像处理似乎是一个奇怪的选择,但它可能会成为未来很多应用的架构选择。

 

Lukas Biewald

Lukas Biewald是CrowdFlower的创始人兼CEO。CrowdFlower始于2009年,是一个数据增强的平台,可以帮助企业获得随需的人力来收集、产生训练数据,以及参与人-机器学习循环的工作。 在从斯坦福大学拿到数学学士和计算机科学硕士学位后,Lukas领导了雅虎日本的搜索相关团队。随后他去了Powerset,作为一个资深数据科学家进行工作。2008年Powerset被微软收购。Lukas还被《公司》杂志评选为30位30岁以下的著名人士。 Lukas还是一位专家级的围棋选手。

Early aeronautics, 1818. (source: Library of Congress on Wikimedia Commons).