スポンサーリンク

RaspberryPi

MQTTでドローンの自動操縦を考える(1)

ドローンの自動操縦への一歩として、前回、


を紹介しましたが、

今回は、遠隔地から、どのようにドローンの状態(センサー値)を取得、
または遠隔操作ができるかを検証してみます。
使う技術はMQTTプロトコルというものです。

ドローンと操作端末をインターネット経由で接続し、
双方向に低レンテンシーで通信できるエコな手段は何がよいか?
を模索し、辿り着きました。

具体的に何をしようとしているかと言うと、
ドローンのセンサーデータ: 傾き(X軸、Y軸)、針路、高度、昇降速度を
インターネット経由でリアルタイムにローカルPCで表示させます。

イメージとしては、こんな感じです。
scrs20160718

MQTTの登場人物は
パブリッシャー、ブローカー、サブスクライバーの三者になります。

パブリッシャー: 情報の送り主
ブローカー: 情報を全て仲介する
サブスクライバー: 情報の受取主

それぞれ、トピックという付加情報で、パブリッシャーから発信された情報が
適切なサブスクライバー(複数)に伝達されるという仕組みですね。
大雑把に言うと。

今日のIoT技術にはなくてはならない存在になっているようで、
Facebook MessaangerもMQTTを利用しているそうですよ。

今回作ろうとしているのは、
ドローンから定期的にパブリッシュされたデータを
ローカルPCで受信するという要件ですので、

■ パブリッシャー
・・・フライトコントローラーと接続したRaspberryPi
■ ブローカー
・・・Heroku上のアドオンCloudMQTT
■ サブスクライバー
・・・ローカルPCのスタティックHTML

という構成を考えました。
それぞれについて、ブローカー、パブリッシャー、サブスクライバーの順で
構築手順を紹介します。

MQTTブローカー

MQTTブローカー(サーバー)は
自分でサーバを用意してインストールして管理する方法と
外部のサービスを利用する方法があります。

今回は、なるべく楽をするために、
MQTTサービスを提供しているクラウドサービスを探していると
HerokuでCloudMQTTというアドオンを見つけました。

このアドオンは、とても使いやすいので
Herokuアカウントがあれば、3分でMQTTブローカー(WebSocket付き)
を準備できます。

Herokuアプリの作成

Herokuにログイン
https://id.heroku.com/login

※アカウントは無料で作れます。
https://signup.heroku.com/login

ログインしたら、新規アプリ作成しますので、
“Create New App”をクリックします。

screen20160710231821

アプリ名を適当に決めて入力し、”Create App”ボタンをクリックすれば、
アプリ作成完了です。

screen20160710232432

CloudMQTTアドオンの追加

次に、作成したアプリにアドオンを追加しますので、
“Resources”タブから、Add-onsの検索画面に
“CloudMQTT”と入力すると、候補が出てきますのでクリックします。

screen20160710232459

料金プランを聞かれますので、無料のCute Catプランを選択。
※CuteCatプランの制限は、クライアントの最大接続数が10までとなります。

“Provision”をクリックすると、、

screen20160710232513

瞬時に、サービスが立ち上がり、接続するための各種情報が表示されました。
こんなに早く、簡単に、無料でサービスが使える感覚はたまらないですね。

screen20160710232540

ここに表示される
MQTTサーバ名、ポート番号、WebSocketポート番号
は、後ほど設定で使用しますので、控えます。

ユーザの作成

もう使える状態なのですが、
一応、接続するための専用ユーザを作りたいと思います。
Manage Usersにユーザ名とパスワードを入力し、Saveをクリック。

screen20160710232737

すると、もうユーザ登録ができたようで、
ユーザ一覧に表示されます。

screen20160710232754

アクセス権限の付与

続いて、アクセス権限の設定です。
作成したraspberrypiユーザに、”topic/drone”というトピック名でのメッセージに対し、
Read、Writeの権限を付与します。

screen20160710232911

Readというのは、サブスクライブしてよいか?
Writeはパブリッシュしてよいか?
の権限のことです。

こちらもまたすぐに、ACLsに登録内容が表示されます。

screen20160710232921

あまりにも簡単でしたが、
以上で、MQTTブローカー構築完了。

パブリッシャー

SP Racing F3とRaspberryPiをUSBケーブルで接続します。
そして、フライトコントローラーのセンサーデータを取得するため、
RaspberryPiで簡単なプログラムを用意する必要があります。

センサーデータ取得は、おなじみMSP(MultiWiiProtocol)
http://www.multiwii.com/wiki/index.php?title=Multiwii_Serial_Protocol
のメッセージID108,109を使い、機体の傾き、針路、高度、昇降速度を取得します。

プログラムはNode.jsで書きました。

nodeの必要なモジュールは

mqtt ・・・ MQTTプロトコルでの通信に必要
serialport ・・・ フライトコントローラーとの通信に必要

です。

まず、お決まりのnodeの初期化をし、スクリプト”test.js”を編集します。

npm init
npm install mqtt --save
npm install serialport --save
emacs test.js

test.jsの中身はこんな感じです。
MQTTサーバ名、ポート番号、ユーザ名、パスワード
設定した値に置き換えてください

//MQTTサーバとの接続                                                                          
var mqtt = require('mqtt');
var url = 'mqtt://MQTTサーバ名';
var options = {
    port: ポート番号,
    clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
    username: 'ユーザー名',
    password: 'パスワード'
};
var client = mqtt.connect(url, options);
client.on('connect', function() {});

//フライトコントローラーとの接続                                                              
var interval = 100;
var serial = require('serialport');
serial = new serial('/dev/ttyUSB0',{baudrate:115200});
serial.on('open', function(){
    console.log('open FC');
    serial.on('data',function(data){
        myreadResponse(data);
    });
    setInterval(function(){
        sendRequest(108);
        sendRequest(109);
    }, interval);
});
serial.on('error', function(err){
    console.log('error : ' + err);
});

//フライトコントローラーへのリクエスト                                                        
function sendRequest(type){
    var sl1 = 0x24;
    var sl2 = 0x4D;
    var sl3 = 0x3C;
    var sl4 = 0;
    var sl5 = type;
    var crc = sl4 ^ sl5;
    var buf = new Buffer(6);
    buf.fill(sl1, 0);
    buf.fill(sl2, 1);
    buf.fill(sl3, 2);
    buf.writeUInt8(sl4, 3);
    buf.writeUInt8(sl5, 4);
    buf.writeUInt8(crc, 5);
    serial.write(buf, function(err, result){});
}

//フライトコントローラーからのレスポンス                                                      
function myreadResponse(data){
    var buf = new Buffer(data, 'binary');
    var sl1 = buf.readUInt8(0);
    var sl2 = buf.readUInt8(1);
    var sl3 = buf.readUInt8(2);
    var sl4 = buf.readUInt8(3);
    var sl5 = buf.readUInt8(4);
    var dat;

    //MQTTパブリッシュするメッセージ(JSON)を作成                                              
    var ret = '';
    if (sl4 > 0 && sl5 == 108){
        ret = '{';
        ret += '"roll":' + (0 - buf.readInt16LE(5) / 10) + ',';
        ret += '"pitch":' + (0 - buf.readInt16LE(7) / 10) + ',';
        ret += '"turn":' + (buf.readInt16LE(5) / 10) + ',';
        ret += '"heading":' + buf.readInt16LE(9) + '}';
    } else if (sl4 > 0 && sl5 == 109){
        ret = '{';
        ret += '"altitude":' + buf.readInt32LE(5) + ',';
        ret += '"ascent":' + (buf.readInt16LE(9) / 10) + '}';
    }
    if (ret.length > 2){
        client.publish('topic/drone', ret, function(){});
    }
}

処理内容は、100ms(0.1秒)おきに、フライトコントローラーに
センサーデータを送ってほしいリクエストを投げています。

フライトコントローラーから返事がきたら、内容を解析し、
JSON形式のテキストデータに変換し、MQTTブローカーへ
パブリッシュ!

ということをプログラム終了するまで繰り返すという内容です。

サブスクライバー(ローカルPC)

パブリッシャーでデータを送信するところまではできましたので、
受信して表示してあげるところをサブスクライバー側で準備します。

MQTTプロトコルはTCP/IP上のプロトコルであるので、
ブラウザと情報をやり取りをする場合は、
WebSocketを使用しなければできません。

CloudMQTTは標準で、WebSocketに対応してますので、
Javascriptで簡単にMQTToverWebSocketを利用できる
Pahoを組み込みます。

また、データを表示するのに飛行機の計器類に見立てた方がかっこよく、
モチベーションもあがるので、
Flight Indicators jQuery Pluginも使用します。

ローカルPCで、適当なフォルダを作成し、
Flight Indicator jQuery PluginのGithubページから
リポジトリを取得します。

git clone https://github.com/sebmatton/jQuery-Flight-Indicators.git

もしくは、
https://github.com/sebmatton/jQuery-Flight-Indicators/archive/master.zip
を直接ダウンロードし、解凍でもOKです。

ダウンロードしたjQuery-Flight-Indicatorsフォルダに移動し、index.htmlファイルを作成します。

cd jQuery-Flight-Indicators
emacs index.html

index.htmlの内容はこちら
MQTTサーバ名、WebSocketポート番号、ユーザ名、パスワード
を設定した値に置き換えてください

<html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="css/flightindicators.css" />
    <title>ドローン監視アプリ</title>
<style>
        body {
            margin: 0;
            background-color: #212121;
        }
        .container {
            width: 950px;
        }
    </style>
</head>
<body>
<div class="container">
<div class="content">
            <span id="airspeed"></span>
            <span id="attitude"></span>
            <span id="altimeter"></span>
            <span id="turn_coordinator"></span>
            <span id="heading"></span>
            <span id="variometer"></span>
        </div>
    </div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
    <script src="js/jquery.flightindicators.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        var airspeed = $.flightIndicator('#airspeed', 'airspeed', {size:300, showBox: false});
        var attitude = $.flightIndicator('#attitude', 'attitude', {size:300, showBox: false});
        var heading = $.flightIndicator('#heading', 'heading', {size:300, showBox:false});
        var variometer = $.flightIndicator('#variometer', 'variometer', {size:300, showBox:false});
        var altimeter = $.flightIndicator('#altimeter', 'altimeter', {size:300, showBox:false});
        var turn_coordinator = $.flightIndicator('#turn_coordinator', 'turn_coordinator', {size:300, showBox:false});
        var vl = {
            'roll': 0, 'pitch': 0, 'speed': 0, 'altitude' :0, 'pressure': 0,
            'turn': 0, 'heading': 0, 'ascent': 0
        };
        //MQTT接続
        client = new Paho.MQTT.Client("MQTTサーバ名", WebSocketポート番号, "idm" + Math.floor( Math.random() * 11 ));
        client.onConnectionLost = onConnectionLost;
        client.onMessageArrived = onMessageArrived;
        var options = {
            useSSL: true,
            userName: 'ユーザ名',
            password: 'パスワード',
            onSuccess: onConnect
        };
        client.connect(options);
        //MQTT接続が成功したとき
        function onConnect() {
            client.subscribe("topic/drone");
            setInterval(function(){
                    updateFlightIndicator();
                }, 15);
        }
        //MQTT接続が失敗したとき
        function onConnectionLost(responseObject) {
            if (responseObject.errorCode !== 0) {
                console.log("onConnectionLost:"+responseObject.errorMessage);
            }
        }
        //MQTTメッセージを受信したとき
        function onMessageArrived(message) {
            var mes = message.payloadString;
            var jsono = JSON.parse(mes);
            for(key in jsono){
                updateParams(key, jsono[key]);
            }
        }
        //変数値の更新
        function updateParams(target, value){
            vl[target] = value;
        }
        //計器の値を変更
        function updateFlightIndicator(){
            attitude.setRoll(vl.roll);
            attitude.setPitch(vl.pitch);
            airspeed.setAirSpeed(vl.speed);
            altimeter.setAltitude(vl.altitude);
            altimeter.setPressure(vl.pressure);
            turn_coordinator.setTurn(vl.turn);
            heading.setHeading(vl.heading);
            variometer.setVario(vl.ascent);
        }
    </script>
</body>
</html>

以上で、サブスクライバー側も完成です。

いざ、接続!

パブリッシャーであるRasberryPiから
作成したnodeアプリケーション”test.js”を起動します。

node test.js

そして、ローカル環境のPCから、作成したindex.htmlをブラウザで開くと、、、
scrs20160718

ドローンの状態に応じて、計器類が動きました!!!
インターネット経由なので、理論上は遠くはなれてたところからでも
ドローンの状態を監視できるということです。

次回は、これを拡張させて、遠隔操作に挑戦したいと思ってます!

※本記事中のスクリプトはGPLv3ライセンスです。ライセンスの範囲内で自由にご利用ください。

ピックアップ記事

  1. かっこいいアンテナホルダーを自作する方法
  2. ドローン練習に行ってきました #3
  3. フライトコントローラーSP Racing F3をRaspberryPiで操作
  4. QAV250をリニューアル制作
  5. MQTTでドローンの自動操縦を考える(1)

関連記事

  1. フライトコントローラー

    フライトコントローラー(SP Racing F3)にGPSモジュール(ublox neo-6m)追加…

    今回は、前からやりたかったGPSモジュールの追加です。GPSで位…

  2. RaspberryPi

    ラズベリーパイにパッケージインストール

    Raspberry Pi 3をどんどん自分仕様にしていきます。パッ…

  3. RaspberryPi

    ラズパイで3Gデータ通信をするには

    Raspberry Pi 3で、3Gデータ通信をしたいのでUSB型の…

  4. RaspberryPi

    ラズベリーパイはじめました

    秋葉原の千石電器に行き、前から気になっていたRaspberryPi3…

  5. RaspberryPi

    フライトコントローラーSP Racing F3をRaspberryPiで操作

    フライトコントローラーSP Racing F3をプロポではなく、Ra…

おすすめ記事

最近の記事

  1. トイドローンTelloでGo言語プログラミング!フロントカメ…
  2. DJIとIntelの最新技術の結晶 80gの小型ドローンTe…
  3. Parrot Bebop2のカメラ映像をリアルタイムにYou…
  4. Parrot bebop2をNode.jsで操作してみた
  5. プログラミングができる市販ドローン”Parrot…

月別アーカイブ

  1. その他

    ドローンはじめました。
  2. RaspberryPi

    MQTTでドローンの自動操縦を考える(1)
  3. Webプログラミング

    Parrot Bebop2のカメラ映像をリアルタイムにYouTube配信する方法…
  4. 市販ドローン

    プログラミングができる市販ドローン”Parrot Bebop2…
  5. ドローン練習

    ドローンシミュレーター”Liftoff”でレベルアップを…
PAGE TOP