はじめに
「リアルタイムチャートをDjango ChannelsとEpoch.jsでつくってみた」の続編です。前回はDjango ChannelsとEpoch.jsを用いて、モーションセンサのリアルタイム可視化に挑戦しました。
Django ChannelsとEpoch.jsでセンサデータをリアルタイムに表示するチャート作ってみました。Django Channelsを使用することで、HTTPと同じくらい簡単にWebsocketを実装出来ました。Epoch.jsも学習コストが低く使いやすかったです。 pic.twitter.com/oflpVucoVk
— yakiimo121 (@yakiimo121) June 20, 2021
しかし、約100Hzで流れてくる加速度データをEpoch.jsでは捌ききれず、微妙な結果に終わりました。
Epoch.jsでも100Hzのモーションセンサデータをリアルタイムで描画するのは困難でした。遅延が発生したり、それにともないキューからデータがこぼれたりします。さすがにWebベースの限界なのでしょうか?それとも他のライブラリを使えば解決できるのでしょうか??(「リアルタイムチャートをDjango ChannelsとEpoch.jsでつくってみた/まとめ」より)
そこで、今回はEpoch.jsの代わりにWebGL-Plotという可視化ライブラリを用いて、リベンジしようと思います。
完成版はこちらに上げておきます。
WebGL-Plotとは
ネイティブWebGLベースの高性能リアルタイム2Dプロットライブラリです。
Performance (パフォーマンス)
Fast, simple and efficient plotting library with no dependencies based on native WebGL. High refresh rate and ideal for low resource devices
(和訳)ネイティブのWebGLをベースにした、依存性のない高速でシンプルかつ効率的なプロッティングライブラリです。高リフレッシュレートで低リソースデバイスに最適。
高リフレッシュレートである点が今回のポイントです。
Versatile (多用途)
Flexible to use as vanilla Javascript or in combination with variety of popular frameworks and configurations.
(和訳)Javascript単体での使用はもちろん、様々なフレームワークや構成との組み合わせにも柔軟に対応します。
Epoch.jsのときと同様にvanilla Javascriptで使います。
Limitations (制限)
It cannot change the line width due to the OpenGL implementation of a line. The OpenGL specification only guarantees a minimum of a single pixel line width. There are other solutions to increase the line width however they substantially increase the size of the data vector and take a hit on the performance. Top performance (refresh rate, memory, etc) is the top priority for this library.
(和訳)OpenGLの線の実装により、線幅を変更することはできません。OpenGLの仕様では、最低でも1ピクセルの線幅しか保証されていません。線幅を広げる方法は他にもありますが、データベクタのサイズが大きくなり、性能に打撃を与えます。このライブラリでは、最高のパフォーマンス(リフレッシュレート、メモリなど)が最優先されます。
リフレッシュレートやメモリなどに性能を振っているため、線幅の調整などで融通の利かない部分があります。
実装
前回のものをベースにしています。
はじめに、<script>
タグでwebglplot.umd.min.js
を読み込み、<canvas>
タグでWebGL APIを使うためのcanvasを作成します。
<script src="https://cdn.jsdelivr.net/gh/danchitnis/webgl-plot@master/dist/webglplot.umd.min.js"></script>
<div>
<canvas class="canvas" id="my_canvas"></canvas>
</div>
続いて、cssでブラウザ全体とcanvasのスタイルを調整します。
body {
background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);
font-family: Arial, Helvetica, sans-serif;
}
.canvas {
width: 100%;
height: 70vh;
}
最後にjavascriptの実装を行います。大きく分けて二つの機能があります。
- WebSocketでDjangoからデータを受け取る機能
- WebGL-Plotでグラフを描画する機能
WebSocketの実装は前回のEpoch.jsのときと同じです。WebGL-Plotは公式のドキュメントを参考に実装しました。
const dataSocket = new WebSocket('ws://'+window.location.host+'/ws/');
var x_value = 0;
var y_value = 0;
var z_value = 0;
var i = 0;
dataSocket.onmessage = function(e) {
const current = JSON.parse(e.data);
x_value = current[0]["y"];
y_value = current[1]["y"];
z_value = current[2]["y"];
};
const canvas = document.getElementById("my_canvas");
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const numX = canvas.width;
const color_r = new WebglPlotBundle.ColorRGBA(255, 0, 0, 1,);
const color_b = new WebglPlotBundle.ColorRGBA(0, 0, 255, 1,);
const color_g = new WebglPlotBundle.ColorRGBA(0, 255, 0, 1,);
const line1 = new WebglPlotBundle.WebglLine(color_r, numX);
const line2 = new WebglPlotBundle.WebglLine(color_b, numX);
const line3 = new WebglPlotBundle.WebglLine(color_g, numX);
const wglp = new WebglPlotBundle.WebglPlot(canvas);
line1.lineSpaceX(-1, 2 / numX)
line2.lineSpaceX(-1, 2 / numX)
line3.lineSpaceX(-1, 2 / numX)
line1.scaleY = 0.5;
line2.scaleY = 0.5;
line3.scaleY = 0.5;
wglp.addLine(line1);
wglp.addLine(line2);
wglp.addLine(line3);
function newFrame() {
update();
wglp.update();
requestAnimationFrame(newFrame);
}
requestAnimationFrame(newFrame);
function update() {
if (i > line1.numPoints){
line1.shiftAdd([x_value]);
line2.shiftAdd([y_value]);
line3.shiftAdd([z_value]);
}else{
line1.setY(i, x_value);
line2.setY(i, y_value);
line3.setY(i, z_value);
}
i+=1;
}
dataSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
実行
前回と同様にスマートフォンで観測した3軸加速度データをリアルタイムで可視化するデモを行います。
初めにHASC Loggerをスマートフォンにインストールします。
HASC Loggerから[ホストのIP]:4001
へ3軸加速度をUDPで送信します。設定の方法は「角速度から回転行列を求める - 実装編」を参考にしてください。
リアルタイムチャートアプリを起動します。
python manage.py migrate
python manage.py runserver
ブラウザからhttp://[ホストのIP]:8000/realtimechart/webglplot
へアクセスします。(Epoch.js版はhttp://[ホストのIP]:8000/realtimechart/epochjs
からアクセスできます。)
Django ChannelsとWebGL-Plotでセンサデータをリアルタイムで表示するチャートを作ってみました。100Hzで流れてくるモーションセンサでも遅延なく描画できてます。https://t.co/iJy4YsIPQf pic.twitter.com/SYnJCS9j7W
— yakiimo121 (@yakiimo121) January 5, 2022
まとめ
今回はDjango channelsとWebGL-Plotを用いてセンサデータをリアルタイムで表示するチャートを作ってみました。WebGL-Plotを用いることで、Epoch.jsを用いた可視化システムの課題であったリフレッシュレートを改善しました。結果、約100Hzで流れてくる加速度のリアルタイム可視化に成功しました。
しかし、WebGL-Plotには目盛、凡例、軸ラベル、グリッドなどを表示させる機能がありませんでした(自分調べ)。こう言った点の使い勝手の良さはEpoch.jsの方が上だと思います。