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

モーションセンサーで動く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 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する