ウェブ向けセンサー

汎用センサー API を使用して、加速度計、ジャイロスコープ、磁力計などのデバイス内センサーにアクセスします。

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

現在、センサーデータは、没入型ゲーム、フィットネス トラッキング、拡張現実や仮想現実などのユースケースを実現するために、多くのプラットフォーム固有のアプリケーションで使用されています。プラットフォーム固有のアプリケーションとウェブ アプリケーションのギャップを埋めることができたら、素晴らしいと思いませんか?ウェブ用の Generic Sensor API を入力します。

Generic Sensor API とは何ですか?

汎用センサー API は、センサー デバイスをウェブ プラットフォームに公開するインターフェースのセットです。この API は、ベースの Sensor インターフェースと、その上に構築された一連の具体的なセンサー クラスで構成されています。ベース インターフェースがあると、具体的なセンサー クラスの実装と仕様のプロセスが簡素化されます。たとえば、Gyroscope クラスをご覧ください。とても小さいです。コア機能は基本インターフェースで指定され、Gyroscope は角速度を表す 3 つの属性で拡張するだけです。

一部のセンサー クラスは、加速度計やジャイロスコープなどの実際のハードウェア センサーとインターフェースします。これらは低レベル センサーと呼ばれます。フュージョン センサーと呼ばれる他のセンサーは、複数の低レベル センサーからのデータを統合して、スクリプトが計算する必要がある情報を公開します。たとえば、AbsoluteOrientation センサーは、加速度計、ジャイロスコープ、磁力計から取得したデータに基づいて、すぐに使用できる 4×4 の回転行列を提供します。

ウェブ プラットフォームはすでにセンサーデータを提供しているとお考えかもしれませんが、そのとおりです。たとえば、DeviceMotion イベントと DeviceOrientation イベントはモーション センサーのデータを公開します。では、なぜ新しい API が必要なのでしょうか?

既存のインターフェースと比較して、Generic Sensor API には次のような多くの利点があります。

  • Generic Sensor API は、新しいセンサー クラスで簡単に拡張できるセンサー フレームワークです。これらの各クラスは汎用インターフェースを維持します。1 つのセンサータイプ用に作成されたクライアント コードは、わずかな変更で別のセンサータイプ用に再利用できます。
  • センサーを構成できます。たとえば、アプリケーションのニーズに適したサンプリング頻度を設定できます。
  • センサーがプラットフォームで使用できるかどうかを検出できます。
  • センサー測定値には高精度のタイムスタンプが含まれているため、アプリ内の他のアクティビティとの同期を改善できます。
  • センサー データモデルと座標系が明確に定義されているため、ブラウザ ベンダーは相互運用可能なソリューションを実装できます。
  • Generic Sensor ベースのインターフェースは DOM にバインドされておらず(つまり、navigator オブジェクトでも window オブジェクトでもありません)、サービス ワーカー内で API を使用したり、組み込みデバイスなどのヘッドレス JavaScript ランタイムで API を実装したりする将来の可能性が開かれています。
  • セキュリティとプライバシーの側面は、Generic Sensor API の最優先事項であり、以前のセンサー API と比較してセキュリティが大幅に向上しています。Permissions API との統合があります。
  • AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorMagnetometer では、画面座標との自動同期が利用できます。

利用可能な汎用センサー API

執筆時点では、テストできるセンサーがいくつかあります。

モーション センサー:

  • Accelerometer
  • Gyroscope
  • LinearAccelerationSensor
  • AbsoluteOrientationSensor
  • RelativeOrientationSensor
  • GravitySensor

環境センサー:

  • AmbientLightSensor(Chromium の #enable-generic-sensor-extra-classes フラグの背後)。
  • Magnetometer(Chromium の #enable-generic-sensor-extra-classes フラグの背後)。

特徴検出

ハードウェア API の機能検出は、ブラウザが対象のインターフェースをサポートしているかどうかと、デバイスに該当するセンサーがあるかどうかの両方を検出する必要があるため、複雑です。ブラウザがインターフェースをサポートしているかどうかを確認するプロセスは簡単です。(Accelerometer は、上記で説明した他のインターフェースのいずれかに置き換えます)。

if ('Accelerometer' in window) {
  // The `Accelerometer` interface is supported by the browser.
  // Does the device have an accelerometer, though?
}

実際に意味のある機能検出結果を得るには、センサーへの接続も試す必要があります。この例では、その方法を示します。

let accelerometer = null;
try {
  accelerometer = new Accelerometer({ frequency: 10 });
  accelerometer.onerror = (event) => {
    // Handle runtime errors.
    if (event.error.name === 'NotAllowedError') {
      console.log('Permission to access sensor was denied.');
    } else if (event.error.name === 'NotReadableError') {
      console.log('Cannot connect to the sensor.');
    }
  };
  accelerometer.onreading = (e) => {
    console.log(e);
  };
  accelerometer.start();
} catch (error) {
  // Handle construction errors.
  if (error.name === 'SecurityError') {
    console.log('Sensor construction was blocked by the Permissions Policy.');
  } else if (error.name === 'ReferenceError') {
    console.log('Sensor is not supported by the User Agent.');
  } else {
    throw error;
  }
}

ポリフィル

Generic Sensor API をサポートしていないブラウザでは、polyfill を利用できます。このポリフィルを使用すると、関連するセンサーの実装のみを読み込むことができます。

// Import the objects you need.
import { Gyroscope, AbsoluteOrientationSensor } from './src/motion-sensors.js';

// And they're ready for use!
const gyroscope = new Gyroscope({ frequency: 15 });
const orientation = new AbsoluteOrientationSensor({ frequency: 60 });

これらのセンサーは何ですか?どのように使用すればよいですか?

センサーは、簡単な説明が必要になる可能性がある分野です。センサーについてよく知っている場合は、ハンズオン コーディング セクションに直接進んでください。それ以外の場合は、サポートされている各センサーについて詳しく見ていきましょう。

加速度センサーと直線加速度センサー

加速度センサーの測定値

Accelerometer センサーは、センサーをホストするデバイスの 3 つの軸(X、Y、Z)の加速度を測定します。このセンサーは慣性センサーです。つまり、デバイスが線形自由落下している場合、測定される合計加速度は 0 m/s2 になります。デバイスがテーブルの上に平らに置かれている場合、上向き(Z 軸)の加速度は地球の重力に等しくなります。つまり、g ≈ +9.8 m/s2 になります。これは、テーブルがデバイスを上向きに押す力を測定しているためです。デバイスを右に押すと、X 軸の加速度は正になります。デバイスを右から左に加速させると、負になります。

加速度計は、歩数計、モーション センサー、デバイスの向きの検出などに使用できます。加速度計の測定値は、他のソースのデータと組み合わせて、方位センサーなどのフュージョン センサーを作成することがよくあります。

LinearAccelerationSensor は、センサーをホストするデバイスに適用される加速度を測定します(重力の影響は除外されます)。デバイスが静止している場合(テーブルの上に平らに置かれている場合など)、センサーは 3 つの軸で ≈ 0 m/s2 の加速度を測定します。

重力センサー

ユーザーは AccelerometerLinearAccelerometer の測定値を手動で確認することで、重力センサーの測定値に近い値を手動で導き出すことができますが、これは面倒な作業であり、これらのセンサーが提供する値の精度に依存します。Android などのプラットフォームでは、オペレーティング システムの一部として重力測定値を提供できます。これは、計算の面でより安価で、ユーザーのハードウェアに応じてより正確な値を提供し、API エルゴノミクスの面でより使いやすくなります。GravitySensor は、重力によるデバイスの X、Y、Z 軸に沿った加速度の効果を返します。

ジャイロスコープ

ジャイロスコープ センサーの測定値

Gyroscope センサーは、デバイスのローカル X、Y、Z 軸を中心とした角速度をラジアン毎秒で測定します。ほとんどの消費者向けデバイスには、機械式(MEMS)ジャイロスコープが搭載されています。これは、慣性コリオリ力に基づいて回転速度を測定する慣性センサーです。MEMS ジャイロスコープは、センサーの重力感度によってセンサーの内部機械システムが変形し、ドリフトが発生しやすい傾向があります。ジャイロスコープは、比較的高い周波数で振動します(例: そのため、他のセンサーよりも電力を消費する可能性があります。

方位センサー

絶対方位センサーの測定値

AbsoluteOrientationSensor は、地球の座標系に対するデバイスの回転を測定するフュージョン センサーです。一方、RelativeOrientationSensor は、静止した基準座標系に対するモーション センサーをホストするデバイスの回転を表すデータを提供します。

最新の 3D JavaScript フレームワークはすべて、回転を表す クォータニオン回転行列をサポートしています。ただし、WebGL を直接使用する場合は、OrientationSensorquaternion プロパティpopulateMatrix() メソッドの両方が用意されています。以下にスニペットの例を示します。

three.js

let torusGeometry = new THREE.TorusGeometry(7, 1.6, 4, 3, 6.3);
let material = new THREE.MeshBasicMaterial({ color: 0x0071c5 });
let torus = new THREE.Mesh(torusGeometry, material);
scene.add(torus);

// Update mesh rotation using quaternion.
const sensorAbs = new AbsoluteOrientationSensor();
sensorAbs.onreading = () => torus.quaternion.fromArray(sensorAbs.quaternion);
sensorAbs.start();

// Update mesh rotation using rotation matrix.
const sensorRel = new RelativeOrientationSensor();
let rotationMatrix = new Float32Array(16);
sensor_rel.onreading = () => {
  sensorRel.populateMatrix(rotationMatrix);
  torus.matrix.fromArray(rotationMatrix);
};
sensorRel.start();

BABYLON

const mesh = new BABYLON.Mesh.CreateCylinder('mesh', 0.9, 0.3, 0.6, 9, 1, scene);
const sensorRel = new RelativeOrientationSensor({ frequency: 30 });
sensorRel.onreading = () => mesh.rotationQuaternion.FromArray(sensorRel.quaternion);
sensorRel.start();

WebGL

// Initialize sensor and update model matrix when new reading is available.
let modMatrix = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const sensorAbs = new AbsoluteOrientationSensor({ frequency: 60 });
sensorAbs.onreading = () => sensorAbs.populateMatrix(modMatrix);
sensorAbs.start();

// Somewhere in rendering code, update vertex shader attribute for the model
gl.uniformMatrix4fv(modMatrixAttr, false, modMatrix);

方向センサーは、没入型ゲーム、拡張現実、仮想現実など、さまざまなユースケースを可能にします。

モーション センサー、高度なユースケース、要件の詳細については、モーション センサーの説明をご覧ください。

画面座標との同期

デフォルトでは、空間センサーの読み取り値は、デバイスにバインドされ、画面の向きを考慮しないローカル座標系で解決されます。

デバイス座標系
デバイスの座標系

しかし、ゲームや拡張現実、仮想現実などの多くのユースケースでは、センサーの読み取り値を画面の向きにバインドされた座標系で解決する必要があります。

画面座標系
画面の座標系

以前は、センサーの読み取り値を画面座標に再マッピングする処理を JavaScript で実装する必要がありました。このアプローチは非効率的であり、ウェブ アプリケーション コードの複雑さも大幅に増大します。ウェブ アプリケーションは画面の向きの変化を監視し、センサーの読み取り値の座標変換を実行する必要があります。これは、オイラー角やクォータニオンにとっては簡単なことではありません。

Generic Sensor API は、はるかにシンプルで信頼性の高いソリューションを提供します。ローカル座標系は、定義されたすべての空間センサー クラス(AccelerometerGyroscopeLinearAccelerationSensorAbsoluteOrientationSensorRelativeOrientationSensorMagnetometer)で構成可能です。referenceFrame オプションをセンサー オブジェクト コンストラクタに渡すことで、返される測定値がデバイス座標と画面座標のどちらで解決されるかを定義します。

// Sensor readings are resolved in the Device coordinate system by default.
// Alternatively, could be RelativeOrientationSensor({referenceFrame: "device"}).
const sensorRelDevice = new RelativeOrientationSensor();

// Sensor readings are resolved in the Screen coordinate system. No manual remapping is required!
const sensorRelScreen = new RelativeOrientationSensor({ referenceFrame: 'screen' });

コーディングを始めましょう。

Generic Sensor API は非常にシンプルで使いやすい API です。Sensor インターフェースには、センサーの状態を制御する start() メソッドと stop() メソッド、センサーの有効化、エラー、新たに利用可能な測定値に関する通知を受け取るためのいくつかのイベント ハンドラがあります。通常、具体的なセンサー クラスは、特定の読み取り属性をベースクラスに追加します。

開発環境

開発中は、localhost を通じてセンサーを使用できます。モバイル デバイス向けに開発している場合は、ローカル サーバーのポート転送を設定すれば、準備完了です。

コードの準備ができたら、HTTPS をサポートするサーバーにデプロイします。GitHub Pages は HTTPS で配信されるため、デモを共有するのに最適な場所です。

3D モデルの回転

この簡単な例では、絶対方位センサーのデータを使用して 3D モデルの回転クォータニオンを変更します。model は、quaternion プロパティを持つ three.js Object3D クラスのインスタンスです。向きの電話デモの次のコード スニペットは、絶対向きセンサーを使用して 3D モデルを回転させる方法を示しています。

function initSensor() {
  sensor = new AbsoluteOrientationSensor({ frequency: 60 });
  sensor.onreading = () => model.quaternion.fromArray(sensor.quaternion);
  sensor.onerror = (event) => {
    if (event.error.name == 'NotReadableError') {
      console.log('Sensor is not available.');
    }
  };
  sensor.start();
}

デバイスの向きは、WebGL シーン内の 3D model 回転に反映されます。

センサーが 3D モデルの向きを更新する
センサーが 3D モデルの向きを更新

パンチメーター

次のコード スニペットは、パンチメーターのデモから抽出したものです。このコードは、デバイスが最初に静止していると仮定して、線形加速度センサーを使用してデバイスの最大速度を計算する方法を示しています。

this.maxSpeed = 0;
this.vx = 0;
this.ax = 0;
this.t = 0;

/* … */

this.accel.onreading = () => {
  let dt = (this.accel.timestamp - this.t) * 0.001; // In seconds.
  this.vx += ((this.accel.x + this.ax) / 2) * dt;

  let speed = Math.abs(this.vx);

  if (this.maxSpeed < speed) {
    this.maxSpeed = speed;
  }

  this.t = this.accel.timestamp;
  this.ax = this.accel.x;
};

現在の速度は、加速度関数の積分を近似値として計算されます。

パンチ速度測定用のデモ ウェブ アプリケーション
パンチの速度の測定

Chrome DevTools を使用したデバッグとセンサーのオーバーライド

Generic Sensor API を使用するために実機が必要ない場合もあります。Chrome DevTools では、デバイスの向きのシミュレーションが強力にサポートされています。

仮想スマートフォンのカスタム方向データをオーバーライドするために使用される Chrome DevTools
Chrome DevTools でデバイスの向きをシミュレートする

プライバシーとセキュリティ

センサーの読み取り値は機密データであり、悪意のあるウェブページからのさまざまな攻撃を受ける可能性があります。Generic Sensor API の実装では、セキュリティとプライバシーのリスクを軽減するためにいくつかの制限が適用されます。API を使用するデベロッパーは、これらの制限を考慮する必要があります。以下に簡単にまとめます。

HTTPS のみ

Generic Sensor API は強力な機能であるため、ブラウザでは安全なコンテキストでのみ使用できます。実際には、Generic Sensor API を使用するには、HTTPS 経由でページにアクセスする必要があります。開発中は http://localhost を使用できますが、本番環境ではサーバーで HTTPS を使用する必要があります。ベスト プラクティスとガイドラインについては、安全とセキュリティ コレクションをご覧ください。

権限に関するポリシーの統合

Generic Sensor API の権限ポリシーの統合は、フレームのセンサー データへのアクセスを制御します。

デフォルトでは、Sensor オブジェクトはメインフレームまたは同一オリジンのサブフレーム内でのみ作成できるため、クロスオリジン iframe によるセンサーデータの不正な読み取りを防ぐことができます。このデフォルトの動作は、対応するポリシー制御機能を明示的に有効または無効にすることで変更できます。

次のスニペットは、クロスオリジン iframe に加速度計データへのアクセス権を付与する例を示しています。つまり、これで Accelerometer オブジェクトまたは LinearAccelerationSensor オブジェクトをそこで作成できるようになります。

<iframe src="https://third-party.com" allow="accelerometer" />

センサーの測定値の配信を一時停止できる

センサーの読み取り値は、ユーザーが実際に操作しているなど、表示されているウェブページからのみアクセスできます。また、ユーザーのフォーカスがクロスオリジン サブフレームに移動した場合、センサーデータは親フレームに提供されません。これにより、親フレームがユーザー入力を推測することを防ぎます。

次のステップ

周囲光センサー近接センサーなど、近い将来に実装されることがすでに指定されているセンサー クラスのセットがあります。ただし、汎用センサー フレームワークの優れた拡張性のおかげで、さまざまなセンサータイプを表す新しいクラスがさらに登場することが予想されます。

今後の重要な取り組みの 1 つは、Generic Sensor API 自体の改善です。Generic Sensor の仕様は現在候補勧告であり、デベロッパーが必要とする修正や新機能の導入にはまだ時間があります。

ぜひご協力ください。

センサーの仕様が候補の推奨事項の成熟度レベルに達したため、ウェブ デベロッパーとブラウザ デベロッパーからのフィードバックを歓迎します。追加すると便利な機能や、現在の API で変更したい点などがありましたら、お知らせください。

Chrome の実装に関するバグだけでなく、仕様に関する問題もお気軽にご報告ください。

リソース

謝辞

この記事は、Joe MedleyKayce Basques によってレビューされました。