katiのアイコン画像
kati 2021年04月21日作成
製作品 製作品 閲覧数 1754
kati 2021年04月21日作成 製作品 製作品 閲覧数 1754

モーションセンサーで動く3Dオブジェクト

モーションセンサーで動く3Dオブジェクト

デモ動画

部品

部品名 個数
obniz Board 1Y 1
LSM303DLH 1

設計図

「モーションセンサーで動く3Dオブジェクト」は、obniz Board 1Yに接続されたモーションセンサー「LSM303DLH」の動きに応じて、ブラウザに表示された3Dオブジェクトが動きます。また、3Dオブジェクトの動きの開始及び終了は、Web Speech APIを使用した音声認識により行い、音声合成により状況を通知します。3Dオブジェクトはjavascriptライブラリ「Three.js」を用いて描画します。

使用方法

「モーションセンサーで動く3Dオブジェクト」の使用方法を次に示します。

  1. obniz Board 1Yに電源を入れて、作成したブラウザソースコードをアクセスすると、ブラウザに3Dオブジェクトが表示されます。
  2. ブラウザ画面に表示されている「音声合成」ボタンを押して、マイクに「開始して」を発話すると、音声合成により「運動を開始します」が発話されます。
  3. obniz Board 1Yに接続されているモーションセンサー「LSM303DLH」を傾けると、その傾きに従って表示されている3Dオブジェクトが傾きます。
  4. マイクに「終了して」を発話すると、音声合成により「運動を終了します」が発話され、以降モーションセンサー「LSM303DLH」を傾けても、表示されている3Dオブジェクトは傾きません。

回路図2

obniz Board 1Yとモーションセンサー「LSM303DLH」を次のようにI2Cにより接続します。

キャプションを入力できます

ハードウェア

ハードウェアの接続画像を次に示します。

キャプションを入力できます

ブラウザ表示

ブラウザソースコードにアクセスするとブラウザに次のように3Dオブジェクトが表示されます。

キャプションを入力できます

ソースコード

「ブラウザソースコード」にアクセスすることにより、「モーションセンサーで動く3Dオブジェクト」が開始されます。javascriptライブラリ「Three.js」は「ブラウザソースコード」から呼ばれて3Dオブジェクトを描画し、「LSM303DLHクラスライブラリ」は「ブラウザソースコード」から呼ばれて加速度データや方位角を取得します。

ブラウザソースコード

ブラウザソースコードは次の処理を行います。

  • 「音声合成」ボタンを表示します。このボタンを押すことにより音声合成を許可します。
  • Obnizクラスをインスタンス化してobniz Board 1Yに接続します。
  • 接続が完了すると、LSM303DLHクラス「 LSM303」をインスタンス化してモーションセンサー「LSM303DLH」を初期化します。
  • 音声認識のコールバック関数「recognition.onresult」を登録し、発話すると音声認識が行われて結果がコールバック関数「recognition.onresult」に渡され、音声合成を用いて音声認識に対応する音声を発話します。
  • 300msごとに運動の開始が要求されているかを調べ、運動の開始が要求されているとaccelreadAxesメソッドで加速度データを取得し、readHeadingメソッドで方位角を取得します。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>obniz IoTコンテスト</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <link type="text/css" rel="stylesheet" href="main.css"> <script src="https://unpkg.com/obniz@3.14.0/obniz.js" crossorigin="anonymous"></script> <script src="./lsm303.js"></script> </head> <body> <div id="info"> <button id="exportASCII">音声合成</button> </div> <script> var obniz = new Obniz("OBNIZ_ID_HERE"); var lsm303; var lsm303dx = 0; var lsm303dy = 0; var lsm303dz = 0; var uttrstart = new SpeechSynthesisUtterance("運動を開始します"); var uttrstop = new SpeechSynthesisUtterance("運動を終了します"); var startflg = 0; obniz.onconnect = async function (obniz) { lsm303 = new LSM303(); lsm303.params = {vcc: 0, scl: 1, sda: 2, gnd: 3}; lsm303.wired(obniz); lsm303.enabl(); const recognition = new webkitSpeechRecognition(); recognition.lang = "ja-JP"; recognition.continuous = true; recognition.onresult = function (event) { // omit console.log("onresult2! " + event.results[event.resultIndex][0] .transcript); if (event.results[event.resultIndex][0].transcript.includes( '開始')) { startflg = 1; speechSynthesis.speak(uttrstart); } else if (event.results[event.resultIndex][0].transcript.includes( '終了')) { startflg = 0; speechSynthesis.speak(uttrstop); } }; recognition.onspeechend = function (event) { // omit console.log("onspeechend! " + event); }; recognition.onend = function (event) { console.log("onend! " + event); recognition.start(); }; recognition.start(); }; // called while online. obniz.onloop = async function () { obniz.wait(300); if (startflg === 1) { let axes = await lsm303.accelreadAxes(); lsm303dx = axes.x / 8; lsm303dy = axes.y / 8; lsm303dz = axes.z / 8; console.log(axes); let heading = await lsm303.readHeading(); console.log(heading); } else { var lsm303x = lsm303y = lsm303z = 0; } // JavaScript Examples }; // called on offline obniz.onclose = async function () { }; </script> <script type="module" src="3dobject.js"> </script> </body> </html>

LSM303DLHクラスライブラリ

obniz標準のパーツライブラリでは モーションセンサー「LSM303DLH」がサポートされていないため、LSM303DLHクラスライブラリ「lsm303.js」を作成して単なるクラスとして使用しました。LSM303DLHクラスライブラリでは、accelreadAxesメソッドで加速度センサーからの加速度データを取得し、readHeadingメソッドで磁気コンパスからデータを取得して方位角を計算します。

lsm303.js

//====================================== // LSM303 : デジタルコンパス・加速度センサ //============================== ======== class LSM303 { constructor() { this.requiredKeys = []; this.keys = ["vcc", "gnd", "sda", "scl", "i2c"]; this.acceladdress = 0x18; this.magaddress = 0x1e; } static info() { return { name: "LSM303" }; } wired(obniz) { this.obniz = obniz; this.obniz.setVccGnd(this.params.vcc, this.params.gnd, "5v"); this.params.clock = 400000; this.params.pull = "5v"; this.params.mode = "master"; this.i2c = obniz.getI2CWithConfig(this.params); this.x_offset = 0; this.y_offset = 0; this.z_offset = 0; this.obniz.wait(100); } async enabl() { this.i2c.write(this.acceladdress, [0x20, 0x27]); // enabling Accelerometer this.i2c.write(this.magaddress, [0x02, 0x00]); // enabling Magnetometer } async accelreadAxes() { this.i2c.write(this.acceladdress, [0x28 | 0x80]); let res = await this.i2c.readWait(this.acceladdress, 6); return this.buffToXYZAccel(res); } async readHeading() { this.i2c.write(this.magaddress, [0x03]); let res = await this.i2c.readWait(this.magaddress, 6); return this.buffToHeadMag(res); } buffToXYZAccel(buffer) { var pos; pos = { x: this.twoscomp(((buffer[1] << 8) | buffer[0]) >> 4, 12), y: this.twoscomp(((buffer[3] << 8) | buffer[2]) >> 4, 12), z: this.twoscomp(((buffer[5] << 8) | buffer[4]) >> 4, 12) }; return pos; } buffToHeadMag(buffer) { var pos; pos = { x: (this.twoscomp((buffer[0] << 8) | buffer[1], 16) - this.x_offset), z: (this.twoscomp((buffer[2] << 8) | buffer[3], 16) - this.z_offset), y: (this.twoscomp((buffer[4] << 8) | buffer[5], 16) - this.y_offset) }; return this.toPolar(pos.x, pos.y); } twoscomp(value, no_of_bits) { var upper = Math.pow(2, no_of_bits); if (value > upper / 2) { return value - upper; } else { return value; } } toPolar(x, y) { // returns polar coordinates as an object (radians) var polarCoords = {}; polarCoords.r = Math.sqrt(x * x + y * y); polarCoords.theta = Math.PI / 2 - Math.atan2(y, x); if (polarCoords.theta < 0) { polarCoords.theta += 2 * Math.PI; } polarCoords.theta = 2 * Math.PI - polarCoords.theta; polarCoords.theta = (180 / Math.PI * polarCoords.theta); return ((polarCoords.theta !== 360) ? polarCoords.theta : 0); } }

javascriptライブラリ「Three.js」

javascriptライブラリ「Three.js」はwebGLを使用して3Dオブジェクトを描画します。今回使用する3Dオブジェクトは、three.jsのexamplesを参考にしました。モーションセンサー「LSM303DLH」からのデータを取得するために「OrbitControls.js」を次のように変更して、300msごとに加速度データを取得しています。

OrbitControls.js

・・・ window.addEventListener('DOMContentLoaded', function () { setInterval(() => { rotateEnd.set(lsm303dx, lsm303dy); rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed); const element = scope.domElement; rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); rotateStart.copy(rotateEnd); scope.update(); }, 300); });   ・・・
1
  • kati さんが 2021/04/21 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する