Czujniki internetu

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

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 lub wirtualna. Nie byłoby wspaniale, gdyby można było połączyć aplikacje internetowe z aplikacją na konkretne platformy? Wpisz Generic Sensor API w internecie.

Generic Sensor API to zestaw interfejsów, które udostępniają urządzenia czujnikowe 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. Na przykład spójrz na klasę Gyroscope. Jest bardzo mały. Główna funkcjonalność jest określana przez interfejs podstawowy, a Gyroscope rozszerza ją tylko o 3 atrybuty reprezentujące 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, zwane czujnikami fuzji, łączą dane z kilku czujników niskiego poziomu, aby udostępniać informacje, które skrypt musiałby inaczej 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 czujnika ruchu. Dlaczego więc potrzebujemy nowego interfejsu API?

W porównaniu z dotychczasowymi interfejsami Generic Sensor API oferuje wiele zalet:

  • 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, co pozwala dostawcom przeglądarek wdrażać interoperacyjne 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 niż starsze interfejsy API czujników. 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 interfejsy API czujników ogólnych

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 czujniki? Jak mogę ich używać?

Czujniki to obszar, który 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ę szczegółowo każdemu obsługiwanemu czujnikowi.

Akcelerometr i czujnik przyspieszenia liniowego

Dane z czujnika akcelerometru

Czujnik Accelerometer mierzy przyspieszenie urządzenia, na którym jest zainstalowany, na trzech 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ć uciążliwe i zależy od dokładności wartości dostarczanych przez te czujniki. Platformy takie jak Android mogą udostępniać odczyty grawitacji w ramach systemu operacyjnego, co powinno być tańsze pod względem obliczeń, zapewniać dokładniejsze wartości w zależności od sprzętu użytkownika i być łatwiejsze w użyciu pod względem ergonomii interfejsu API. Funkcja GravitySensor zwraca efekt przyspieszenia wzdłuż osi X, Y i Z urządzenia spowodowany grawitacją.

Żyroskop

Pomiarów czujnika żyroskopu

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

Czujniki orientacji

Absolute orientation sensor measurements

AbsoluteOrientationSensor to czujnik fuzji, który mierzy obrót urządzenia w stosunku do układu współrzędnych Ziemi, podczas gdy RelativeOrientationSensor zawiera dane przedstawiające obrót urządzenia z czujnikami ruchu w stosunku do stałego układu odniesienia.

Wszystkie nowoczesne platformy JavaScript 3D obsługują kwaternionymacierze obrotu, aby reprezentować obrót. Jeśli jednak używasz bezpośrednio WebGL, obiekt OrientationSensor ma zarówno właściwość quaternion, jak i metodę 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, takie jak wciągające gry, rzeczywistość rozszerzona i wirtualna.

Więcej informacji o czujnikach ruchu, zaawansowanych zastosowaniach i wymaganiach znajdziesz w artykule Omówienie czujników ruchu.

Synchronizacja z koordynatami 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ą orientacji ekranu.

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

Jednak w wielu przypadkach, np. w grach lub rzeczywistości rozszerzonej i wirtualnej, odczyty czujników muszą być 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 proste 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 będą rozwiązywane w układzie współrzędnych urządzenia czy ekranu.

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

Zacznijmy kodować.

Interfejs Generic Sensor API jest bardzo prosty i łatwy w użyciu. Interfejs czujnika 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 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 updates 3D model's orientation
Czujnik aktualizuje orientację modelu 3D

Punchmeter

Ten fragment kodu został wyodrębniony z demo urządzenia do pomiaru uderzeń 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;
};

Bieżąca prędkość jest obliczana jako przybliżenie całki funkcji przyspieszenia.

Demonstracyjna aplikacja internetowa do pomiaru szybkości uderzenia
Pomiar szybkości uderzenia pięścią

Debugowanie i zastępowanie czujników za pomocą Narzędzi deweloperskich w Chrome

W niektórych przypadkach do zabawy z interfejsem Generic Sensor API nie potrzebujesz fizycznego urządzenia. Narzędzia deweloperskie w Chrome zapewniają doskonałe wsparcie w zakresie symulowania orientacji urządzenia.

Narzędzia deweloperskie w Chrome używane do zastępowania niestandardowych danych orientacji na telefonie wirtualnym
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 Generic Sensor API jest potężną funkcją, dlatego przeglądarka zezwala na jego użycie tylko w bezpiecznych kontekstach. W praktyce oznacza to, że aby korzystać z interfejsu Generic Sensor API, musisz uzyskać 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. Aby poznać sprawdzone metody i wytyczne, zapoznaj się z kolekcją Bezpieczeństwo i bezpieczeństwo.

Integracja z zasadami dotyczącymi uprawnień

Integracja z zasadami dotyczącymi uprawnień w interfejsie Generic 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 tworzyć w niej obiekty Accelerometer lub LinearAccelerationSensor.

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

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

Wyniki pomiarów 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 nacisk na element frame w innej domenie. Zapobiega to wywnioskowaniu danych wejściowych użytkownika przez nadrzędny element.

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 Medley i Kayce Basques.