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

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

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

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

Что такое Generic Sensor API?

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

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

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

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

  • Generic Sensor API — это платформа для работы с датчиками, которую легко расширять новыми классами датчиков, при этом каждый из этих классов сохраняет свой универсальный интерфейс. Клиентский код, написанный для одного типа датчика, можно повторно использовать для другого с минимальными изменениями!
  • Вы можете настроить датчик. Например, вы можете установить частоту дискретизации, подходящую для потребностей вашего приложения.
  • Вы можете определить, доступен ли датчик на платформе.
  • Показания датчиков имеют высокоточные временные метки, что обеспечивает лучшую синхронизацию с другими процессами в вашем приложении.
  • Модели данных датчиков и системы координат четко определены, что позволяет производителям браузеров внедрять совместимые решения.
  • Интерфейсы на основе универсальных датчиков не привязаны к DOM (то есть они не являются ни navigator , ни window объектами), что открывает новые возможности для использования API в сервисных работниках или его реализации в безголовых средах выполнения JavaScript, например, на встроенных устройствах.
  • Вопросы безопасности и конфиденциальности являются приоритетными для универсального API датчиков и обеспечивают гораздо более высокий уровень защиты по сравнению со старыми API датчиков. Предусмотрена интеграция с API разрешений.
  • Автоматическая синхронизация с экранными координатами доступна для 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 м/ с² , а когда устройство лежит плашмя на столе, ускорение в направлении вверх (ось Z) будет равно силе тяжести Земли, т.е. g ≈ +9,8 м/ с², поскольку измеряется сила, с которой стол толкает устройство вверх. Если толкнуть устройство вправо, ускорение по оси X будет положительным, или отрицательным, если устройство ускоряется справа налево.

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

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

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

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

Гироскоп

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

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

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

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

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

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

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

ВАВИЛОН

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

Универсальный 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' });

Давайте программировать!

Универсальный API для датчиков очень прост и удобен в использовании! Интерфейс Sensor содержит методы start() и stop() для управления состоянием датчика, а также несколько обработчиков событий для получения уведомлений об активации датчика, ошибках и новых доступных показаниях. Конкретные классы датчиков обычно добавляют свои специфические атрибуты показаний к базовому классу.

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

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

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

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

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

Отметка прокрутки

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

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.

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

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

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

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

Только HTTPS

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

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

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

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

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

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

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

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

Что дальше?

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

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

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

Технические характеристики датчиков достигли уровня зрелости «Кандидат на рекомендацию» , поэтому мы будем очень благодарны за отзывы веб-разработчиков и разработчиков браузеров. Сообщите нам, какие функции было бы здорово добавить или что вы хотели бы изменить в текущем API.

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

Ресурсы

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

Данная статья была рецензирована Джо Медли и Кейси Баскес .