Cảm biến cho web

Sử dụng Generic Sensor API để truy cập vào các cảm biến trên thiết bị như gia tốc kế, con quay hồi chuyển và từ kế.

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

Ngày nay, dữ liệu cảm biến được dùng trong nhiều ứng dụng dành riêng cho nền tảng để hỗ trợ các trường hợp sử dụng như chơi trò chơi nhập vai, theo dõi hoạt động thể dục và thực tế tăng cường hoặc thực tế ảo. Thật tuyệt vời nếu có thể thu hẹp khoảng cách giữa các ứng dụng dành riêng cho nền tảng và ứng dụng web. Nhập Generic Sensor API cho web!

Generic Sensor API là gì?

Generic Sensor API là một tập hợp các giao diện hiển thị các thiết bị cảm biến cho nền tảng web. API này bao gồm giao diện Sensor cơ sở và một tập hợp các lớp cảm biến cụ thể được xây dựng trên cùng. Việc có một giao diện cơ sở giúp đơn giản hoá quy trình triển khai và chỉ định cho các lớp cảm biến cụ thể. Ví dụ: hãy xem lớp Gyroscope. Nó siêu nhỏ! Chức năng cốt lõi được chỉ định bằng giao diện cơ sở và Gyroscope chỉ mở rộng giao diện này bằng 3 thuộc tính biểu thị vận tốc góc.

Một số lớp cảm biến giao tiếp với các cảm biến phần cứng thực tế, chẳng hạn như các lớp gia tốc kế hoặc con quay hồi chuyển. Đây được gọi là cảm biến cấp thấp. Các cảm biến khác (còn gọi là cảm biến kết hợp) hợp nhất dữ liệu từ một số cảm biến cấp thấp để hiển thị thông tin mà một tập lệnh sẽ cần tính toán. Ví dụ: cảm biến AbsoluteOrientation cung cấp ma trận xoay 4x4 sẵn sàng sử dụng dựa trên dữ liệu thu được từ gia tốc kế, con quay hồi chuyển và từ kế.

Bạn có thể nghĩ rằng nền tảng web đã cung cấp dữ liệu cảm biến và bạn hoàn toàn đúng! Ví dụ: các sự kiện DeviceMotionDeviceOrientation cho thấy dữ liệu cảm biến chuyển động. Vậy tại sao chúng ta cần một API mới?

So với các giao diện hiện có, Generic Sensor API mang lại rất nhiều lợi ích:

  • Generic Sensor API là một khung cảm biến có thể dễ dàng mở rộng bằng các lớp cảm biến mới và mỗi lớp này sẽ giữ lại giao diện chung. Bạn có thể dùng lại mã ứng dụng được viết cho một loại cảm biến cho một loại cảm biến khác mà chỉ cần sửa đổi rất ít!
  • Bạn có thể định cấu hình cảm biến. Ví dụ: bạn có thể đặt tần suất lấy mẫu phù hợp với nhu cầu của ứng dụng.
  • Bạn có thể phát hiện xem có cảm biến nào trên nền tảng hay không.
  • Các chỉ số cảm biến có dấu thời gian với độ chính xác cao, giúp đồng bộ hoá tốt hơn với các hoạt động khác trong ứng dụng của bạn.
  • Các mô hình dữ liệu cảm biến và hệ toạ độ được xác định rõ ràng, cho phép các nhà cung cấp trình duyệt triển khai các giải pháp có thể tương tác.
  • Các giao diện dựa trên Cảm biến chung không bị ràng buộc với DOM (nghĩa là chúng không phải là đối tượng navigator cũng không phải là đối tượng window), điều này mở ra những cơ hội trong tương lai để sử dụng API trong các worker dịch vụ hoặc triển khai API trong các thời gian chạy JavaScript không có giao diện người dùng, chẳng hạn như các thiết bị nhúng.
  • Các khía cạnh về bảo mật và quyền riêng tư là ưu tiên hàng đầu đối với Generic Sensor API và mang lại khả năng bảo mật tốt hơn nhiều so với các API cảm biến cũ. Có chế độ tích hợp với Permissions API.
  • Tính năng đồng bộ hoá tự động với toạ độ màn hình có sẵn cho Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensorMagnetometer.

Các API cảm biến chung hiện có

Tại thời điểm viết bài này, có một số cảm biến mà bạn có thể thử nghiệm.

Cảm biến chuyển động:

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

Cảm biến môi trường:

  • AmbientLightSensor (Đằng sau cờ #enable-generic-sensor-extra-classes trong Chromium.)
  • Magnetometer (Đằng sau cờ #enable-generic-sensor-extra-classes trong Chromium.)

Phát hiện đối tượng

Việc phát hiện tính năng của các API phần cứng khá phức tạp, vì bạn cần phát hiện cả việc trình duyệt có hỗ trợ giao diện được đề cập hay không, thiết bị có cảm biến tương ứng hay không. Việc kiểm tra xem trình duyệt có hỗ trợ một giao diện hay không rất đơn giản. (Thay thế Accelerometer bằng bất kỳ giao diện nào khác được đề cập ở trên.)

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

Để có kết quả phát hiện tính năng thực sự có ý nghĩa, bạn cũng cần thử kết nối với cảm biến. Ví dụ này minh hoạ cách thực hiện việc đó.

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

Đối với những trình duyệt không hỗ trợ Generic Sensor API, bạn có thể dùng một polyfill. Polyfill cho phép bạn chỉ tải các phương thức triển khai cảm biến có liên quan.

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

Tất cả những cảm biến này là gì? Làm cách nào để sử dụng các chỉ số này?

Cảm biến là một lĩnh vực có thể cần được giới thiệu ngắn gọn. Nếu đã quen thuộc với các cảm biến, bạn có thể chuyển ngay đến phần thực hành lập trình. Nếu không, hãy xem xét chi tiết từng cảm biến được hỗ trợ.

Gia tốc kế và cảm biến gia tốc tuyến tính

Các phép đo bằng cảm biến gia tốc kế

Cảm biến Accelerometer đo gia tốc của một thiết bị lưu trữ cảm biến trên 3 trục (X, Y và Z). Cảm biến này là một cảm biến quán tính, nghĩa là khi thiết bị rơi tự do theo đường thẳng, tổng gia tốc đo được sẽ là 0 m/s2 và khi thiết bị nằm phẳng trên bàn, gia tốc theo hướng lên trên (trục Z) sẽ bằng trọng lực của Trái Đất, tức là g ≈ +9, 8 m/s2 vì cảm biến đang đo lực của bàn đẩy thiết bị lên trên. Nếu bạn đẩy thiết bị sang phải, gia tốc trên trục X sẽ là dương hoặc âm nếu thiết bị được tăng tốc từ phải sang trái.

Gia tốc kế có thể được dùng cho những việc như: đếm bước, cảm biến chuyển động hoặc định hướng thiết bị đơn giản. Thông thường, các phép đo gia tốc kế được kết hợp với dữ liệu từ các nguồn khác để tạo ra các cảm biến kết hợp, chẳng hạn như cảm biến hướng.

LinearAccelerationSensor đo gia tốc được áp dụng cho thiết bị lưu trữ cảm biến, không bao gồm sự đóng góp của trọng lực. Khi thiết bị ở trạng thái nghỉ, chẳng hạn như nằm phẳng trên bàn, cảm biến sẽ đo gia tốc ≈ 0 m/s2 trên 3 trục.

Cảm biến trọng lực

Người dùng đã có thể tự lấy các chỉ số gần với chỉ số của cảm biến trọng lực bằng cách tự kiểm tra các chỉ số AccelerometerLinearAccelerometer, nhưng việc này có thể gây phiền toái và phụ thuộc vào độ chính xác của các giá trị do những cảm biến đó cung cấp. Các nền tảng như Android có thể cung cấp các chỉ số về trọng lực trong hệ điều hành. Điều này sẽ giúp giảm chi phí tính toán, cung cấp các giá trị chính xác hơn tuỳ thuộc vào phần cứng của người dùng và dễ sử dụng hơn về mặt công thái học API. GravitySensor trả về hiệu ứng gia tốc dọc theo trục X, Y và Z của thiết bị do trọng lực.

Con quay hồi chuyển

Đo lường bằng cảm biến con quay hồi chuyển

Cảm biến Gyroscope đo vận tốc góc theo radian trên giây xung quanh trục X, Y và Z cục bộ của thiết bị. Hầu hết các thiết bị tiêu dùng đều có con quay hồi chuyển cơ học (MEMS), đây là các cảm biến quán tính đo tốc độ quay dựa trên lực quán tính Coriolis. Con quay hồi chuyển MEMS dễ bị trôi do độ nhạy trọng lực của cảm biến làm biến dạng hệ thống cơ học bên trong của cảm biến. Con quay hồi chuyển dao động ở tần số tương đối cao, ví dụ: 10 giây của kHz và do đó, có thể tiêu thụ nhiều năng lượng hơn so với các cảm biến khác.

Cảm biến hướng

Các phép đo bằng cảm biến hướng tuyệt đối

AbsoluteOrientationSensor là một cảm biến kết hợp đo lường độ xoay của thiết bị so với hệ toạ độ của Trái Đất, trong khi RelativeOrientationSensor cung cấp dữ liệu biểu thị độ xoay của thiết bị lưu trữ các cảm biến chuyển động so với hệ toạ độ tham chiếu cố định.

Tất cả các khung JavaScript 3D hiện đại đều hỗ trợ quaternionma trận xoay để biểu thị phép xoay; tuy nhiên, nếu bạn sử dụng trực tiếp WebGL, thì OrientationSensor sẽ có cả thuộc tính quaternionphương thức populateMatrix() một cách thuận tiện. Dưới đây là một vài đoạn trích:

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

Cảm biến hướng hỗ trợ nhiều trường hợp sử dụng, chẳng hạn như chơi game nhập vai, thực tế tăng cường và thực tế ảo.

Để biết thêm thông tin về cảm biến chuyển động, các trường hợp sử dụng nâng cao và yêu cầu, hãy xem tài liệu giải thích về cảm biến chuyển động.

Đồng bộ hoá với toạ độ màn hình

Theo mặc định, các chỉ số của cảm biến không gian được phân giải trong một hệ toạ độ cục bộ được liên kết với thiết bị và không tính đến hướng màn hình.

Hệ toạ độ thiết bị
Hệ toạ độ thiết bị

Tuy nhiên, nhiều trường hợp sử dụng như trò chơi hoặc thực tế tăng cường và thực tế ảo yêu cầu các chỉ số cảm biến được phân giải trong một hệ toạ độ thay vì được liên kết với hướng màn hình.

Hệ toạ độ màn hình
Hệ toạ độ màn hình

Trước đây, bạn phải triển khai việc ánh xạ lại các chỉ số của cảm biến thành toạ độ trên màn hình bằng JavaScript. Phương pháp này không hiệu quả và cũng làm tăng đáng kể độ phức tạp của mã ứng dụng web; ứng dụng web phải theo dõi các thay đổi về hướng màn hình và thực hiện các phép biến đổi toạ độ cho các chỉ số cảm biến. Đây không phải là việc dễ dàng đối với các góc Euler hoặc quaternion.

Generic Sensor API cung cấp một giải pháp đơn giản và đáng tin cậy hơn nhiều! Bạn có thể định cấu hình hệ toạ độ cục bộ cho tất cả các lớp cảm biến không gian đã xác định: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensorMagnetometer. Bằng cách truyền lựa chọn referenceFrame vào hàm khởi tạo đối tượng cảm biến, người dùng sẽ xác định xem các chỉ số được trả về sẽ được phân giải trong toạ độ thiết bị hay màn hình.

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

Hãy lập trình!

Generic Sensor API rất đơn giản và dễ sử dụng! Giao diện Cảm biến có các phương thức start()stop() để kiểm soát trạng thái cảm biến và một số trình xử lý sự kiện để nhận thông báo về việc kích hoạt cảm biến, lỗi và các chỉ số mới có sẵn. Các lớp cảm biến cụ thể thường thêm các thuộc tính đọc cụ thể vào lớp cơ sở.

Môi trường phát triển

Trong quá trình phát triển, bạn sẽ có thể sử dụng các cảm biến thông qua localhost. Nếu đang phát triển cho thiết bị di động, hãy thiết lập chuyển tiếp cổng cho máy chủ cục bộ và bạn đã sẵn sàng!

Khi mã đã sẵn sàng, hãy triển khai mã đó trên một máy chủ hỗ trợ HTTPS. GitHub Pages được phân phát qua HTTPS, nên đây là nơi lý tưởng để chia sẻ các bản minh hoạ của bạn.

Xoay mô hình 3D

Trong ví dụ đơn giản này, chúng ta sử dụng dữ liệu từ một cảm biến hướng tuyệt đối để sửa đổi quaternion xoay của một mô hình 3D. model là một thực thể lớp Object3D three.js có thuộc tính quaternion. Đoạn mã sau đây trong bản minh hoạ điện thoại theo hướng minh hoạ cách sử dụng cảm biến hướng tuyệt đối để xoay mô hình 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();
}

Hướng của thiết bị sẽ được phản ánh trong chế độ xoay 3D model trong cảnh WebGL.

Cảm biến cập nhật hướng của mô hình 3D
Cảm biến cập nhật hướng của mô hình 3D

Punchmeter

Đoạn mã sau đây được trích xuất từ bản minh hoạ punchmeter, minh hoạ cách có thể dùng cảm biến gia tốc tuyến tính để tính vận tốc tối đa của một thiết bị, giả sử ban đầu thiết bị đang nằm yên.

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

Vận tốc hiện tại được tính xấp xỉ bằng tích phân của hàm gia tốc.

Ứng dụng web minh hoạ để đo tốc độ đấm
Đo tốc độ đấm

Gỡ lỗi và ghi đè cảm biến bằng Công cụ của Chrome cho nhà phát triển

Trong một số trường hợp, bạn không cần thiết bị thực để sử dụng Generic Sensor API. Chrome DevTools hỗ trợ rất tốt việc mô phỏng hướng thiết bị.

Công cụ của Chrome cho nhà phát triển được dùng để ghi đè dữ liệu hướng tuỳ chỉnh của một điện thoại ảo
Mô phỏng hướng thiết bị bằng Công cụ dành cho nhà phát triển của Chrome

Quyền riêng tư và bảo mật

Dữ liệu đọc từ cảm biến là dữ liệu nhạy cảm và có thể phải chịu nhiều cuộc tấn công từ các trang web độc hại. Việc triển khai Generic Sensor API sẽ áp dụng một số hạn chế để giảm thiểu các rủi ro có thể xảy ra về bảo mật và quyền riêng tư. Nhà phát triển có ý định sử dụng API này phải lưu ý những hạn chế này. Vì vậy, hãy liệt kê ngắn gọn các hạn chế đó.

Chỉ HTTPS

Vì Generic Sensor API là một tính năng mạnh mẽ, nên trình duyệt chỉ cho phép API này hoạt động trong các bối cảnh an toàn. Trên thực tế, điều này có nghĩa là để sử dụng Generic Sensor API, bạn cần truy cập vào trang của mình thông qua HTTPS. Trong quá trình phát triển, bạn có thể thực hiện việc này thông qua http://localhost nhưng đối với bản phát hành công khai, bạn cần phải có HTTPS trên máy chủ của mình. Hãy xem bộ sưu tập An toàn và bảo mật để biết các phương pháp hay nhất và nguyên tắc.

Tích hợp Chính sách về quyền

Chế độ tích hợp Chính sách về quyền trong Generic Sensor API kiểm soát quyền truy cập vào dữ liệu cảm biến cho một khung hình.

Theo mặc định, các đối tượng Sensor chỉ có thể được tạo trong một khung chính hoặc các khung phụ có cùng nguồn gốc, do đó ngăn các iframe trên nhiều nguồn gốc đọc dữ liệu cảm biến mà không được phép. Bạn có thể sửa đổi hành vi mặc định này bằng cách bật hoặc tắt rõ ràng các tính năng do chính sách kiểm soát tương ứng.

Đoạn mã dưới đây minh hoạ việc cấp quyền truy cập vào dữ liệu gia tốc kế cho một iframe trên nhiều nguồn gốc, tức là giờ đây, các đối tượng Accelerometer hoặc LinearAccelerationSensor có thể được tạo ở đó.

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

Có thể tạm ngưng việc gửi dữ liệu đọc được từ cảm biến

Chỉ có trang web hiển thị mới truy cập được vào các chỉ số của cảm biến, tức là khi người dùng thực sự tương tác với trang web đó. Ngoài ra, dữ liệu cảm biến sẽ không được cung cấp cho khung chính nếu tiêu điểm của người dùng thay đổi thành một khung phụ trên nhiều nguồn. Điều này ngăn khung mẹ suy luận thông tin đầu vào của người dùng.

Tiếp theo là gì?

Có một nhóm các lớp cảm biến đã được chỉ định sẽ được triển khai trong tương lai gần, chẳng hạn như Cảm biến ánh sáng xung quanh hoặc Cảm biến tiệm cận; tuy nhiên, nhờ khả năng mở rộng tuyệt vời của khung Cảm biến chung, chúng ta có thể dự đoán sự xuất hiện của nhiều lớp mới hơn nữa, đại diện cho nhiều loại cảm biến.

Một lĩnh vực quan trọng khác cho công việc trong tương lai là cải thiện chính Generic Sensor API. Thông số Generic Sensor hiện là Đề xuất đề cử, tức là vẫn còn thời gian để khắc phục và mang đến chức năng mới mà nhà phát triển cần.

Bạn có thể trợ giúp!

Các thông số kỹ thuật của cảm biến đã đạt đến cấp độ thành thục Đề xuất ứng cử viên, do đó, chúng tôi rất trân trọng ý kiến phản hồi của các nhà phát triển web và trình duyệt. Hãy cho chúng tôi biết những tính năng bạn muốn thêm hoặc nếu có điều gì bạn muốn sửa đổi trong API hiện tại.

Vui lòng gửi các vấn đề về quy cách cũng như lỗi cho việc triển khai Chrome.

Tài nguyên

Lời cảm ơn

Bài viết này được Joe MedleyKayce Basques xem xét.