Датчики для Интернета

Используйте API универсального датчика для получения доступа к датчикам на устройстве, таким как акселерометры, гироскопы и магнитометры.

Алекс Шаламов
Alex Shalamov
Михаил Поздняков
Mikhail Pozdnyakov

Сегодня данные датчиков используются во многих платформенно-специфичных приложениях для включения вариантов использования, таких как захватывающие игры, фитнес-трекинг и дополненная или виртуальная реальность. Разве не было бы здорово преодолеть разрыв между платформенно-специфичными и веб-приложениями? Введите API Generic Sensor для веба!

Что такое API универсального датчика?

Generic Sensor API — это набор интерфейсов, которые предоставляют сенсорные устройства веб-платформе. API состоит из базового интерфейса Sensor и набора конкретных классов датчиков, построенных поверх него. Наличие базового интерфейса упрощает процесс реализации и спецификации для конкретных классов датчиков. Например, взгляните на класс Gyroscope . Он очень маленький! Основная функциональность определяется базовым интерфейсом, а Gyroscope просто расширяет ее тремя атрибутами, представляющими угловую скорость.

Некоторые классы датчиков взаимодействуют с реальными аппаратными датчиками, такими как, например, классы акселерометра или гироскопа. Они называются датчиками низкого уровня. Другие датчики, называемые датчиками слияния , объединяют данные с нескольких датчиков низкого уровня, чтобы предоставить информацию, которую скрипту в противном случае пришлось бы вычислять. Например, датчик AbsoluteOrientation предоставляет готовую к использованию матрицу вращения четыре на четыре на основе данных, полученных от акселерометра, гироскопа и магнитометра.

Вы можете подумать, что веб-платформа уже предоставляет данные датчиков, и вы абсолютно правы! Например, события DeviceMotion и DeviceOrientation предоставляют данные датчиков движения. Так зачем же нам новый API?

По сравнению с существующими интерфейсами, Generic Sensor API обеспечивает целый ряд преимуществ:

  • Generic Sensor API — это фреймворк датчиков, который можно легко расширить новыми классами датчиков, и каждый из этих классов сохранит общий интерфейс. Клиентский код, написанный для одного типа датчика, можно повторно использовать для другого с небольшими изменениями!
  • Вы можете настроить датчик. Например, вы можете установить частоту выборки, подходящую для ваших потребностей.
  • Вы можете определить, доступен ли датчик на платформе.
  • Показания датчиков имеют высокоточные временные метки, что обеспечивает лучшую синхронизацию с другими действиями в вашем приложении.
  • Модели данных датчиков и системы координат четко определены, что позволяет поставщикам браузеров реализовывать совместимые решения.
  • Интерфейсы на основе Generic Sensor не привязаны к DOM (то есть они не являются ни объектами navigator , ни объектами window ), и это открывает будущие возможности для использования API в Service Worker или его реализации в безголовых средах выполнения JavaScript, таких как встроенные устройства.
  • Безопасность и конфиденциальность являются главным приоритетом для API Generic Sensor и обеспечивают гораздо лучшую безопасность по сравнению со старыми API датчиков. Интеграция с API Permissions есть.
  • Автоматическая синхронизация с экранными координатами доступна для Accelerometer , Gyroscope , LinearAccelerationSensor , AbsoluteOrientationSensor , RelativeOrientationSensor и Magnetometer .

Доступные общие API датчиков

На момент написания статьи вы можете поэкспериментировать с несколькими датчиками.

Датчики движения:

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

Датчики окружающей среды:

  • AmbientLightSensor (Стоит за флагом #enable-generic-sensor-extra-classes в Chromium.)
  • Magnetometer (за флагом #enable-generic-sensor-extra-classes в Chromium.)

Обнаружение особенностей

Обнаружение функций аппаратных 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 измеряет ускорение устройства, на котором установлен датчик, по трем осям (X, Y и Z). Этот датчик является инерционным, что означает, что когда устройство находится в линейном свободном падении, общее измеренное ускорение будет равно 0 м/с 2 , а когда устройство лежит на столе, ускорение в направлении вверх (ось Z) будет равно силе тяжести Земли, то есть g ≈ +9,8 м/с 2 , поскольку он измеряет силу стола, толкающую устройство вверх. Если вы толкаете устройство вправо, ускорение по оси X будет положительным или отрицательным, если устройство ускоряется справа налево.

Акселерометры могут использоваться для таких вещей, как: подсчет шагов, обнаружение движения или простая ориентация устройства. Довольно часто измерения акселерометра объединяются с данными из других источников для создания датчиков слияния, таких как датчики ориентации.

LinearAccelerationSensor измеряет ускорение, которое прикладывается к устройству, на котором установлен датчик, исключая влияние силы тяжести. Когда устройство находится в состоянии покоя, например, лежит на столе, датчик будет измерять ускорение ≈ 0 м/с 2 по трем осям.

Датчик гравитации

Пользователи уже могут вручную получать показания, близкие к показаниям датчика гравитации, вручную проверяя показания Accelerometer и LinearAccelerometer , но это может быть обременительно и зависеть от точности значений, предоставляемых этими датчиками. Такие платформы, как Android, могут предоставлять показания гравитации как часть операционной системы, что должно быть дешевле с точки зрения вычислений, предоставлять более точные значения в зависимости от оборудования пользователя и быть проще в использовании с точки зрения эргономики API. GravitySensor возвращает эффект ускорения вдоль осей X, Y и Z устройства из-за гравитации.

Гироскоп

Измерения датчика гироскопа

Датчик Gyroscope измеряет угловую скорость в радианах в секунду вокруг локальных осей X, Y и Z устройства. Большинство потребительских устройств имеют механические ( MEMS ) гироскопы, которые являются инерционными датчиками, измеряющими скорость вращения на основе инерционной силы Кориолиса . MEMS-гироскопы склонны к дрейфу, вызванному гравитационной чувствительностью датчика, которая деформирует внутреннюю механическую систему датчика. Гироскопы колеблются на относительно высоких частотах, например, 10 кГц, и поэтому могут потреблять больше энергии по сравнению с другими датчиками.

Датчики ориентации

Измерения датчика абсолютной ориентации

AbsoluteOrientationSensor — это датчик слияния, который измеряет вращение устройства относительно системы координат Земли, в то время как RelativeOrientationSensor предоставляет данные, представляющие вращение устройства, оснащенного датчиками движения, относительно неподвижной опорной системы координат.

Все современные 3D JavaScript-фреймворки поддерживают кватернионы и матрицы вращения для представления вращения; однако, если вы используете WebGL напрямую, OrientationSensor удобно имеет как свойство quaternion , так и метод populateMatrix() . Вот несколько фрагментов:

три.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();

ВАВИЛОН

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. Такой подход неэффективен и также значительно увеличивает сложность кода веб-приложения; веб-приложение должно отслеживать изменения ориентации экрана и выполнять преобразования координат для показаний датчиков, что не является тривиальной задачей для углов Эйлера или кватернионов.

API Generic Sensor обеспечивает гораздо более простое и надежное решение! Локальная система координат настраивается для всех определенных пространственных классов датчиков: 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 очень прост и удобен в использовании! Интерфейс Sensor имеет методы start() и stop() для управления состоянием датчика и несколько обработчиков событий для получения уведомлений об активации датчика, ошибках и новых доступных показаниях. Конкретные классы датчиков обычно добавляют свои особые атрибуты показаний в базовый класс.

Среда разработки

Во время разработки вы сможете использовать датчики через localhost . Если вы разрабатываете для мобильных устройств, настройте переадресацию портов для вашего локального сервера, и вы готовы к работе!

Когда ваш код будет готов, разверните его на сервере, который поддерживает HTTPS. GitHub Pages обслуживаются через HTTPS, что делает его отличным местом для обмена вашими демонстрациями.

Вращение 3D-модели

В этом простом примере мы используем данные с датчика абсолютной ориентации для изменения кватерниона вращения 3D-модели. model представляет собой экземпляр класса three.js Object3D , имеющий свойство quaternion . Следующий фрагмент кода из демонстрации ориентации телефона иллюстрирует, как датчик абсолютной ориентации может использоваться для вращения 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();
}

Ориентация устройства будет отражена во вращении 3D- model в сцене WebGL.

Датчик обновляет ориентацию 3D-модели
Датчик обновляет ориентацию 3D-модели

Пуншметр

Следующий фрагмент кода извлечен из демонстрационной версии Punchmeter и иллюстрирует, как датчик линейного ускорения может использоваться для расчета максимальной скорости устройства, предполагая, что изначально оно лежит неподвижно.

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

В некоторых случаях вам не нужно физическое устройство для работы с API Generic Sensor. Chrome DevTools имеет отличную поддержку для имитации ориентации устройства .

Chrome DevTools используется для переопределения данных пользовательской ориентации виртуального телефона
Имитация ориентации устройства с помощью Chrome DevTools

Конфиденциальность и безопасность

Показания датчиков являются конфиденциальными данными, которые могут подвергаться различным атакам со стороны вредоносных веб-страниц. Реализации API Generic Sensor налагают несколько ограничений для снижения возможных рисков безопасности и конфиденциальности. Эти ограничения должны учитываться разработчиками, которые собираются использовать API, поэтому давайте кратко перечислим их.

Только HTTPS

Поскольку Generic Sensor API — это мощная функция, браузер разрешает ее только в защищенных контекстах. На практике это означает, что для использования Generic Sensor API вам нужно будет получить доступ к своей странице через HTTPS. Во время разработки вы можете сделать это через http://localhost , но для производства вам понадобится HTTPS на вашем сервере. Ознакомьтесь с коллекцией Safe and Secure для получения рекомендаций и рекомендаций.

Интеграция политики разрешений

Интеграция политики разрешений в API универсального датчика контролирует доступ к данным датчиков для кадра.

По умолчанию объекты Sensor могут быть созданы только в пределах основного фрейма или подфреймов с тем же происхождением, что предотвращает несанкционированное чтение данных датчика через кросс-источники iframes. Это поведение по умолчанию можно изменить, явно включив или отключив соответствующие функции, контролируемые политикой .

В приведенном ниже фрагменте показано предоставление доступа к данным акселерометра кросс-источниковому iframe, что означает, что теперь там можно создавать объекты Accelerometer или LinearAccelerationSensor .

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

Передача показаний датчиков может быть приостановлена

Показания датчика доступны только видимой веб-странице, т. е. когда пользователь фактически взаимодействует с ней. Более того, данные датчика не будут предоставлены родительскому фрейму, если фокус пользователя изменится на подфрейм с перекрестным происхождением. Это не позволяет родительскому фрейму выводить пользовательский ввод.

Что дальше?

Существует набор уже определенных классов датчиков, которые будут реализованы в ближайшем будущем, например, датчик внешней освещенности или датчик приближения ; однако благодаря большой расширяемости фреймворка Generic Sensor мы можем ожидать появления еще большего количества новых классов, представляющих различные типы датчиков.

Еще одним важным направлением будущей работы является улучшение самого API Generic Sensor. Спецификация Generic Sensor в настоящее время имеет статус кандидата на рекомендацию, что означает, что еще есть время для внесения исправлений и добавления новых функций, необходимых разработчикам.

Вы можете помочь!

Спецификации датчиков достигли уровня зрелости Candidate Recommendation , поэтому отзывы от разработчиков веб-сайтов и браузеров очень ценятся. Дайте нам знать, какие функции было бы здорово добавить или есть ли что-то, что вы хотели бы изменить в текущем API.

Не стесняйтесь сообщать о проблемах со спецификациями , а также об ошибках в реализации Chrome.

Ресурсы

Благодарности

Эту статью рецензировали Джо Медли и Кейс Баскес .