IoT時代に一般化される(?)次世代モニター『省スペースARモニター』作ってみた
概要
・何台もの機器を管理するIoT時代に最適なモニターとは何か考えます。
・『省スペースARモニター』と命名したモニターを紹介します。
・作ったARモニターのデモ映像とその作り方を紹介します。
次世代モニターを考える
パソコンには、パソコン本体だけでなくモニターが必要です。ですがIoT機器の利用が進み、100台以上のIoT機器を利用するようになった場合、各IoT機器にモニタを設置するには、それなりに場所が必要になります。モニターの省スペース化の方法として、1台のモニタを複数の機器で共有する方法がありますが、切り替えが必要ですし100台の切り替えはやりたくないです。同時に見れないのも難点です。そのようなことを考えると、IoT用のモニターには以下の条件が必要そうです。
・省スペースで設置できること
・IoT機器が増えても対応できること
この要件を満たすモニターとして、思いついたのが AR (拡張現実)を使ったモニターになります。つまり、各IoT機器にはモニタを設置せずARマーカーを用意します。そしてARマーカーで表示する3Dオブジェクトをモニターにします。これなら設置に必要なスペースはARマーカーを置く場所だけでよく、電源も必要ありません。(当然ですが、ヘッドマウントディスプレイには電源必要です)
このモニターを『省スペースARモニター』と命名し、実際に作っていきます。
AR (拡張現実)とは?
本題に入る前にARについて紹介します。「ARってなんだ?」という質問に対しては「ポケモンGOです」と答えています。以前は「ドラゴンボールのスカウターみたいなやつです」と答えてました。かなり荒っぽい説明ですが、そんな感じです(笑)
サンプル作ってみた
早速ですが、完成したサンプルを紹介します。
ARモニターに、IoT機器であるラズベリーパイ(以後、ラズパイ)のCPU使用率を表示します。
まずは以下のように、ラズパイの隣にARマーカを用意します。これだけではARモニターは表示されません。
次に、これをARに対応したヘッドマウントディスプレイで見ると、以下のようにARマーカーにラズベリーパイのCPU使用率を表示するモニターが表示されます。↓↓↓ こんな感じ
画像ではわかりにくいと思いデモ動画も作りました。予算の都合でヘッドマウントディスプレイは使っていませんが、雰囲気つたわると思います。
作ったサンプルの構成図
省スペースARモニターの作り方を紹介する前に構成図を紹介します。
ラズパイとWindowsパソコンを同じネットワーク内にして通信できるようにします。
また、ラズパイにはWEBサーバーを構築し、ARコンテンツを配信できるようにします。
作り方
さて、作り方を紹介します。
1. ラズベリーパイのセットアップ
ラズベリーパイのOSインストールから基本セットアップは以前まとめた記事があります。
2. Nodejsインストール
ARのWEBコンテンツを使うためNodejsをインストールします。
3. オレオレ証明書でNodejsをhttps通信
ARのWEBコンテンツは、コンテンツを開く機器(構成図ではWindowsパソコン)のカメラ📷を使用します。カメラ📷を使うには(httpではなく)https通信が必要になります。
4. 必要なデータを作成する
ARコンテンツに必要な以下のデータを用意します。
<用意するデータの説明>
aframe-html-shader.min.js
ARモジュール。ar.html で使用します。
https://github.com/mayognaise/aframe-html-shaderでダウンロードできました。
ar.html
ARコンテンツ。jsonファイル(sample.json)を定期的に読み込みCPU使用率を表示します。
ラズベリーパイのCPU使用率を取得し、jsonファイル(sample.json)として出力します。スクリプトは、Python3 で書いています。
sample.json
json形式で、CPU使用率の情報を記載。getInfoOutToJson.pyで定期的に更新し、ar.html が定期的に読み込みます。
ラズベリーパイにランダムに負荷を与えます。CPU使用率が変わるようにします。必要なければ作る必要ありません。
4.1 AR用のサイト(ar.html)
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width">
<title>aframe</title>
<script src="https://aframe.io/releases/0.6.1/aframe.min.js"></script>
<script src="aframe-html-shader.min.js"></script>
<script src="https://jeromeetienne.github.io/AR.js/aframe/build/aframe-ar.js"></script>
<style type="text/css">
#target {
width: 128px;
height: 128px;
position: absolute;
background: rgba(200,200,200,1.0);
}
#htmlTarget {
position: absolute;
z-index: 1;
height: 100%;
width: 100%;
overflow: hidden;
position: fixed;
top: 0;
}
</style>
<script>
//*************************************
AFRAME.registerComponent('update-html', {
init: function () {
this.lettersEntity = document.getElementById('lettersEntity');
this.lettersIndex = 0;
this.lettersUpdateDur = 1500 / this.lettersEntity.getAttribute('material').fps;
this.lettersTime = 0;
this.pre = document.getElementById('pre');
},
tick: function (t, dt) {
this.lettersTime += dt;
if (this.lettersTime >= this.lettersUpdateDur) {
var nxtSrc = "";
var tmp = getStr();
document.getElementById('pre').innerHTML = tmp;
this.lettersTime = 0;
}
}
});
//*************************************/
function getStr() {
var wStr = "";
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if(req.readyState == 4 && req.status == 200){
var data = JSON.parse(req.responseText);
var len = data.length;
for(var i=0; i<len; i++) {
switch( data[i].name ){
case 'CLOCK': wStr += data[i].value; break;
default : wStr += "<br />" + data[i].name + ":" + data[i].value; break;
}
}
}
};
req.open("GET", "sample.json", false);
req.send(null);
return( wStr );
}
</script>
</head>
<body>
<a-scene update-html arjs="debugUIEnabled:false;">
<a-marker preset="hiro">
<a-entity
id="lettersEntity"
geometry="primitive: box;
width: 1.0;
height: 1.0;
depth: 0.01;"
rotation="-90 0 0"
position="0 0 0"
material="
shader: html;
target: #target;
transparent: true;
ratio: width;
fps: 1.5"
></a-entity>
<a-entity position=" 0.6 0.0 0.1" rotation="0 0 0" geometry="primitive:box;width:0.2;height:0.1;depth:1.2;" material="color: #0000FF;roughness:0.1;metalness:0.5;"></a-entity>
<a-entity position="-0.6 0.0 -0.1" rotation="0 0 0" geometry="primitive:box;width:0.2;height:0.1;depth:1.2;" material="color: #00FF00;roughness:0.1;metalness:0.5;"></a-entity>
<a-entity position=" 0.1 0.0 -0.6" rotation="0 90 0" geometry="primitive:box;width:0.2;height:0.1;depth:1.2;" material="color: #FF0000;roughness:0.1;metalness:0.5;"></a-entity>
<a-entity position="-0.1 0.0 0.6" rotation="0 90 0" geometry="primitive:box;width:0.2;height:0.1;depth:1.2;" material="color: #FF00FF;roughness:0.1;metalness:0.5;"></a-entity>
</a-marker>
<a-entity camera></a-entity>
</a-scene>
<!-- HTML to render as a material. -->
<div id="htmlTarget" style="width:0px;height:0px;">
<center>
<div id="target">
<div ID="pre"></div>
</div>
</center>
</div>
</body>
</html>
4.2 JSONデータ更新スクリプト(getInfoOutToJson.py)
import subprocess
import sys
import datetime
import time
def OutInfo():
res = subprocess.run( ["vmstat","1","3"], stdout=subprocess.PIPE )
res = res.stdout
resLine = res.splitlines(False)
resArr = str( resLine[4] ).split()
now = datetime.datetime.now()
clock = str(now.hour) + ':' + str(now.minute) + ':' + str(now.second)
print( resLine[0])
print( resLine[1])
print( resLine[4])
print( resArr[13])
path = 'sample.json'
with open( path, mode='w' ) as f:
outStr = ''
outStr += '['
outStr += '{"name":"CLOCK","value":"' + str(clock) + '"}'
outStr += ',{"name":"CPU[%]","value":"' + resArr[13] + '"}'
outStr += ']'
f.write( outStr )
for i in range(10000):
OutInfo()
time.sleep( 1 )
動かし方
nodejsのWebサービスを開始します。
node index.js
JSONデータを定期的に更新するPython3スクリプトを実行します。
python3 getInfoOutToJson.py
これでラズパイにあるWEBサーバの準備が完了しARコンテンツを配信できるようになりました。
ラズパイと同じネットワークにいるWindowsパソコンでブラウザを起動し ARコンテンツ(ar.html) を開きます。ARコンテンツを開くには以下のようなURLを指定します。
https://{ラズベリーパイのIPアドレス}:{ポート}/ar.html
無事ARコンテンツが開くとWindowsパソコンに設置されたカメラ📷からの映像が表示されます。その映像にARマーカー(Hiroのマーク)を映すと、ラズパイのCPU使用率を表示するモニターが表示されるはずです。
おまけ
省スペースARモニターが動作しても、CPU使用率が一定だと面白くありません。そこで、ラズパイのCPU使用率がコロコロ変えるスクリプト(stress.py)を作りました。ソースは↓こちらです。(python3 stress.py で実行します)
import subprocess
import sys
import random
import time
def doCom():
coFlg = random.randint(0,5)
if coFlg == 0:
hoge = subprocess.run( ["ls","-l"], stdout=subprocess.PIPE )
elif coFlg == 1:
hoge = subprocess.run( ["tree","/etc"], stdout=subprocess.PIPE )
elif coFlg == 2:
hoge = subprocess.run( ["tree","/home"], stdout=subprocess.PIPE )
else:
hoge = subprocess.run( ["ls","-l","-a"], stdout=subprocess.PIPE )
for i in range(10000):
waitsec = random.randint(0,5)
lpmax = random.randint(10,50)
print("doCom " + str(lpmax) + " [times]")
for j in range( lpmax ):
doCom()
print("wait " + str(waitsec) + " [sec]")
time.sleep( waitsec )
さいごに
未来のことはわかりませんが、今回紹介したような、ARを使用したモニターの活用は一般的になるのではないかと、サンプルを作ってそう思いました。
こんな弱小ブログでもサポートしてくれる人がいることに感謝です。