Google Maps Platform

IoT 対応のジオロケーションと Firebase

[この記事は、Ken Nevarez, Industry Solutions Lead at Google による Geo Developers Blog の記事 "Geolocation and Firebase for the Internet of Things" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]

GPS は位置情報サービスの要ですが、GPS ハードウェアのコストや消費電力を抑えたいケースや、市街地や建物内など GPS の精度に欠ける場所にあるデバイスを探すといったケースも存在します。

近年、アセットの追跡や盗難防止、使用状況の最適化、アセットの保守点検などを目的として、GPS の代わりに Google Maps Geolocation API を用いた IoT(モノのインターネット)アプリケーションが増えています。そこで、Industry Solutions プロジェクトの一環として、周辺の Wi-Fi ネットワークと Google Maps Geolocation API を使って自身の位置を特定できる IoT デバイスのプロトタイプ作成に取り組みました。この記事では、実装した機能の一部を紹介すると共に、皆さんがご自分でもプロトタイプを作成できるよう、その方法を簡単に説明します。

作成した IoT デバイスは、周辺の Wi-Fi を探してその結果(Wi-Fi ホットスポットとその信号強度)を Firebase Realtime Database へ書き込みます。次に、バックエンド サービスがこのデータを読み出し、Google Maps Geolocation API を使ってそのデータから実際の位置を導き出します。導き出された位置は地図上にプロットすることができます。

pasted image 0.png

デバイスのセットアップとローカルでの書き込み

これを実現するために、Intel EdisonをLinux ベースのコンピューティング プラットフォームとして使い、これに Sparkfun の Edison Blocks を加えました。なお、デバイスの作成には Intel EdisonBase BlockBattery BlockHardwareパックがそれぞれ 1 つずつ必要です。

IMG_20161207_1317317v2p.JPEG

Edison の開発は Intel XDK IDE を使えば簡単です。JavaScript でシンプルな Node.js アプリケーションを作っていきます。今回、3 つのライブラリ(データベース接続には Firebase、Wi-Fiネットワークの取得には wireless-tools/iwlist、デバイスのMAC 取得には macaddress)を使用しました。実装手順はそれぞれのリンク先のページを参照してください。

ステップ 1: デバイスの MAC アドレスを取得して Firebase に接続する

  function initialize() {
    macaddress.one('wlan0', function (err, mac) {
        mac_address = mac;
        if (mac === null) {
            console.log('exiting due to null mac Address');
            process.exit(1);
        }
        firebase.initializeApp({
            serviceAccount: '/node_app_slot/<service-account-key>.json',
            databaseURL: 'https://<project-id>.firebaseio.com/'
        });
        var db = firebase.database();
        ref_samples = db.ref('/samples');
        locationSample();
    });
}

上記のコードに含まれている 2 つのプレースホルダーは次のとおりです。

  1. service-account-key は Firebase コンソールで作成するプライベート キーです。コンソールの左上部にある歯車アイコンから「settings」を選択して、Generate New Private Key をクリックします。このキーを Edison の /node_app_slot/ ディレクトリに入れます。詳しくは、Firebase のドキュメントをご覧ください。
  2. データベース URLの中の project-id は、Google プロジェクトを Firebase と連携させた後、Firebase コンソールのデータベース ページに表示されます。

ステップ 2: 10秒毎に Wi-Fiネットワークをスキャンしてローカルに書き込む

  function locationSample() {
    var t = new Date();
    iwlist.scan('wlan0', function(err, networks) {
        if(err === null) {
            ref_samples.push({
                mac: mac_address,
                t_usec: t.getTime(),
                t_locale_string: t.toLocaleString(),
                networks: networks,
            });
        } else {
            console.log(err);
        }        
    });
    setTimeout(locationSample, 10000);
}

クラウドへの書き込み

上記の locationSample() は、検出可能なWi-Fi ネットワークを Firebase のデータベースに書き込む関数で、データベースはネットワーク接続時にクラウドと同期します。

補足説明: Firebase へのアクセス権と認証の設定では、デバイスを「サーバー」として設定しました。この設定方法については Firebase ウェブサイトをご覧ください。今回、認証情報の保存に関しデバイスのセキュリティは十分であると仮定しましたが、皆さんが実装する際にこの点が該当しない場合は、クライアント JavaScript SDK の設定手順に従ってください。

データベースではワークロード管理に 3 つのキュー(Wi-Fi サンプル キュー、ジオロケーション結果キュー、視覚化データ キュー)を使用します。ワークフローは次のとおりです。デバイスからのサンプルがサンプル キューに入り、これを使ってジオロケーションが生成され、生成されたジオロケーションはジオロケーション キューに入れられます。ジオロケーションは取得されて表示用にフォーマット化され、デバイスによって整理された後、出力が視覚化バケットに保管され、フロントエンドのウェブサイトがこれを使用するという流れです。

以下に、Firebase Database コンソール上に表示されるサンプル、ジオロケーション、デバイスによって書き込まれた視覚化データの例を示します。

Samples_iotepq6.PNG

データを Google App Engine で処理する

サンプルデータの処理の実行には、長期実行中の Google App Engine Backend Module と Java Client for Google Maps Services のカスタム版を使用しました。

補足説明: Firebase と App Engine を併用する場合は、必ず手動スケーリングを使用してください。Firebase はバックグラウンド スレッドを使って変更をリッスンしていて、App Engine は手動でスケーリングされたバックエンド インスタンスに対する長期のバックグラウンド スレッドしか許可しません。

Java Client for Google Maps Services は Google Maps API を利用する際に必要な大量の通信コードを扱い、エラー処理について公開されているベスト プラクティスおよびレート制限に従ったリトライ方針に従います。次に示す GeolocateWifiSample() 関数は、イベント リスナーとしてFirebase に登録されています。この関数はデバイスから報告される各ネットワークをループし、それをジオロケーション リクエストに組み込みます。

  private void GeolocateWifiSample(DataSnapshot sample,  Firebase db_geolocations, Firebase db_errors) {
    // initalize the context and request
    GeoApiContext context = new GeoApiContext(new GaeRequestHandler()).setApiKey("");
    GeolocationApiRequest request = GeolocationApi.newRequest(context)
            .ConsiderIp(false);
    // for every network that was reported in this sample...
    for (DataSnapshot wap : sample.child("networks").getChildren()) {
        // extract the network data from the database so it’s easier to work with
        String wapMac = wap.child("address").getValue(String.class);
        int wapSignalToNoise = wap.child("quality").getValue(int.class);
        int wapStrength = wap.child("signal").getValue(int.class);
        // include this network in our request
        request.AddWifiAccessPoint(new WifiAccessPoint.WifiAccessPointBuilder()
                .MacAddress(wapMac)
                .SignalStrength(wapStrength)
                .SignalToNoiseRatio(wapSignalToNoise)
                .createWifiAccessPoint());
    }
    ...
    try {
        // call the api
        GeolocationResult result = request.CreatePayload().await();
        ...
        // write results to the database and remove the original sample
    } catch (final NotFoundException e) {
        ...
    } catch (final Throwable e) {
        ...
    }
}

GeolocateWifiSample() 関数をイベント ハンドラーとして登録します。ジオロケーションの結果を処理して視覚化データを生成するそれ以外のリスナーも、同じようなパターンで作成します。

  ChildEventListener samplesListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        // geolocate and write to new location
        GeolocateWifiSample(dataSnapshot, db_geolocations, db_errors);
    }
    ...
};
db_samples.addChildEventListener(samplesListener);

データの視覚化

デバイスの位置の視覚化には、Google App Engine を使います。保存されたデータを Firebase から渡して、Google Maps JavaScript API を使って結果表示用のシンプルなウェブページを作成しました。index.html ページの中身は空の

とID “map” です。この

を初期化して Google Map オブジェクトを入れました。また、イベントハンドラーの “child_added” と “child_removed” を追加して、時間の経過に伴ってデータが変化したときにマップを更新するようにしています。

  function initMap() {
    // attach listeners
    firebase.database().ref('/visualization').on('child_added', function(data) {
        ...
        data.ref.on('child_added', function(vizData) {
            circles[vizData.key]= new CircleRoyale(map,
                                vizData.val().lat,
                                vizData.val().lng,
                                vizData.val().accuracy,
                                color);
          set_latest_position(data.key, vizData.val().lat, vizData.val().lng);
        });
        data.ref.on('child_removed', function(data) {
            circles[data.key].removeFromMap();
        });
    });
    // create the map
    map = new google.maps.Map(document.getElementById('map'), {
      center: get_next_device(),
      zoom: 20,
      scaleControl: true,
    });
    ...
}

API は位置だけでなく精度表示も返すため、精度のコンポーネントを示すカスタム マーカー(半径内の範囲が脈動する)を作成しました。

pasted%2Bimage%2B0%2B%25281%2529v7zu.PNG
2 個のデバイス(赤と青)と判明した位置の直近 5 か所

次に進むには

この記事では、インターネットに接続されたデバイス(ロボットからウェアラブル端末まで)を追跡するために、Google Maps Geolocation API を使った IoT デバイスの構築方法を紹介しました。App Engine の処理モジュールを拡張し、他の Google Maps API を使ってルート案内標高プレイスタイムゾーン情報などの地図データを提供することもできます。皆さん、ぜひ作成してみてください。

さらに、Firebase の代わりに Google Cloud Platform を使って同様のソリューションを実現することも可能です。手順はこちらの記事をご覧ください。