ミニドローンをRaspberry Piと接続してLeap Motionで飛ばす

Tangoアプリ開発の裏で密かに進めていたドローン制御プロジェクトが（物理的な意味で）ようやく動き始めたので、投稿します。


## 目的
当座の目標は、プログラム制御でドローンを自由自在に飛ばすこと。
最終的なゴールとしては、自律飛行とサイコミュによる操作ができるようになることですかね。


## システム構成
### ハードウェア
- Parrot MAMBO
- Raspberry Pi3 Model B
- BUFFALO WLI-UC-GNME
- MacBook Pro (13-inch, Mid 2010)
- Leap Motion

### ソフトウェア
- Raspbian Jessie with PIXEL
- Node.js v6.9.4
- noble 1.7.0
- rolling-spider 1.5.2
- leapjs 0.6.4



## 開発記録
### ドローンを手に入れる
兎にも角にもドローンが必要です。

色々調べたところ、首都圏ではドローンを飛ばせる場所がほとんどないらしく、[規制の対象外](http://www.mlit.go.jp/koku/koku_fr10_000040.html)となる200g未満のミニドローンから始めるのが良いらしい。
さらに調べると、Parrot社のドローンはエントリーモデルもSDKで操作でき、Node.jsからBluetooth接続した実績も報告されています。
http://qiita.com/tkyko13/items/02f74180daac05254367
https://liginc.co.jp/187633

上記を踏まえ、ドローン初号機は以下を購入することにしました。
[Parrot MAMBO](https://www.parrot.com/us/minidrones/parrot-Mambo#parrot-mambo-)

![IMG_1395.jpg](https://qiita-image-store.s3.amazonaws.com/0/153460/142f92bd-8864-39df-580d-12200f58a913.jpeg)

換装可能な**キャノン砲**と**ロボットアーム**がロマンです。


### MacBookから操作する・・・はずだった
ドローンを調達できたので、まずは公式のiOSアプリで動作確認。
https://itunes.apple.com/jp/app/freeflight-mini/id1137022728

アプリだからかもしれないけど、意外と操作が難しい・・・。
プロペラがウィンウィン鳴ってます。

しばらく触って、基本的な動きを理解したところで、次はいよいよNode.jsからの操作。
さくっと動かす・・・つもりが、nobleが以下のエラーを出している。

```shell-session
$ sudo node find.js 
/Users/shigemitsu/drone/node_modules/noble/lib/noble.js:76
      throw error;
      ^

Error: Could not start scanning, state is unsupported (not poweredOn)
    at Noble.startScanning (/Users/shigemitsu/drone/node_modules/noble/lib/noble.js:71:17)
    at Noble.start (/Users/shigemitsu/drone/find.js:14:9)
    at emitOne (events.js:96:13)
    at Noble.emit (events.js:188:7)
    at Noble.onStateChange (/Users/shigemitsu/drone/node_modules/noble/lib/noble.js:60:8)
    at emitOne (events.js:96:13)
    at NobleBindings.emit (events.js:188:7)
    at NobleBindings.<anonymous> (/Users/shigemitsu/drone/node_modules/noble/lib/mac/yosemite.js:78:8)
    at emitOne (events.js:96:13)
    at NobleBindings.emit (events.js:188:7)
```


**調査結果：**
**MacBookが古すぎて、Bluetooth 4.0に対応していません。**

**なんてこった…(´・ω・｀)**

プロジェクトは一時中断を余儀なくされます。


### 仕方ないので、Raspberry Piを買う
まあ、前から欲しかったし。ここでBluetoothドングルに逃げたら、負けかなと思った。
![IMG_1388.jpg](https://qiita-image-store.s3.amazonaws.com/0/153460/ad239ccd-798f-3261-5616-7d4583be4a7e.jpeg)

WiFiドングルは、ラズパイでの動作実績が報告されている[BUFFALO WLI-UC-GNME](http://buffalo.jp/product/wireless-lan/client/wli-uc-gnme/)を装着。他に、USBマウス、キーボード、電源とモニタを繋いで使っています。
![IMG_1397.jpg](https://qiita-image-store.s3.amazonaws.com/0/153460/4f8534fb-beab-6858-f11c-e06fd7a18aae.jpeg)


OSインストールは[NOOBS](https://www.raspberrypi.org/downloads/noobs/)を使って、滞りなく完了。
日本語化やWiFiへの接続も簡単にできました。


### 改めて、Node.jsでドローンを飛ばす
ラズパイでNode.jsの環境を作ります。
apt-getでインストールしたらバージョンが古かったので、以下の記事を参考にして最新版にアップデートしました。
http://qiita.com/da1fujimoto/items/8f66accafb6b86bf8d56

```shell-session
$ sudo apt-get update
$ sudo apt-get install -y nodejs npm
$ node -v
v0.10.29
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt-get install -y nodejs
$ node -v
v6.9.4
```

作業用ディレクトリを作成して、`npm install`で必要なライブラリをインストールします。

```shell-session
$ mkdir -p work/drone
$ cd work/drone
$ npm install noble rolling-spider keypress
```

WARNがいくつか出ましたが、細かいことは気にしない強靭なメンタルで動作確認してみます。

```shell-session
$ sudo node find.js
------1台目:
　xxxxxxxxxxxx Mambo_XXXXXX
1: Mambo_XXXXXX (xxxxxxxxxxxx), RSSI -62
```

無事、デバイスを見つけることができました。
上記のUUIDに接続して、keypressイベントで操作。

```shell-session
$ sudo node app.js
Configured for Rolling Spider! Mambo_XXXXXX
Mambo_XXXXXX => SESSION START
got "keypress" => { name: 't',
  ctrl: false,
  meta: false,
  shift: false,
  sequence: 't' }
takeoff
```

**飛びました！ヽ(=´▽`=)ﾉ**


### Leap Motionで操作した方がスタイリッシュなはずだ
Node.jsで接続できたのは良いけど、キーボード操作だけじゃ芸がないよなぁ・・・と考えていたとき、目に付いたのが、机の端に転がっていた[Leap Motion](https://www.leapmotion.com/)です。
手の動きを検知したら、フライトコントローラには不可能な動きができるかもしれない。

Leap Motionの公式にも、ラズパイやParrot社のドローンと連携した事例が紹介されているので、利用には問題ないでしょう。
https://gallery.leapmotion.com/leap-motion-raspberry-pi-2/
https://gallery.leapmotion.com/cylon-js-parrot-ar-drone/


### MacBookをWebSocket Serverとして使う
Leap Motionをラズパイに直接接続することはできないようで、結局MacBookが必要になりました。
以下のサイトを参考にして設定を行っています。
http://www.pentacreation.com/blog/2016/10/161008.html

ネットワークとしては、MacBookを有線でルータに繋いで、WiFiテザリングの設定にし、Leap MotionをUSB接続します。
ラズパイは有線接続なしで、MacBookのWiFiに接続、WiFi経由のWebSocketでLeap Motionにリモート接続しつつ、Bluetoothでドローンとペアリングします。

**【2017/2/20 追記】**
WiFiテザリングしなくても、同じWiFiルータに繋いで、MacBookのプライベートIPを指定すればOKでした。


### Raspberry PiでLeap Motionのデータを利用する
ラズパイにleapjsを追加で`npm install`します。

```shell-session
$ npm install leapjs
```

leapjsのAPIリファレンスを見ると、コンストラクタでWebSocket ServerのIPアドレスとポート番号を指定できるようになっており、接続先だけ変えればlocalhost接続時と同様に利用できます。
https://developer.leapmotion.com/documentation/javascript/api/Leap.Controller.html

gestureイベントをドローン制御に紐付ける実装で試してみます。

```test.js
var RollingSpider = require("rolling-spider");
var Leap = require("leapjs");

var ACTIVE = true;
var d = new RollingSpider({uuid:"xxxxxxxxxxxx"});

d.connect(function () {
    d.setup(function () {
        console.log('Configured for Rolling Spider! ', d.name);
        d.flatTrim();
        d.startPing();
        d.flatTrim();
        setTimeout(function () {
            console.log(d.name + ' => SESSION START');
            ACTIVE = true;
        }, 1000);
    });
});

var controller = new Leap.Controller({
    host: '[WebSocket ServerのIPアドレス]',
    port: 6437,
    //ラズパイではframeEventNameを無指定にした方がよい
    //frameEventName: 'deviceFrame',
    enableGestures: true
});

controller.connect();

controller.on('gesture', function (gesture) {
    switch (gesture.type) {
        case 'circle':
            onCircle(gesture);
            break;
        case 'swipe':
            onSwipe(gesture);
            break;
    }
});

function onCircle(gesture){
    if (gesture.state === 'stop') {
        console.log('circle => takeoff');
        d.takeOff();
    }
} 

function onSwipe(gesture){
    if (gesture.state === 'stop') {
        console.log('swipe => landing');
        d.land();
    }
} 
```


### Leap Motionで飛ばしてみた
動画に撮りました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Leap Motionでドローンを飛ばすテスト <a href="https://t.co/tGTeRbLsS8">pic.twitter.com/tGTeRbLsS8</a></p>&mdash; jyuko (@jyuko49) <a href="https://twitter.com/jyuko49/status/826474023508807680">2017年1月31日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

ラズパイでNode.jsのプログラムを実行して、MacBookに接続されたLeap Motionのデータを取得、Bluetoothでドローンに命令を送っています。
指で宙に円を描くジェスチャーに何度か失敗したので、あまりスタイリッシュには見えないかも。

**っていうか、急に飛ぶからビックリした(´ﾟдﾟ｀)**


**【2017/2/20 追記】**
急に飛んだように見えたのは、ジェスチャーの問題ではなく、ラグが発生していました。
frameEventNameの設定を'animationFrame(60fps)'にするとラズパイのCPUでは処理が追い付かず、'deviceFrame(処理能力に応じたfps)'にしていたのですが、この設定だと大きなラグが出ることがあります。
結果として、いずれも指定せずに接続すると反応速度が改善しました。


## まとめ
察しの良い方はお気付きと思いますが、お使いのPCがBluetooth 4.0に対応していれば、Node.jsで完結するので、Leap Motionでドローンを操作するのにラズパイは要らないです。
とはいえ、ラズパイでBluetoothやWebSocketを扱う方法がわかったので、無駄ではないと信じたい。

今回は疎通確認を目的に、gestureイベントで簡単に実装しましたが、フレームごとの手の動きにドローンを追従させるようチューニングを行って、もっと自在に飛ばせるようにしたいです。