Generic Sensor API を使用して、加速度計、ジャイロスコープ、磁力計などのデバイス上のセンサーにアクセスします。
現在、センサーデータは多くのプラットフォーム固有のアプリケーションで使用され、臨場感のあるゲーム、フィットネス トラッキング、拡張現実や仮想現実などのユースケースを実現しています。プラットフォーム固有のアプリケーションとウェブ アプリケーションのギャップを埋めることができたら、便利だと思いませんか?ウェブ向けの Generic Sensor API をご紹介します。
Generic Sensor API とは何ですか?
Generic Sensor API は、センサー デバイスをウェブ プラットフォームに公開する一連のインターフェースです。この API は、ベースの Sensor
インターフェースと、それを利用する一連の具象センサークラスで構成されています。ベース インターフェースを使用すると、具体的なセンサークラスの実装と仕様プロセスが簡素化されます。たとえば、Gyroscope
クラスをご覧ください。とても小さいです。コア機能はベース インターフェースで指定され、Gyroscope
は角速度を表す 3 つの属性で拡張するだけです。
一部のセンサークラスは、加速度計クラスやジャイロスコープ クラスなど、実際のハードウェア センサーとインターフェースします。これらは低レベル センサーと呼ばれます。その他のセンサー(融合センサー)は、複数の低レベル センサーのデータを統合して、スクリプトで計算する必要のある情報を公開します。たとえば、AbsoluteOrientation
センサーは、加速度計、ジャイロスコープ、地磁気センサーから取得したデータに基づいて、すぐに使用できる 4×4 の回転行列を提供します。
ウェブ プラットフォームでセンサーデータがすでに提供されていると思われるかもしれませんが、そのとおりです。たとえば、DeviceMotion
イベントと DeviceOrientation
イベントはモーション センサー データを公開します。では、新しい API が必要な理由は何でしょうか。
既存のインターフェースと比較すると、Generic Sensor API には次のような多くの利点があります。
- Generic Sensor API は、新しいセンサークラスで簡単に拡張できるセンサー フレームワークです。これらのクラスはそれぞれ汎用インターフェースを保持します。1 つのセンサータイプ用に作成されたクライアント コードは、ほとんど変更せずに別のセンサータイプで再利用できます。
- センサーを構成できます。たとえば、アプリケーションのニーズに適したサンプリング頻度を設定できます。
- プラットフォームでセンサーが利用可能かどうかを検出できます。
- センサー測定値には高精度のタイムスタンプが付加されているため、アプリ内の他のアクティビティとの同期がより正確になります。
- センサーデータモデルと座標系が明確に定義されているため、ブラウザ ベンダーは相互運用可能なソリューションを実装できます。
- 汎用センサーベースのインターフェースは DOM にバインドされていません(つまり、
navigator
オブジェクトでもwindow
オブジェクトでもありません)。これにより、サービス ワーカー内で API を使用したり、埋め込みデバイスなどのヘッドレス JavaScript ランタイムに実装したりできるようになります。 - セキュリティとプライバシーは、汎用センサー 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 をサポートしていないブラウザの場合は、ポリフィルを使用できます。ポリフィルを使用すると、関連するセンサーの実装のみを読み込むことができます。
// 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 の加速度を測定します。
重力センサー
ユーザーは、Accelerometer
と LinearAccelerometer
の測定値を手動で検査することで、重力センサーの測定値に近い測定値を手動で導出できますが、これは手間がかかり、これらのセンサーから提供される値の精度に依存します。Android などのプラットフォームでは、オペレーティング システムの一部として重力測定値を提供できます。これは、計算の面で低コストで、ユーザーのハードウェアに応じてより正確な値を提供し、API の使い勝手の面でより使いやすいものです。GravitySensor
は、重力によるデバイスの X、Y、Z 軸の加速度の効果を返します。
ジャイロスコープ
Gyroscope
センサーは、デバイスのローカル X、Y、Z 軸周りの角速度をラジアン / 秒単位で測定します。ほとんどのコンシューマ デバイスには、機械式(MEMS)ジャイロスコープが搭載されています。これは、慣性コリオリ力に基づいて回転率を測定する慣性センサーです。MEMS ジャイロスコープは、センサーの重力感度によってセンサーの内部機械システムが変形し、ドリフトが発生しやすくなります。ジャイロスコープは比較的高い周波数で振動します(10 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 は非常にシンプルで使いやすい 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
の回転に反映されます。
パンチメーター
次のコード スニペットは、パンチメーター デモから抜粋したものです。デバイスが最初は静止していると仮定して、リニア加速度センサーを使用してデバイスの最大速度を計算する方法を示しています。
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 内に作成できるようになります。
<iframe src="https://third-party.com" allow="accelerometer" />
センサーの測定値の配信が一時停止される
センサーの測定値にアクセスできるのは、表示されているウェブページ(ユーザーが実際に操作しているページ)に限られます。また、ユーザーのフォーカスがクロスオリジン サブフレームに移動した場合、センサーデータは親フレームに提供されません。これにより、親フレームがユーザー入力を推測するのを防ぐことができます。
次のステップ
周囲光センサーや近接センサーなど、近い将来実装される予定のセンサークラスがすでにいくつかあります。ただし、汎用センサー フレームワークの優れた拡張性により、さまざまなセンサータイプを表す新しいクラスがさらに増えることが予想されます。
今後の作業の重要な分野として、Generic Sensor API 自体の改善があります。Generic Sensor 仕様は現在候補推奨仕様です。つまり、デベロッパーが必要とする修正や新機能を追加する時間はまだあります。
ご協力いただけます。
センサーの仕様は候補の推奨事項の成熟度レベルに達しています。そのため、ウェブ デベロッパーとブラウザ デベロッパーからのフィードバックをお待ちしております。追加したい機能や、現在の API で変更したい点がございましたら、お知らせください。
Chrome の実装に関する仕様に関する問題やバグをお気軽に報告してください。
リソース
- デモ プロジェクト: https://intel.github.io/generic-sensor-demos/
- Generic 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 が確認しました。