Generic Sensor API を使用して、加速度計、ジャイロスコープ、磁力計などのデバイス上のセンサーにアクセスできます。
現在、センサーデータは、没入型ゲーム、フィットネス トラッキング、拡張 / バーチャル リアリティなどのユースケースを可能にするために、多くのプラットフォーム固有のアプリで使用されています。プラットフォーム固有のアプリケーションと ウェブアプリケーションのギャップを埋めるのはウェブの場合は、Generic Sensor API を入力します。
Generic Sensor API とは何ですか?
Generic Sensor API は、センサー デバイスをウェブ プラットフォームに公開するインターフェースのセットです。この API は、基本の Sensor
インターフェースと、その上に構築された具体的なセンサークラスのセットで構成されます。ベース インターフェースがあると、具体的なセンサークラスの実装と指定のプロセスが簡単になります。例として、Gyroscope
クラスをご覧ください。すごく小さいんだ!コア機能はベース インターフェースで指定され、Gyroscope
はそれを角速度を表す 3 つの属性で拡張するだけです。
一部のセンサークラスは、加速度計やジャイロスコープのクラスなど、実際のハードウェア センサーとインターフェースします。これらは低レベルセンサーと呼ばれます。フュージョン センサーと呼ばれる他のセンサーは、複数の低レベルのセンサーのデータを結合して、スクリプトが計算する必要がある情報を公開します。たとえば、AbsoluteOrientation
センサーは、加速度計、ジャイロスコープ、磁力計から取得したデータに基づいて、すぐに使用できる 4 x 4 の回転行列を提供します。
ウェブ プラットフォームがすでにセンサーデータを提供しているとお考えかもしれませんが、そのとおりです。たとえば、DeviceMotion
イベントと DeviceOrientation
イベントによってモーション センサーデータが公開されます。では、なぜ新しい API が必要なのでしょうか。
既存のインターフェースと比較して、Generic Sensor API には多くの利点があります。
- Generic Sensor API は、新しいセンサークラスを使用して簡単に拡張できるセンサー フレームワークで、これらの各クラスは汎用インターフェースを維持します。あるセンサータイプ用に記述されたクライアント コードは、ほとんど変更せずに別のセンサータイプに再利用できます。
- センサーを設定できます。たとえば、アプリケーションのニーズに適したサンプリング頻度を設定できます。
- プラットフォームでセンサーが使用可能かどうかを検出できます。
- センサーの読み取り値のタイムスタンプは高精度であるため、アプリ内の他のアクティビティとの同期が向上します。
- センサーのデータモデルと座標系が明確に定義されているため、ブラウザ ベンダーは相互運用可能なソリューションを実装できます。
- 汎用センサーベースのインターフェースは DOM にバインドされていません(つまり、
navigator
オブジェクトでもwindow
オブジェクトでもないため)。これにより、今後、Service Worker 内で API を使用したり、埋め込みデバイスなどのヘッドレス JavaScript ランタイムに API を実装したりできます。 - セキュリティとプライバシーは Generic Sensor API の最優先事項であり、古いセンサー API と比較してはるかに優れたセキュリティを提供します。Permissions API と統合されています
Accelerometer
、Gyroscope
、LinearAccelerationSensor
、AbsoluteOrientationSensor
、RelativeOrientationSensor
、Magnetometer
では、画面座標との自動同期を使用できます。
利用可能な汎用センサー 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/s になります)。2デバイスを右に押すと X 軸の加速度は正になり、デバイスを右から左に加速すると負になります。
加速度計は、歩数計、モーション検知、シンプルなデバイスの向きなどに使用できます。多くの場合、加速度計の測定値は他のソースからのデータと組み合わせて、方向センサーなどのフュージョン センサーを作成します。
LinearAccelerationSensor
は、重力の分を除いて、センサーをホストするデバイスに適用される加速度を測定します。デバイスが静止しているとき、たとえばテーブルの上に平らに置かれているとき、センサーは 3 軸で約 0 m/s2 の加速度を測定します。
重力センサー
Accelerometer
と LinearAccelerometer
の測定値を手動で検査することで、重力センサーの測定値に近い測定値を手動で導き出すことはすでに可能ですが、面倒なことがあり、センサーによって提供される値の精度にも依存します。Android などのプラットフォームは、オペレーティング システムの一部として重力の読み取り値を提供できます。これは計算の点でコストが低く、ユーザーのハードウェアに応じてより正確な値を提供し、API エルゴノミクスの観点からより使いやすいものでなければなりません。GravitySensor
は、重力によるデバイスの X、Y、Z 軸に沿った加速度の影響を返します。
ジャイロスコープ
Gyroscope
センサーは、デバイスのローカルの X、Y、Z 軸を中心とした角速度をラジアン/秒で測定します。ほとんどの消費者向けデバイスには、慣性コリオリ力に基づいて回転数を測定する慣性センサーである機械(MEMS)ジャイロスコープが搭載されています。MEMS ジャイロスコープは、センサーの重力感度によってセンサーの内部機械システムが変形するため、ドリフトが発生しやすくなります。ジャイロスコープは、数十 kHz であるため、他のセンサーよりも電力を消費する可能性があります。
方向センサー
AbsoluteOrientationSensor
は地球の座標系を基準としてデバイスの回転を測定するフュージョン センサーであり、RelativeOrientationSensor
は、静止基準座標系に対するモーション センサーをホストするデバイスの回転を表すデータを提供します。
最新の 3D JavaScript フレームワークはすべて、回転を表す四元数と回転行列をサポートしています。ただし、WebGL を直接使用する場合は、OrientationSensor
に quaternion
プロパティと populateMatrix()
メソッドの両方があることが便利です。以下にスニペットを示します。
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();
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();
// 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 は、はるかにシンプルで信頼性の高いソリューションを提供します。ローカル座標系は、定義済みのすべての空間センサークラス(Accelerometer
、Gyroscope
、LinearAccelerationSensor
、AbsoluteOrientationSensor
、RelativeOrientationSensor
、Magnetometer
)に対して構成できます。ユーザーは、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 は、とてもシンプルで使いやすいツールです。センサー インターフェースには、センサーの状態を制御する start()
メソッドと stop()
メソッドがあり、センサーの有効化、エラー、新たに利用可能になった測定値に関する通知を受信するためのイベント ハンドラがあります。具体的なセンサークラスは通常、固有の読み取り属性を基本クラスに追加します。
開発環境
開発中は、localhost
を通じてセンサーを使用できます。モバイル デバイス向けに開発している場合は、ローカル サーバー用にポート転送を設定すれば準備は完了です。
コードの準備ができたら、HTTPS をサポートするサーバーにデプロイします。GitHub ページは HTTPS 経由で提供されるため、デモを共有するのに最適です。
3D モデルの回転
この簡単な例では、絶対方位センサーのデータを使用して、3D モデルの回転四元数を変更しています。model
は、quaternion
プロパティを持つ 3.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
回転に反映されます。
パンチメーター
次のコード スニペットは、パンチメーターのデモから抜粋したものです。これは、初期状態で静止していると仮定して、直線加速度センサーを使用してデバイスの最大速度を計算する方法を示しています。
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 は、デバイスの向きのシミュレーションに優れています。
プライバシーとセキュリティ
センサーの読み取り値はセンシティブ データであり、悪意のあるウェブページからさまざまな攻撃を受ける可能性があります。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 の実装に関する仕様に関する問題やbugsは、ご遠慮なくお知らせください。
リソース
- デモ プロジェクト: https://intel.github.io/generic-sensor-demos/
- 一般的な Sensor API の仕様: https://w3c.github.io/sensors/
- 仕様の問題: https://github.com/w3c/sensors/issues
- W3C ワーキング グループのメーリング リスト: public-device-apis@w3.org
- Chrome 機能のステータス: https://www.chromestatus.com/feature/5698781827825664
- 実装のバグ: http://crbug.com?q=component:Blink>Sensor
謝辞
この記事は、Joe Medley と Kayce Basques によってレビューされました。ヒーロー画像(Misko、Wikimedia Commons 提供)。