Node-REDとクラウドを用いたセンシングデータのAR可視化サンプル


はじめに

温度や湿度、水の流量、匂い、ガス濃度などセンサーによる計測(センシング)データをVRやARを使って可視化すること自体は技術的には新しくありませんが、従来は専用のSDK(ソフトウェア開発キット)について知らないといけなかったため、敷居が高いものでした。そこで、IoTの世界では標準ツールとして、プログラミングを学ぶ人にとっては、小中学生からのロボット操作やAI(人工知能)の学習に採用実績のあるオープンソースソフトウェア「Node-RED」を使って、離れている場所のセンサーなど、リモートセンシングデータの可視化をさくっと実現します。

使用サービス

IBM Cloud ライト・アカウント https://www.ibm.com/jp-ja/cloud/lite-account
無料かつクレジットカード不要で利用できるクラウドサービスです。IBM Cloud ライト・アカウントを使用し、Webブラウザ上でプログラミングを行い、動作確認まで行います。

作るもの

A-Frameを用いてセンサーの値の変化に合わせて、ARコンテンツの色が変わる仕組みを実装します。

A-Frame

Mozillaが作っているVR/ARコンテンツのためのフレームワークで、HTMLとJavaScriptでVR/ARコンテンツを作ることができます。
A-Frame ドキュメント https://aframe.io/docs/1.0.0/introduction/

開発環境

無料かつクレジットカード不要で使える「IBM Cloud ライト・アカウント」にて、ビジュアルプログラミング環境の「Node-RED」を使用します。サンプルを使えばコードを書かなくても動作確認可能です。
また、作業に使用するWebブラウザは、Google Chromeまたは最新版のMicrosoft Edgeで確認済みです。

使用するセンサー

センサーは、IBM Watson IoTの仮想センサーを用いています。温度や湿度、水の流量センサーなどを持っている場合は、そちらを使っていただいても構いません。センサーを持っていない方を想定し、仮想センサーを使用しています。

Node-REDの準備

作業に用いるNode-RED環境の準備には、2020年4月 Node-REDハンズオン (Node-RED環境の準備)をご確認ください。

Node-REDに、センサーデータを扱うためのノード(部品)を追加

Node-REDの画面で、画面右上の「三」>>「パレットの管理」>>「ノードを追加」の順にクリックします。

下図のように「ibmiotapp」と検索します。
「node-red-contrib-scx-ibmiotapp」が表示されます。「ノードを追加」をクリックします。

下図のように表示されますので、「追加」をクリックします。

しばらくまちます。成功すれば、「ノードをパレットに追加しました」とメッセージが表示されます。
画面左側に、「ibmiot in」と「ibmiot out」ノードが追加されます。今回は、「ibmiot in」ノードを使用します。

サンプルをNode-REDで読み込み

下記を端から端までコピーし、Node-REDの「読み込み」を使ってインポートしてください。

[{"id":"ad1edd4d.f8676","type":"tab","label":"AR x Sensor demo","disabled":false,"info":""},{"id":"1ce3085e.838b88","type":"http response","z":"ad1edd4d.f8676","name":"","statusCode":"","headers":{},"x":930,"y":320,"wires":[]},{"id":"30a7ca7.9437f36","type":"template","z":"ad1edd4d.f8676","name":"A-Frame","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n  <head>\n    <script src=\"https://aframe.io/releases/1.0.4/aframe.min.js\"></script>\n    <script src=\"https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js\"></script>\n    <script>\n        const timer = 60000    // reload interval time: millisecond(ms) , 1minuts = 60000ms\n        window.addEventListener('load',function(){\n        setInterval('location.reload()',timer);\n        });\n    </script>\n  </head>\n  <body>\n    <a-scene embedded arjs>\n    <a-marker preset=\"hiro\">\n      <a-text value=\"Hello World!\" position=\"0 4.4 0\"></a-text>\n      <a-box position=\"0 0.5 0\" color=\"{{payload.level1}}\"></a-box>\n      <a-box position=\"0 1.6 0\" color=\"{{payload.level2}}\"></a-box>\n      <a-box position=\"0 2.7 0\" color=\"{{payload.level3}}\"></a-box>\n    </a-marker>\n    <a-entity camera></a-entity>\n    </a-scene>\n  </body>\n</html>","output":"str","x":740,"y":320,"wires":[["1ce3085e.838b88"]]},{"id":"1db96d66.6f8983","type":"ibmiot in","z":"ad1edd4d.f8676","authentication":"quickstart","apiKey":"","inputType":"evt","logicalInterface":"","ruleId":"","deviceId":"33a2c1a13d27","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"IBM IoT","service":"quickstart","allDevices":"","allApplications":"","allDeviceTypes":true,"allLogicalInterfaces":"","allEvents":true,"allCommands":"","allFormats":"","qos":0,"x":130,"y":80,"wires":[["941e5e37.33c23"]]},{"id":"46248474.7ff8cc","type":"switch","z":"ad1edd4d.f8676","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gte","v":"30","vt":"num"},{"t":"lte","v":"29","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":130,"y":320,"wires":[["5d996f18.38dd2"],["2daa13ff.bf819c"]]},{"id":"10a30e7c.ffb252","type":"function","z":"ad1edd4d.f8676","name":"Get Flow variable","func":"var temp = flow.get(\"temp\");\nmsg.payload = temp;\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":220,"wires":[["46248474.7ff8cc"]]},{"id":"b5a40ba3.48d1a8","type":"http in","z":"ad1edd4d.f8676","name":"","url":"/aframe","method":"get","upload":false,"swaggerDoc":"","x":150,"y":220,"wires":[["10a30e7c.ffb252"]]},{"id":"941e5e37.33c23","type":"change","z":"ad1edd4d.f8676","name":"Insert Sensor data to Flow  variable","rules":[{"t":"set","p":"temp","pt":"flow","to":"payload.d.temp","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":80,"wires":[["156af4b6.5f9bfb"]]},{"id":"7d881c11.031824","type":"debug","z":"ad1edd4d.f8676","name":"Debug: Flow Variable","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":900,"y":80,"wires":[]},{"id":"156af4b6.5f9bfb","type":"function","z":"ad1edd4d.f8676","name":"Flow variable","func":"var temp = flow.get(\"temp\");\nmsg.payload = temp;\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":80,"wires":[["7d881c11.031824"]]},{"id":"64a4e7ca.18f368","type":"comment","z":"ad1edd4d.f8676","name":"Get Sensor data","info":"","x":160,"y":40,"wires":[]},{"id":"62ead0fd.e447","type":"comment","z":"ad1edd4d.f8676","name":"Generate AR","info":"","x":150,"y":180,"wires":[]},{"id":"2daa13ff.bf819c","type":"function","z":"ad1edd4d.f8676","name":"Set Collor : under 29 degrees Celsius","func":"var color1 = \"#d9d44c\";\nvar color2 = \"#d99c4c\";\nvar color3 = \"#E6E6E6\";\nmsg.payload = {\n    level1: color1,\n    level2: color2,\n    level3: color3\n};\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":380,"wires":[["30a7ca7.9437f36"]]},{"id":"5d996f18.38dd2","type":"function","z":"ad1edd4d.f8676","name":"Set Collor : over 30 degrees Celsius","func":"var color1 = \"#d9d44c\";\nvar color2 = \"#d99c4c\";\nvar color3 = \"#d9614c\";\nmsg.payload = {\n    level1: color1,\n    level2: color2,\n    level3: color3\n};\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":320,"wires":[["30a7ca7.9437f36"]]}]

Githubにも同じものがあります。
Githubを使い慣れている方向け >> node-red-recipe/ibmiot_sensor_ar_integration_demo.json

サンプル読み込み手順

Node-REDの画面で、画面右上の「三」>>「読み込み」の順にクリックします。

「フローをクリップボードから読み込み」画面で、コピーしたサンプルを下図のように貼り付けます。貼り付け後、読み込み先に「新しいタブ」をクリックし、さらに「読み込み」ボタンをクリックします。
ここで「読み込み」ボタンが表示されない場合は、サンプルがただしく貼り付けられていないことを示しますので、再度サンプルをコピーし直してください。

無事にサンプルが読み込まれると、下図のように表示されます。

IBM IoT 仮想センサーの利用

実物のセンサーが手元にない方を想定しまして、無料で使える仮想センサーを使用します。Webブラウザで下記URLにアクセスします。
http://quickstart.internetofthings.ibmcloud.com/iotsensor

仮想センサーが表示されます。
下図のように画面右上の英数字の箇所(枠部分)をクリックします。この英数字は「デバイスID」と言います。

「IBMご利用条件に同意します」にチェックを入れ、「進む」ボタンをクリックします。また枠内に表示されている英数字(デバイスID)をコピーします。なお、この仮想センサーの画面は、今回のサンプルを動かしている間は閉じないでください。仮想センサーの画面を閉じると仮想センサーが使えなくなります。その場合は、前述のURLに再度アクセスし、デバイスIDを取得し直してください。

Node-REDの画面に戻ります。

Node-RED上で作業

「IBM IoT」ノードをダブルクリックし、「ibmiot inノードを編集」画面を開きます。

Device Idの欄に、先ほどコピーしたデバイスIDの英数字を貼り付け、「完了」をクリックします。

画面右上の「デプロイ」ボタンをクリックします。

Node-REDで動かすサンプルの解説

サンプルを動かすだけであれば不要だと思いますが、Node-RED使用箇所の解説 : Node-REDとクラウドを用いたセンシングデータのAR可視化サンプル にて解説します。サンプルをもとにアレコレ改造したりするのであれば一読しておくことをオススメします。

動作確認

仮想センサーの値の受信確認

下図のように画面右上の虫型のアイコンをクリックし、デバッグタブを表示します。デバッグタブには、仮想センサーの値が表示されます。表示されていることで、Node-REDで仮想センサーの値が受信できていることがわかります。

AR表示用URLの確認

Node-REDのURLをコピーします。メモ帳やVisual Studio Code などお好みのエディタをお使いください。
Node-REDのURLのうち、mybluemix.netの手前の/(スラッシュ)までを削除し、末尾に/aframeを追加します。
たとえば、https://node-red-xxxx.mybluemix.net/aframe といったようになるはずです。xxxx の部分は各自で異なりますのでご注意ください。この/aframeで終わるURLに、PCやスマートフォンのWebブラウザからアクセスすることで、Webブラウザ経由でカメラがARマーカーを認識し、ARコンテンツを表示することができます。

ARマーカーを用意します。

印刷しておくことがベストですが、スマートフォンで表示させたり、PCの画面に表示させても認識します。

PCのWebブラウザで、ARの表示を確認

PCで確認する場合は、Webカメラを搭載しているか、USB接続のカメラが使えることが前提ですえ。PCのWebブラウザで、AR表示用URLにアクセスします。Webブラウザは、Google Chromeと最新版のMicrosoft Edgeで確認済みです。

カメラの使用許可を求められますので、「許可」をクリックします。

印刷したARマーカーもしくは、スマートフォンに表示されたARマーカーをPCのカメラに認識させることで、ARコンテンツが表示されます。
下図は、仮想センサーの値が、29度以下のときの表示です。

仮想センサーの値を変更

仮想センサーの画面に戻り、温度を31度に変更します。上下の矢印をクリックすることで、変更できます。

PCのWebブラウザで、ARの表示を確認

AR表示は1分毎に自動でWebブラウザを読み込みなおす処理を組み込んでありますが、お急ぎの場合は、Webブラウザで再読み込みします。

今度は、仮想センサーで31度にしましたので、30度以上のときの色に変わって表示されます。

スマートフォンのWebブラウザで、ARの表示を確認

iPhoneのSafariで確認しています。Androidの場合は、Google Cheromeが良いでしょう。
AR表示用のURLにアクセスし、ARマーカーを認識させることで下図のように表示されます。

拡張

センサーの実物とM5StackやRaspberry Pi Zero WHなどをお持ちであれば、仮想センサーを置き換えて、実物のセンサーで確認することをオススメします。センサーによっては、川の水位や水田の水質、流量、温度、湿度、ガス濃度、二酸化炭素濃度、血中酸素飽和濃度、血圧などVR/ARによる可視化が可能です。
ARとして表示するものは今回は立方体ですが、センサーに合わせた3DCGモデルを作成し表示したり、アニメーションをつけることもオススメです。

また、「Hello Wordl!」と表示している箇所を日本語に変更しても良いでしょう。A-Frameのドキュメントで、a-textについて確認することを推奨します。