חיישנים לאינטרנט

כדאי להשתמש ב-Sensor API הכללי כדי לקבל גישה לחיישנים במכשיר, כמו מדי תאוצה, ג'ירוסקופים ומגנטומטר.

Alex Shalamov
Alex Shalamov
Mikhail Pozdnyakov
Mikhail Pozdnyakov

כיום, נתוני חיישנים משמשים באפליקציות רבות שספציפיות לפלטפורמה כדי לאפשר שימוש בתרחישים לדוגמה, כמו גיימינג סוחף, מעקב כושר ומציאות רבודה או מציאות מדומה. נכון שיהיה מגניב לגשר על הפער בין אפליקציות ספציפיות לפלטפורמה לבין אפליקציות אינטרנט? מזינים את General Sensor API, לשימוש באינטרנט.

מהו ה-General Sensor API?

גנרית חיישן API היא קבוצה של ממשקים שחושפים של מכשירי חיישנים לפלטפורמת האינטרנט. ה-API מורכב ממשק Sensor וסטנדים של בטון סיווגי חיישנים שמורכבים על העליון. ממשק בסיסי מפשט את ההטמעה והמפרט את סיווגי החיישנים של הבטון. לדוגמה, קראו את Gyroscope. הוא ממש קטן! הפונקציונליות העיקרית מוגדרת על ידי הממשק הבסיסי, ו-Gyroscope רק מרחיב אותה באמצעות שלוש שמייצגים מהירות זוויתית.

חלק מסוגי החיישנים מתממשקים עם חיישני חומרה, כמו מד התאוצה או סוגים של ג'ירוסקופ. הם נקראים חיישנים ברמה נמוכה. חיישנים אחרים חיישני היתוך, מיזוג נתונים מכמה רמות מהחיישנים שלנו כדי לחשוף מידע שאחרת סקריפט היה צריך לחשב. לדוגמה, חיישן AbsoluteOrientation מספקת מטריצה מוכנה לשימוש של 4 על 4 סיבובים, על סמך הנתונים שהתקבלו מד תאוצה, ג'ירוסקופ ומגנטומטר.

יכול להיות שחשבתם שפלטפורמת האינטרנט כבר מספקת נתוני חיישנים, ואתם צודקים לגמרי. עבור למשל, DeviceMotion DeviceOrientation אירועים מרכזיים חושפים את הנתונים של חיישני התנועה. אז למה אנחנו צריכים API חדש?

בהשוואה לממשקים הקיימים, General Sensor API מספק מספר יתרונות גדולים:

  • גנרית חיישן API היא מסגרת חיישנים שאפשר להרחיב בקלות באמצעות סוגי חיישנים חדשים כל אחד מהמחלקות יישאר בממשק הגנרי. קוד הלקוח נכתב עבור סוג חיישן אחד ניתן לעשות בו שימוש חוזר בסרטון אחר עם מעט מאוד שינויים!
  • אפשר להגדיר את החיישן. לדוגמה, ניתן להגדיר את תדירות הדגימה המתאימה לצרכים של האפליקציה.
  • אפשר לזהות אם יש חיישן זמין בפלטפורמה.
  • קריאות החיישנים כוללות חותמות זמן ברמת דיוק גבוהה, וכך ניתן להסתנכרן טוב יותר עם חיישנים אחרים הפעילויות שלכם באפליקציה.
  • המודלים של נתוני החיישנים ומערכות הקואורדינטות מוגדרים בבירור, כך שספקי הדפדפנים יכולים להטמיע פתרונות בעלי יכולת פעולה הדדית.
  • הממשקים הגנריים המבוססים על חיישנים לא קשורים ל-DOM (כלומר הם לא navigator או אובייקטים מסוג window), וכך ייפתחו הזדמנויות עתידיות לשימוש ב-API בתוך השירות או להטמיע אותו בסביבות זמני ריצה של JavaScript ללא GUI, כמו מכשירים.
  • היבטי האבטחה והפרטיות נמצאים בראש סדר העדיפויות של החיישן הכללי ממשקי API ומספקים אבטחה טובה יותר בהשוואה לממשקי API ישנים יותר של חיישנים. יש שילוב עם ב-Permissions 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;
  }
}

פוליפיל

לדפדפנים שלא תומכים ב-General Sensor API, polyfill זמין. ה-polyfill מאפשר לך לטעון רק את החיישנים הרלוונטיים בפועל.

// 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 m/s2 כמו הוא מודד את הכוח של השולחן שדוחף את המכשיר כלפי מעלה. אם דוחפים את המכשיר מימין, התאוצה בציר ה-X תהיה חיובית או שלילית אם מימין לשמאל.

אפשר להשתמש במדי תאוצה לפעולות כמו ספירת צעדים, לחיישן תנועה או למכשיר פשוט לכיוון מסוים. בדרך כלל, המדידות של מד התאוצה משולבות עם נתונים ממקורות אחרים, ליצור חיישני היתוך, כמו חיישני כיוון.

LinearAccelerationSensor מודדת את התאוצה שמופעלת על המכשיר שמארח את החיישן, לא כולל התרומה של כוח הכבידה. כשהמכשיר במצב מנוחה, למשל כשהוא שוכב על השולחן, החיישן ימדוד ≈ 0 מטר/שנ'2 תאוצה בשלושה צירים.

חיישן כוח הכבידה

המשתמשים כבר יכולים להסיק באופן ידני נתונים שקרובים לערכים של חיישן הכבידה באמצעות מתבצעת בדיקה ידנית של קריאות Accelerometer ו-LinearAccelerometer, אבל זה עלול להיות מסורבל ותלויים בדיוק הערכים של החיישנים האלה. פלטפורמות כמו Android יכולות לספק נתונים של כוח הכבידה כחלק ממערכת ההפעלה, והם אמורים להיות זולים יותר חישובים, לספק ערכים מדויקים יותר בהתאם לחומרה של המשתמש, ולהיות קלים יותר לשימוש במונחים של ארגונומיה של API. הפקודה GravitySensor מחזירה את האפקט. תאוצה לאורך ציר ה-X, ה-Y ו-Z של המכשיר בגלל כוח הכבידה.

ג'ירוסקופ

מדידות של חיישן הג'ירוסקופ

החיישן Gyroscope מודד מהירות זוויתית ברדיאנים לשנייה סביב הציר המקומי של X, Y ו-Z. הכי צרכנים מכשירים בעלי מכשירים מכניים (MEMS) ג'ירוסקופים, שהם חיישנים אינרציים שמודדים את קצב הסיבוב הכוח האירטואלי של קוריוליס. ג'ירוסקופים של MEMS מועדים סחף שנוצר בגלל הרגישות הכבידה של החיישן, שמבטלת במערכת מכנית פנימית. ג'ירוסקופים מתנדנדים בתדרים גבוהים יחסית, למשל 10 שניות של קילו-הרץ, ולכן עשויים לצרוך יותר חשמל בהשוואה לחיישנים אחרים.

חיישני כיוון

מדידות של חיישן כיוון מוחלט

AbsoluteOrientationSensor הוא חיישן היתוך שמודד סיבוב מכשיר ביחס למערכת הקואורדינטות של כדור הארץ, בזמן RelativeOrientationSensor מספק נתונים שמייצגים סיבוב של מכשיר שמארח את חיישני התנועה ביחס במערכת הקואורדינטות של הפניות.

כל המסגרות המודרניות של JavaScript בתלת-ממד תומכות ברבעונים ומטריצות סיבוב כדי לייצג סיבוב; עם זאת, אם משתמשים ישירות ב-WebGL, ב-OrientationSensor יש נוח נכס quaternion וגם populateMatrix() method. הנה כמה קטעי טקסט:

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

חיישני כיוון מאפשרים שימוש בתרחישים שונים לדוגמה, כמו גיימינג סוחף, משחקים משופרים ווירטואליים למציאות.

מידע נוסף על חיישני תנועה, תרחישים מתקדמים לדוגמה ודרישות, זמין ב מסמך הסבר על חיישני תנועה.

סנכרון עם קואורדינטות מסך

כברירת מחדל, נקבעות תוצאות של חיישנים מרחביים במערכת קואורדינטות מקומית שמקושרת למכשיר ולא מועברת לכיוון המסך חשבון.

מערכת הקואורדינטות של המכשיר
מערכת קואורדינטות של מכשירים

עם זאת, במקרים רבים כמו משחקים, או מציאות רבודה ומציאות מדומה, קריאות החיישנים צריכות להיות נקבעים במערכת קואורדינטות הקשורה במקום זאת לכיוון המסך.

מערכת קואורדינטות מסך
מערכת קואורדינטות מסך

בעבר, היה צורך במיפוי מחדש של קריאות חיישנים לקואורדינטות מסך ב-JavaScript. הגישה הזו אינה יעילה והיא גם מגדילה במידה משמעותית את המורכבות של האינטרנט קוד אפליקציה, אפליקציית האינטרנט חייבת לצפות בשינויים בכיוון המסך ולבצע קואורדינטות של קריאות חיישנים, וזה לא דבר טריוויאלי לעשות בשביל זוויות אוילר ורביעינים.

ה-General Sensor API מספק פתרון פשוט ואמין הרבה יותר! מערכת הקואורדינטות המקומית היא ניתנים להגדרה לכל סיווגי החיישנים המרחביים המוגדרים: Accelerometer, Gyroscope, LinearAccelerationSensor, AbsoluteOrientationSensor, RelativeOrientationSensor וגם Magnetometer. על ידי העברת האפשרות referenceFrame ל-constructor של אובייקט החיישן, המשתמש מגדיר אם הקריאות שמוחזרות ייפתרו device או הקואורדינטות של המסך.

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

בואו נתכנת!

ה-General Sensor API הוא פשוט מאוד וקל לשימוש! בממשק החיישן יש start() ו- stop() שיטות לשליטה במצב החיישן גורמים מטפלים באירועים לקבלת התראות על הפעלה של חיישן, שגיאות ואפשרויות חדשות זמינות קריאה. סוגי החיישנים של בטון בדרך כלל מוסיפים את מאפייני הקריאה הספציפיים שלהם לבסיס בכיתה.

סביבת פיתוח

במהלך הפיתוח תהיה לך אפשרות להשתמש בחיישנים דרך localhost. אם אתם מפתחים מכשירים ניידים, הגדרה העברה ליציאה אחרת השרת המקומי שלכם, ואתם מוכנים להתחיל לשחק!

כשהקוד יהיה מוכן, פרסו אותו בשרת שתומך ב-HTTPS. דפי GitHub מוצגים באמצעות HTTPS, וזה מקום נהדר לשתף את ההדגמות שלכם.

סיבוב מודל תלת-ממדי

בדוגמה הפשוטה הזו, אנחנו משתמשים בנתונים מחיישן כיוון מוחלט כדי לשנות את הסיבוב רב-שכבתי של מודל תלת-ממדי. השדה model הוא שלושה.js מופע של מחלקה Object3D עם נכס quaternion. את קטע הקוד הבא טלפון ממוקד שממחישה כיצד ניתן להשתמש בחיישן הכיוון המוחלט כדי לסובב מודל תלת-ממדי.

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

כיוון המכשיר יבוא לידי ביטוי בסיבוב תלת-ממדי של model בסצנת WebGL.

עדכון הכיוון של המודל התלת-ממדי באמצעות חיישן
עדכון הכיוון של מודל תלת-ממדי באמצעות חיישנים

אגרוף

קטע הקוד הבא נשלף מתוך 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

במקרים מסוימים אין צורך במכשיר פיזי כדי לשחק עם ה-General Sensor API. כלי פיתוח ל-Chrome יש תמיכה רבה סימולציה של כיוון המכשיר.

כלי פיתוח ל-Chrome שמשמשים לשינוי נתוני הכיוון המותאם אישית של טלפון וירטואלי
הדמיה של כיוון המכשיר באמצעות כלי הפיתוח ל-Chrome

פרטיות ואבטחה

קריאות מחיישנים הן מידע אישי רגיש שעלול להיות כפוף להתקפות שונות מדפי אינטרנט זדוניים. כשמטמיעים ממשקי API כלליים של חיישנים, המערכת אוכפת מספר מגבלות כדי לצמצם את הסיכון לאבטחה וסיכוני פרטיות. מפתחים שרוצים להשתמש ב-Google Ads צריכים להביא בחשבון את המגבלות האלה אז נפרט אותם בקצרה.

רק HTTPS

מכיוון ש-General Sensor API הוא תכונה עוצמתית, הדפדפן מאפשר אותו רק בהקשרים מאובטחים. לחשבון כלומר, כדי להשתמש בממשק ה-API הכללי של החיישן, תצטרכו לגשת לדף דרך HTTPS. במהלך הפיתוח ניתן לעשות זאת דרך http://localhost, אבל בשלב הייצור תצטרך צריך להיות HTTPS בשרת. לעיון באוסף בטוח ומאובטח לשיטות מומלצות והנחיות.

שילוב של מדיניות ההרשאות

שילוב של מדיניות ההרשאות באופן כללי Sensor API שולט בגישה לנתוני החיישנים של פריים.

כברירת מחדל, אפשר ליצור אובייקטים מסוג Sensor רק במסגרת ראשית או בתת-מסגרות מאותו מקור, וכך למנוע ממסגרות iframe ממקורות שונים לקרוא נתוני חיישנים לא מאושרים. פעולת ברירת המחדל הזו על ידי הפעלה או השבתה מפורשות של המודלים המתאימים תכונות שנמצאות בשליטת המדיניות.

קטע הקוד שבהמשך ממחיש מתן גישה לנתוני מד התאוצה ל-iframe ממקורות שונים, כלומר שעכשיו אפשר ליצור שם אובייקטים מסוג Accelerometer או LinearAccelerationSensor.

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

ניתן להשעות את שליחת הנתונים מהחיישנים

ניתן לגשת לקריאות מהחיישן רק דרך דף אינטרנט גלוי, כלומר, כשהמשתמש בפועל לקיים אינטראקציה איתו. בנוסף, נתוני חיישנים לא יסופקו למסגרת ההורה אם המשתמש המיקוד משתנה בתת-מסגרת ממקורות שונים. ההגדרה הזו מונעת הסקת קלט של משתמשים במסגרת ההורה.

מה השלב הבא?

קיימת קבוצה של סוגי חיישנים שכבר צוינו להטמעה בעתיד הקרוב, כמו חיישן אור סביבה או חיישן קרבה; עם זאת, בזכות ההרחבה הגדולה מסגרת חיישנים כללית, אנחנו יכולים לצפות הופעה של עוד סוגים חדשים של סוגים שונים סוגי חיישנים.

תחום חשוב נוסף לעבודה עתידית הוא שיפור ה-General Sensor API עצמו, מפרט החיישן הוא כעת המלצה של מועמד, כך שעדיין יש זמן לבצע תיקונים ולהוסיף פונקציונליות חדשה שהמפתחים צריכים.

יש לך אפשרות לעזור!

הגעת למפרטי החיישן המלצה למועמד לכן אנחנו מעריכים מאוד את המשוב ממפתחי אתרים ודפדפנים. נשמח לקבל תוכלו לדעת אילו תכונות כדאי להוסיף או אם יש משהו שתרצו לשנות בממשק ה-API הנוכחי.

אפשר גם לשלוח בעיות שקשורות למפרט כבאגים להטמעה של Chrome.

משאבים

אישורים

המאמר הזה נבדק על ידי ג'ו מדלי קייס בסקית. תמונה ראשית (Hero) מאת Misko דרך Wikimedia Commons.