Sensores para la Web

Usa la API de Generic Sensor para acceder a los sensores integrados en el dispositivo, como acelerómetros, giroscopios y magnetómetros.

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

Actualmente, los datos de sensores se utilizan en muchas aplicaciones específicas de la plataforma para habilitar casos de uso, como juegos inmersivos, seguimiento de la actividad física y realidad aumentada o virtual. ¿No sería genial cerrar la brecha entre las aplicaciones web y las específicas de cada plataforma? Presentamos la API de Generic Sensor para la Web.

¿Qué es la API de Generic Sensor?

La API de Generic Sensor es un conjunto de interfaces que exponen dispositivos de sensores a la plataforma web. La API consta de la interfaz base Sensor y un conjunto de clases de sensores concretas compiladas sobre ella. Tener una interfaz base simplifica el proceso de implementación y especificación de las clases de sensores concretas. Por ejemplo, consulta la clase Gyroscope. Es muy pequeño. La interfaz base especifica la funcionalidad principal, y Gyroscope solo la extiende con tres atributos que representan la velocidad angular.

Algunas clases de sensores se conectan a sensores de hardware reales, como, por ejemplo, las clases de acelerómetro o giroscopio. Estos se conocen como sensores de bajo nivel. Otros sensores, denominados sensores de fusión, combinan datos de varios sensores de nivel inferior para exponer información que, de otro modo, un script debería calcular. Por ejemplo, el sensor AbsoluteOrientation proporciona una matriz de rotación de cuatro por cuatro lista para usar basada en los datos obtenidos del acelerómetro, el giroscopio y el magnetómetro.

Es posible que pienses que la plataforma web ya proporciona datos de sensores, y tienes toda la razón. Por ejemplo, los eventos DeviceMotion y DeviceOrientation exponen datos del sensor de movimiento. Entonces, ¿por qué necesitamos una API nueva?

En comparación con las interfaces existentes, la API de Generic Sensor ofrece una gran cantidad de ventajas:

  • La API de Generic Sensor es un framework de sensores que se puede extender fácilmente con nuevas clases de sensores, y cada una de estas clases conservará la interfaz genérica. El código del cliente escrito para un tipo de sensor se puede reutilizar para otro con muy pocas modificaciones.
  • Puedes configurar el sensor. Por ejemplo, puedes establecer la frecuencia de muestreo adecuada para las necesidades de tu aplicación.
  • Puedes detectar si un sensor está disponible en la plataforma.
  • Las lecturas de los sensores tienen marcas de tiempo de alta precisión, lo que permite una mejor sincronización con otras actividades de tu aplicación.
  • Los modelos de datos de sensores y los sistemas de coordenadas están claramente definidos, lo que permite a los proveedores de navegadores implementar soluciones interoperables.
  • Las interfaces basadas en Generic Sensor no están vinculadas al DOM (es decir, no son objetos navigator ni window), lo que abre oportunidades futuras para usar la API dentro de los service workers o implementarla en tiempos de ejecución de JavaScript sin encabezado, como dispositivos integrados.
  • Los aspectos de seguridad y privacidad son la principal prioridad de la API de Generic Sensor y proporcionan mucha mejor seguridad en comparación con las APIs de sensores anteriores. Hay integración con la API de Permissions.
  • La sincronización automática con las coordenadas de la pantalla está disponible para Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor y Magnetometer.

APIs de sensores genéricos disponibles

En el momento de escribir este artículo, hay varios sensores con los que puedes experimentar.

Sensores de movimiento:

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

Sensores ambientales:

  • AmbientLightSensor (Detrás de la marca #enable-generic-sensor-extra-classes en Chromium)
  • Magnetometer (Detrás de la marca #enable-generic-sensor-extra-classes en Chromium)

Detección de características

La detección de funciones de las APIs de hardware es compleja, ya que debes detectar si el navegador admite la interfaz en cuestión y si el dispositivo tiene el sensor correspondiente. Comprobar si el navegador admite una interfaz es sencillo. (Reemplaza Accelerometer por cualquiera de las otras interfaces mencionadas anteriormente).

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

Para obtener un resultado de detección de características realmente significativo, también debes intentar conectarte al sensor. En este ejemplo, se ilustra cómo hacerlo.

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

Polyfill

Para los navegadores que no admiten la API de Generic Sensor, hay disponible un polyfill. El polyfill te permite cargar solo las implementaciones de los sensores pertinentes.

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

¿Qué son todos estos sensores? ¿Cómo puedo usarlos?

Sensores es un área que podría necesitar una breve introducción. Si ya conoces los sensores, puedes ir directamente a la sección de programación práctica. De lo contrario, veamos cada sensor compatible en detalle.

Acelerómetro y sensor de aceleración lineal

Mediciones del sensor del acelerómetro

El sensor Accelerometer mide la aceleración de un dispositivo que aloja el sensor en tres ejes (X, Y y Z). Este sensor es un sensor inercial, lo que significa que, cuando el dispositivo está en caída libre lineal, la aceleración total medida sería de 0 m/s2 y, cuando un dispositivo está apoyado sobre una mesa, la aceleración en dirección ascendente (eje Z) será igual a la gravedad de la Tierra, es decir, g ≈ +9.8 m/s2, ya que mide la fuerza de la mesa que empuja el dispositivo hacia arriba. Si empujas el dispositivo hacia la derecha, la aceleración en el eje X sería positiva, o negativa si el dispositivo se acelera de derecha a izquierda.

Los acelerómetros se pueden usar para contar pasos, detectar movimientos o determinar la orientación del dispositivo. A menudo, las mediciones del acelerómetro se combinan con datos de otras fuentes para crear sensores de fusión, como los sensores de orientación.

El objeto LinearAccelerationSensor mide la aceleración que se aplica al dispositivo que aloja el sensor, sin incluir la contribución de la gravedad. Cuando un dispositivo está en reposo, por ejemplo, apoyado sobre una mesa, el sensor mediría una aceleración de ≈ 0 m/s2 en los tres ejes.

Sensor de gravedad

Los usuarios ya pueden obtener manualmente lecturas cercanas a las de un sensor de gravedad inspeccionando manualmente las lecturas de Accelerometer y LinearAccelerometer, pero esto puede ser engorroso y depender de la precisión de los valores proporcionados por esos sensores. Las plataformas como Android pueden proporcionar lecturas de gravedad como parte del sistema operativo, lo que debería ser más económico en términos de procesamiento, proporcionar valores más precisos según el hardware del usuario y ser más fácil de usar en términos de ergonomía de la API. El objeto GravitySensor devuelve el efecto de la aceleración a lo largo de los ejes X, Y y Z del dispositivo debido a la gravedad.

Giroscopio

Mediciones del sensor de giroscopio

El sensor Gyroscope mide la velocidad angular en radianes por segundo alrededor de los ejes X, Y y Z locales del dispositivo. La mayoría de los dispositivos para el consumidor tienen giroscopios mecánicos (MEMS), que son sensores inerciales que miden la velocidad de rotación en función de la fuerza inercial de Coriolis. Los giroscopios MEMS son propensos a la deriva causada por la sensibilidad gravitacional del sensor, que deforma el sistema mecánico interno del sensor. Los giroscopios oscilan a frecuencias relativamente altas, p.ej., decenas de kHz y, por lo tanto, podría consumir más energía en comparación con otros sensores.

Sensores de orientación

Mediciones del sensor de orientación absoluta

El sensor AbsoluteOrientationSensor es un sensor de fusión que mide la rotación de un dispositivo en relación con el sistema de coordenadas de la Tierra, mientras que el sensor RelativeOrientationSensor proporciona datos que representan la rotación de un dispositivo que aloja sensores de movimiento en relación con un sistema de coordenadas de referencia estacionario.

Todos los frameworks modernos de JavaScript en 3D admiten cuaterniones y matrices de rotación para representar la rotación. Sin embargo, si usas WebGL directamente, OrientationSensor incluye convenientemente una propiedad quaternion y un método populateMatrix(). Estos son algunos fragmentos:

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

Los sensores de orientación habilitan varios casos de uso, como juegos inmersivos y realidad aumentada y virtual.

Para obtener más información sobre los sensores de movimiento, los casos de uso avanzados y los requisitos, consulta el documento explicativo sobre los sensores de movimiento.

Sincronización con las coordenadas de la pantalla

De forma predeterminada, las lecturas de los sensores espaciales se resuelven en un sistema de coordenadas local que está vinculado al dispositivo y no tiene en cuenta la orientación de la pantalla.

Sistema de coordenadas del dispositivo
Sistema de coordenadas del dispositivo

Sin embargo, muchos casos de uso, como los juegos o la realidad aumentada y virtual, requieren que las lecturas de los sensores se resuelvan en un sistema de coordenadas que, en cambio, esté vinculado a la orientación de la pantalla.

Sistema de coordenadas de la pantalla
Sistema de coordenadas de la pantalla

Anteriormente, la reasignación de las lecturas del sensor a las coordenadas de la pantalla debía implementarse en JavaScript. Este enfoque es ineficiente y también aumenta significativamente la complejidad del código de la aplicación web. La aplicación web debe observar los cambios de orientación de la pantalla y realizar transformaciones de coordenadas para las lecturas del sensor, lo que no es trivial para los ángulos de Euler o los cuaterniones.

La API de Generic Sensor proporciona una solución mucho más simple y confiable. El sistema de coordenadas local se puede configurar para todas las clases de sensores espaciales definidas: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor y Magnetometer. Si se pasa la opción referenceFrame al constructor del objeto del sensor, el usuario define si las lecturas devueltas se resolverán en coordenadas de dispositivo o de pantalla.

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

¡Comencemos a programar!

La API de Generic Sensor es muy simple y fácil de usar. La interfaz Sensor tiene métodos start() y stop() para controlar el estado del sensor y varios controladores de eventos para recibir notificaciones sobre la activación del sensor, los errores y las lecturas disponibles recientemente. Por lo general, las clases de sensores concretos agregan sus atributos de lectura específicos a la clase base.

Entorno de desarrollo

Durante el desarrollo, podrás usar los sensores a través de localhost. Si desarrollas para dispositivos móviles, configura el redireccionamiento de puertos para tu servidor local y estarás listo para comenzar.

Cuando tu código esté listo, impleméntalo en un servidor que admita HTTPS. Las páginas de GitHub se publican a través de HTTPS, lo que las convierte en un excelente lugar para compartir tus demostraciones.

Rotación del modelo 3D

En este ejemplo simple, usamos los datos de un sensor de orientación absoluta para modificar el cuaternión de rotación de un modelo 3D. model es una instancia de la clase Object3D de three.js que tiene una propiedad quaternion. En el siguiente fragmento de código de la demostración de teléfono con orientación, se ilustra cómo se puede usar el sensor de orientación absoluta para rotar un modelo 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();
}

La orientación del dispositivo se reflejará en la rotación model en 3D dentro de la escena de WebGL.

Los sensores actualizan la orientación del modelo 3D
El sensor actualiza la orientación de un modelo 3D

Punzómetro

El siguiente fragmento de código se extrajo de la demostración de Punch Meter y muestra cómo se puede usar el sensor de aceleración lineal para calcular la velocidad máxima de un dispositivo, suponiendo que inicialmente está quieto.

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

La velocidad actual se calcula como una aproximación de la integral de la función de aceleración.

Aplicación web de demostración para medir la velocidad de los golpes
Medición de la velocidad de un golpe

Depuración y anulación de sensores con las Herramientas para desarrolladores de Chrome

En algunos casos, no necesitas un dispositivo físico para experimentar con la API de Generic Sensor. Las Herramientas para desarrolladores de Chrome ofrecen una excelente compatibilidad para simular la orientación del dispositivo.

Las Herramientas para desarrolladores de Chrome se usan para anular los datos de orientación personalizados de un teléfono virtual
Cómo simular la orientación del dispositivo con las Herramientas para desarrolladores de Chrome

Privacidad y seguridad

Las lecturas de los sensores son datos sensibles que pueden ser objeto de varios ataques de páginas web maliciosas. Las implementaciones de las APIs de Generic Sensor aplican algunas limitaciones para mitigar los posibles riesgos de seguridad y privacidad. Los desarrolladores que deseen usar la API deben tener en cuenta estas limitaciones, por lo que las enumeraremos brevemente.

Solo HTTPS

Debido a que la API de Generic Sensor es una función potente, el navegador solo la permite en contextos seguros. En la práctica, esto significa que, para usar la API de Generic Sensor, deberás acceder a tu página a través de HTTPS. Durante el desarrollo, puedes hacerlo a través de http://localhost, pero para la producción, deberás tener HTTPS en tu servidor. Consulta la colección Seguridad para conocer las prácticas recomendadas y los lineamientos.

Integración de la Política de permisos

La integración de la Política de Permisos en la API de Generic Sensor controla el acceso a los datos de los sensores para un fotograma.

De forma predeterminada, los objetos Sensor solo se pueden crear dentro de un marco principal o submarcos del mismo origen, lo que evita que los iframes de origen cruzado lean datos de sensores sin autorización. Este comportamiento predeterminado se puede modificar si se habilitan o inhabilitan de forma explícita las funciones controladas por políticas correspondientes.

El siguiente fragmento ilustra cómo otorgar acceso a los datos del acelerómetro a un iframe de origen cruzado, lo que significa que ahora se pueden crear objetos Accelerometer o LinearAccelerationSensor allí.

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

Se puede suspender la entrega de lecturas de sensores

Las lecturas de los sensores solo son accesibles para una página web visible, es decir, cuando el usuario interactúa con ella. Además, no se proporcionarían datos del sensor al marco superior si el enfoque del usuario cambia a un submarco de origen cruzado. Esto evita que el iframe superior infiera la entrada del usuario.

Próximos pasos

Hay un conjunto de clases de sensores ya especificadas que se implementarán en el futuro cercano, como Sensor de luz ambiental o Sensor de proximidad. Sin embargo, gracias a la gran extensibilidad del marco de trabajo de Generic Sensor, podemos anticipar la aparición de aún más clases nuevas que representen varios tipos de sensores.

Otra área importante para el trabajo futuro es mejorar la API de Generic Sensor en sí. Actualmente, la especificación de Generic Sensor es una recomendación candidata, lo que significa que aún hay tiempo para realizar correcciones y agregar nuevas funciones que los desarrolladores necesitan.

¡Puedes ayudar!

Las especificaciones del sensor alcanzaron el nivel de madurez de Recomendación candidata, por lo que se agradecen mucho los comentarios de los desarrolladores web y de navegadores. Cuéntanos qué funciones serían buenas para agregar o si hay algo que te gustaría modificar en la API actual.

No dudes en presentar problemas de especificación y errores para la implementación de Chrome.

Recursos

Agradecimientos

Joe Medley y Kayce Basques revisaron este artículo.