Czujniki internetu

Użyj interfejsu Generic Sensor API, aby uzyskać dostęp do czujników na urządzeniu, takich jak akcelerometry, żyroskopy i magnetometry.

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

Obecnie dane z czujników są używane w wielu aplikacjach na konkretne platformy, aby umożliwić takie zastosowania jak gry z rozszerzoną rzeczywistością, monitorowanie aktywności czy rzeczywistość rozszerzona i wirtualna. Czy nie byłoby fajnie połączyć aplikacje internetowe z aplikacją na platformę? Wpisz Generic Sensor API w internecie.

Ogólny Sensor API to zestaw interfejsów, które udostępniają czujniki platformie internetowej. Interfejs API składa się z podstawowego interfejsu Sensor oraz zestawu konkretnych klas czujników, które są na nim oparte. Dzięki interfejsowi podstawowemu upraszcza się proces implementacji i specyfikacji konkretnych klas czujników. Spójrzmy na przykład na klasę Gyroscope. Jest bardzo mały. Główna funkcjonalność jest określana przez interfejs podstawowy, a Gyroscope rozszerza ją za pomocą 3 atrybutów reprezentujących prędkość kątową.

Niektóre klasy czujników współpracują z rzeczywistymi czujnikami sprzętowymi, takimi jak klasy akcelerometru czy żyroskopu. Są to tak zwane czujniki niskiego poziomu. Inne czujniki, nazywane czujnikami Fusion, łączą dane z kilku czujników niskiego poziomu, aby ujawnić informacje, które skrypt musiałby w innym przypadku obliczyć. Na przykład czujnik AbsoluteOrientation udostępnia gotową do użycia macierz obrotu 4 × 4 na podstawie danych uzyskanych z akcelerometru, żyroskopu i magnetometru.

Możesz myśleć, że platforma internetowa już udostępnia dane z czujników. I masz absolutną rację! Na przykład zdarzenia DeviceMotionDeviceOrientation udostępniają dane z czujnika ruchu. Dlaczego więc potrzebujemy nowego interfejsu API?

W porównaniu z dotychczasowymi interfejsami interfejs Generic Sensor API zapewnia wiele korzyści:

  • Interfejs Generic Sensor API to framework czujników, który można łatwo rozszerzyć o nowe klasy czujników. Każda z tych klas będzie miała interfejs ogólny. Kod klienta napisany dla jednego typu czujnika można wykorzystać do innego z niewielkimi modyfikacjami.
  • Możesz skonfigurować czujnik. Możesz na przykład ustawić częstotliwość próbkowania odpowiednią do potrzeb aplikacji.
  • Możesz wykryć, czy czujnik jest dostępny na platformie.
  • Odczyty czujników mają znaczniki czasu o wysokiej dokładności, co umożliwia lepszą synchronizację z innymi działaniami w aplikacji.
  • Modele danych czujników i systemy współrzędnych są jasno zdefiniowane, dzięki czemu dostawcy przeglądarek mogą wdrażać współdziałające rozwiązania.
  • Interfejsy oparte na typowym czujniku nie są powiązane z DOM (co oznacza, że nie są obiektami navigator ani window). Daje to możliwość przyszłego korzystania z interfejsu API w ramach wątków usług lub implementowania go w bezgłowikowych środowiskach uruchomieniowych JavaScriptu, takich jak urządzenia wbudowane.
  • Bezpieczeństwo i prywatność są priorytetem w przypadku interfejsu Generic Sensor API i zapewniają znacznie lepsze zabezpieczenia w porównaniu ze starszymi interfejsami sensorów API. Istnieje integracja z interfejsem Permissions API.
  • Automatyczna synchronizacja z koordynatami ekranu jest dostępna w przypadku usług Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensorMagnetometer.

Dostępne ogólne interfejsy API czujników

W momencie pisania tego artykułu dostępnych jest kilka czujników, z którymi możesz eksperymentować.

Czujniki ruchu:

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

Czujniki środowiskowe:

  • AmbientLightSensor (za flagą #enable-generic-sensor-extra-classes w Chromium).
  • Magnetometer (za flagą #enable-generic-sensor-extra-classes w Chromium).

Wykrywanie cech

Wykrywanie funkcji interfejsów API sprzętowych jest trudne, ponieważ trzeba wykryć, czy przeglądarka obsługuje dany interfejs oraz czy urządzenie ma odpowiedni czujnik. Sprawdzenie, czy przeglądarka obsługuje interfejs, jest proste. (Zastąp Accelerometer dowolnym interfejsem wymienionym wyżej).

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

Aby uzyskać wiarygodny wynik wykrywania funkcji, musisz też spróbować połączyć się z czujnikiem. Ten przykład pokazuje, jak to zrobić.

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;
  }
}

Watolina

W przypadku przeglądarek, które nie obsługują interfejsu Generic Sensor API, dostępna jest funkcja polyfill. Dzięki polyfillowi możesz wczytywać tylko implementacje odpowiednich czujników.

// 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 });

Co to za wszystkie te czujniki? Jak mogę ich używać?

Czujniki to obszar, który może wymagać krótkiego wprowadzenia. Jeśli znasz się na czujnikach, możesz przejść od razu do sekcji poświęconej programowaniu praktycznemu. W przeciwnym razie przyjrzyjmy się dokładniej każdemu obsługiwanemu czujnikowi.

Akcelerometr i czujnik przyspieszenia liniowego

Dane z czujnika przyspieszeniomierza

Czujnik Accelerometer mierzy przyspieszenie urządzenia hostującego czujnik na 3 osiach (X, Y i Z). Jest to czujnik bezwładnościowy, co oznacza, że gdy urządzenie jest w liniowym swobodnym spadku, całkowite zmierzone przyspieszenie będzie wynosić 0 m/s2, a gdy urządzenie leży na stole, przyspieszenie w kierunku w górę (oś Z) będzie równe przyspieszeniu ziemskiemu, czyli g ≈ +9,8 m/s2, ponieważ mierzy siłę stołu, który wypycha urządzenie w górę. Jeśli naciśniesz urządzenie w prawo, przyspieszenie na osi X będzie dodatnie. Jeśli urządzenie przyspiesza z prawa w lewo, przyspieszenie na osi X będzie ujemne.

Akcelerometry mogą być używane do liczenia kroków, wykrywania ruchu lub prostej orientacji urządzenia. Często pomiary akcelerometru są łączone z danymi z innych źródeł, aby tworzyć czujniki fuzji, takie jak czujniki orientacji.

LinearAccelerationSensor zbiera dane o przyspieszeniu, które jest stosowane do urządzenia, na którym znajduje się czujnik, z wyłączeniem wpływu grawitacji. Gdy urządzenie jest nieruchome, np. leży na stole, czujnik mierzy przyspieszenie ≈ 0 m/s2 na trzech osiach.

Czujnik grawitacyjny

Użytkownicy mogą już ręcznie uzyskiwać odczyty zbliżone do odczytów z czujnika grawitacyjnego, ręcznie sprawdzając odczyty AccelerometerLinearAccelerometer, ale może to być kłopotliwe i zależy od dokładności wartości dostarczanych przez te czujniki. Platformy takie jak Android mogą dostarczać odczyty grawitacji w ramach systemu operacyjnego, co powinno być tańsze pod względem obliczeń, dostarczać dokładniejsze wartości w zależności od sprzętu użytkownika i łatwiejsze w obsłudze pod względem ergonomii interfejsu API. GravitySensor zwraca efekt przyspieszenia wzdłuż osi X, Y i Z urządzenia wywołanego grawitacją.

Żyroskop

Pomiarów czujnika żyroskopu

Czujnik Gyroscope mierzy prędkość kątową w radianach na sekundę wokół lokalnej osi X, Y i Z urządzenia. Większość urządzeń konsumenckich ma żyroskopy mechaniczne (MEMS), czyli czujniki bezwładnościowe, które mierzą prędkość obrotu na podstawie bezwładnościowej siły Coriolisa. Żyroskopy MEMS są podatne na dryf spowodowany przez czułość na przyciąganie ziemskie, która deformuje wewnętrzny system mechaniczny czujnika. Żyroskopy oscylują z względnie wysokimi częstotliwościami, np. 10 kHz, a zatem może zużywać więcej energii w porównaniu z innymi czujnikami.

Czujniki orientacji

Pomiary z czujników orientacji bezwzględnej

AbsoluteOrientationSensor to czujnik fuzji, który mierzy obrót urządzenia w odniesieniu do układu współrzędnych Ziemi, a RelativeOrientationSensor udostępnia dane przedstawiające obrót urządzenia hostującego czujniki ruchu w odniesieniu do nieruchomego układu współrzędnych.

Wszystkie nowoczesne platformy JavaScript 3D obsługują kwantyny i macierze obrotu reprezentujące rotację. Jeśli jednak używasz WebGL bezpośrednio, OrientationSensor ma w dogodny sposób zarówno właściwości quaternion, jak i populateMatrix(). Oto kilka fragmentów kodu:

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);

Czujniki orientacji umożliwiają różne zastosowania, np. granie w gry, rzeczywistość rozszerzoną czy rzeczywistość wirtualną.

Więcej informacji o czujnikach ruchu, zaawansowanych przypadkach użycia i wymaganiach znajdziesz w objaśnieniu czujników ruchu.

Synchronizacja ze współrzędnymi ekranu

Domyślnie odczyty czujników przestrzennych są przetwarzane w lokalnym układzie współrzędnych powiązanym z urządzeniem i nie uwzględniającym orientacji ekranu.

Układ współrzędnych urządzenia
Układ współrzędnych urządzenia

Jednak wiele zastosowań, takich jak gry czy rozszerzona i wirtualna rzeczywistość, wymaga, aby odczyty czujników były przetwarzane w układzie współrzędnych powiązanym z orientacją ekranu.

System współrzędnych ekranu
System współrzędnych ekranu

Wcześniej przemapowanie odczytów czujnika na współrzędne ekranu musiało być zaimplementowane w JavaScript. Takie podejście jest nieefektywne i znacznie zwiększa złożoność kodu aplikacji internetowej. Aplikacja internetowa musi obserwować zmiany orientacji ekranu i przekształcać współrzędne odczytów czujnika, co nie jest trywialne w przypadku kątów Eulera ani kwaternionów.

Interfejs Generic Sensor API zapewnia znacznie prostsze i bardziej niezawodne rozwiązanie. Lokalny system współrzędnych można skonfigurować dla wszystkich zdefiniowanych klas czujników przestrzennych: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensorMagnetometer. Przekazując opcję referenceFrame do konstruktora obiektu czujnika, użytkownik określa, czy zwrócone odczyty zostaną zatwierdzone we współrzędnych urządzenie czy ekran.

// 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' });

Kodujmy!

Interfejs Generic Sensor API jest bardzo prosty i łatwy w użyciu. Interfejs Sensor zawiera metody start()stop() służące do kontrolowania stanu czujnika oraz kilka metod obsługi zdarzeń do odbierania powiadomień o aktywowaniu czujnika, błędach i dostępnych nowych odczytach. Klasy konkretnych czujników zwykle dodają do klasy podstawowej określone atrybuty odczytu.

Środowisko programistyczne

Podczas tworzenia możesz używać czujników za pomocą localhost. Jeśli tworzysz aplikacje na urządzenia mobilne, skonfiguruj przekierowanie portów na serwerze lokalnym.

Gdy kod będzie gotowy, wdróż go na serwerze, który obsługuje HTTPS. Strony w GitHub są wyświetlane przy użyciu protokołu HTTPS, co czyni je świetnym miejscem do udostępniania wersji demonstracyjnych.

Obracanie modelu 3D

W tym prostym przykładzie używamy danych z czujnika orientacji bezwzględnej, aby zmodyfikować rotację kwaterniony modelu 3D. Obiekt model jest instancją klasy Object3D z biblioteki three.js, która ma właściwość quaternion. Ten fragment kodu z prezentacji orientacji telefonu pokazuje, jak za pomocą bezwzględnego czujnika orientacji można obracać model 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();
}

Orientacja urządzenia będzie odzwierciedlona w obrocie 3D model w scenie WebGL.

Sensor aktualizuje orientację modelu 3D
Czujnik aktualizuje orientację modelu 3D

Punchmeter

Ten fragment kodu został wyodrębniony z demo punchmetru i pokazuje, jak za pomocą czujnika przyspieszenia liniowego można obliczyć maksymalną prędkość urządzenia przy założeniu, że początkowo leży ono nieruchomo.

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;
};

Prędkość bieżąca jest obliczana w przybliżeniu do całki z funkcji przyspieszania.

Wersja demonstracyjna aplikacji internetowej do pomiaru szybkości przebijania
Pomiar szybkości uderzenia pięścią

Debugowanie i zastępowanie czujników w Narzędziach deweloperskich w Chrome

W niektórych przypadkach do korzystania z interfejsu Generic Sensor API nie musisz mieć fizycznego urządzenia. Narzędzia deweloperskie w Chrome zapewniają doskonałe wsparcie w zakresie symulowania orientacji urządzenia.

Narzędzia deweloperskie w Chrome służące do zastępowania danych dotyczących niestandardowej orientacji telefonu wirtualnego
Symulowanie orientacji urządzenia za pomocą Narzędzi deweloperskich w Chrome

Prywatność i bezpieczeństwo

Dane z czujników to dane wrażliwe, które mogą być narażone na różne ataki ze strony złośliwych stron internetowych. Implementacje interfejsów API czujników ogólnych narzucają kilka ograniczeń, aby ograniczyć możliwe zagrożenia dla bezpieczeństwa i prywatności. Deweloperzy, którzy chcą korzystać z interfejsu API, muszą wziąć pod uwagę te ograniczenia. Oto ich krótki opis.

Tylko HTTPS

Interfejs Generative Sensor API jest zaawansowanymi funkcjami, więc przeglądarka zezwala na niego tylko w bezpiecznych kontekstach. W praktyce oznacza to, że aby korzystać z interfejsu Ogólna Sensor API, musisz uzyskiwać dostęp do strony przez HTTPS. Podczas tworzenia możesz to zrobić za pomocą adresu http://localhost, ale w wersji produkcyjnej musisz mieć na serwerze protokół HTTPS. Sprawdzone metody i wytyczne znajdziesz w kolekcji Bezpieczeństwo i bezpieczeństwo.

Integracja z zasadami dotyczącymi uprawnień

Integracja z zasadami uprawnień w interfejsie Ogólny Sensor API kontroluje dostęp do danych z czujników w ramce.

Domyślnie obiekty Sensor mogą być tworzone tylko w ramach głównego elementu iframe lub podelementów iframe tego samego pochodzenia, co uniemożliwia nieautoryzowane odczytywanie danych z czujników przez iframe z innego pochodzenia. To domyślne zachowanie można zmienić, wyraźnie włączając lub wyłączając odpowiednie funkcje kontrolowane przez zasady.

Poniżej znajduje się fragment kodu, który pokazuje, jak przyznać dostęp do danych akcelerometru do ramki iframe w innej domenie. Oznacza to, że teraz można w niej tworzyć obiekty Accelerometer lub LinearAccelerationSensor.

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

Dostarczanie odczytów z czujników może zostać zawieszone.

Dane z czujników są dostępne tylko dla widocznej strony internetowej, czyli wtedy, gdy użytkownik faktycznie z nią współpracuje. Ponadto dane z czujnika nie będą przekazywane do nadrzędnego elementu frame, jeśli użytkownik przeniesie fokus na element frame w innej domenie. Zapobiega to wnioskowaniu o dane wejściowe użytkownika przez ramkę nadrzędną.

Co dalej?

W najbliższej przyszłości zostanie zaimplementowany zestaw już zdefiniowanych klas czujników, takich jak czujnik jasności otoczenia czy czujnik zbliżeniowy. Dzięki dużej możliwości rozszerzalności Generic Sensor framework możemy się spodziewać pojawienia się jeszcze większej liczby nowych klas reprezentujących różne typy czujników.

Kolejnym ważnym obszarem prac jest ulepszanie samego interfejsu Generic Sensor API. Specyfikacja Generic Sensor jest obecnie w fazie rekomendacji, co oznacza, że wciąż jest czas na wprowadzanie poprawek i dodawanie nowych funkcji potrzebnych deweloperom.

Możesz pomóc!

Specyfikacje czujników osiągnęły poziom Candidate Recommendation, dlatego bardzo cenimy opinie programistów stron internetowych i przeglądarek. Daj nam znać, jakie funkcje warto dodać lub co można zmienić w obecnym interfejsie API.

Zgłaszaj problemy ze specyfikacją oraz błędy związane z implementacją w Chrome.

Zasoby

Podziękowania

Ten artykuł został sprawdzony przez Joe MedleyKayce Basques.