يتيح نظام Android الأساسي العديد من أدوات الاستشعار التي تتيح لك مراقبة حركة الجهاز.
تختلف البُنى الممكنة لأدوات الاستشعار حسب نوعها:
- تعتمد أدوات استشعار الجاذبية والتسارع الخطي ومتجه الدوران والحركة الكبيرة وعداد الخطوات وكاشف الخطوات على الأجهزة أو البرامج.
- تعتمد أدوات استشعار مقياس التسارع والجيروسكوب دائمًا على الأجهزة.
تحتوي معظم الأجهزة التي تعمل بنظام التشغيل Android على مقياس تسارع، ويشتمل العديد منها الآن على جيروسكوب. تختلف إمكانية توفّر أدوات الاستشعار المستندة إلى البرامج بشكل أكبر لأنّها غالبًا ما تعتمد على أداة استشعار واحدة أو أكثر من أدوات الاستشعار المستندة إلى الأجهزة للحصول على بياناتها. ويمكن لأدوات الاستشعار المستندة إلى البرامج هذه الحصول على بياناتها إما من مقياس التسارع ومقياس المغناطيسية أو من الجيروسكوب، وذلك حسب الجهاز.
تُعد أجهزة استشعار الحركة مفيدة لمراقبة حركة الجهاز، مثل الإمالة أو الاهتزاز أو الدوران أو التأرجح. تعكس الحركة عادةً إدخالاً مباشرًا من المستخدم (على سبيل المثال، توجيه المستخدم لسيارة في لعبة أو تحكّمه في كرة في لعبة)، ولكن يمكن أن تعكس أيضًا البيئة المادية التي يتواجد فيها الجهاز (على سبيل المثال، التحرك معك أثناء قيادة سيارتك). في الحالة الأولى، تتم مراقبة الحركة بالنسبة إلى إطار مرجعي للجهاز أو إطار مرجعي للتطبيق، وفي الحالة الثانية، تتم مراقبة الحركة بالنسبة إلى إطار مرجعي للعالم. لا تُستخدم أجهزة استشعار الحركة عادةً بمفردها لمراقبة موضع الجهاز، ولكن يمكن استخدامها مع أجهزة استشعار أخرى، مثل جهاز استشعار المجال المغناطيسي الأرضي، لتحديد موضع الجهاز بالنسبة إلى الإطار المرجعي العالمي (لمزيد من المعلومات، يُرجى الاطّلاع على أجهزة استشعار الموضع).
تعرض جميع أجهزة استشعار الحركة صفائف متعددة الأبعاد لقيم أجهزة الاستشعار لكل SensorEvent. على سبيل المثال، أثناء حدث واحد لأداة الاستشعار، يعرض مقياس التسارع بيانات قوة التسارع لمحاور الإحداثيات الثلاثة، ويعرض الجيروسكوب بيانات معدل الدوران لمحاور الإحداثيات الثلاثة. يتم عرض قيم البيانات هذه في مصفوفة float
(values) مع مَعلمات SensorEvent
أخرى. يلخّص الجدول 1 أجهزة استشعار الحركة المتوفّرة على نظام Android الأساسي.
الجدول 1: أجهزة استشعار الحركة المتوافقة مع نظام Android الأساسي
| أداة استشعار | بيانات أحداث أداة الاستشعار | الوصف | وحدات القياس |
|---|---|---|---|
TYPE_ACCELEROMETER |
SensorEvent.values[0] |
قوة التسارع على طول المحور x (بما في ذلك الجاذبية) | متر/ثانية2 |
SensorEvent.values[1] |
قوة التسارع على طول المحور y (بما في ذلك الجاذبية) | ||
SensorEvent.values[2] |
قوة التسارع على طول المحور z (بما في ذلك الجاذبية) | ||
TYPE_ACCELEROMETER_UNCALIBRATED |
SensorEvent.values[0] |
التسارع الذي تم قياسه على طول المحور "س" بدون أي تعويض عن الانحياز | متر/ثانية2 |
SensorEvent.values[1] |
التسارع الذي تم قياسه على طول المحور ص بدون أي تعويض عن الانحياز | ||
SensorEvent.values[2] |
التسارع الذي تم قياسه على طول المحور Z بدون أي تعويض عن الانحياز | ||
SensorEvent.values[3] |
التسارع الذي تم قياسه على طول المحور س مع تعويض الانحياز المقدَّر | ||
SensorEvent.values[4] |
التسارع الذي تم قياسه على طول المحور Y مع تعويض الانحياز المقدَّر | ||
SensorEvent.values[5] |
التسارع الذي تم قياسه على طول المحور Z مع تعويض الانحياز المقدَّر | ||
TYPE_GRAVITY |
SensorEvent.values[0] |
قوة الجاذبية على طول المحور x | متر/ثانية2 |
SensorEvent.values[1] |
قوة الجاذبية على طول المحور الصادي (Y) | ||
SensorEvent.values[2] |
قوة الجاذبية على طول المحور z | ||
TYPE_GYROSCOPE |
SensorEvent.values[0] |
معدّل الدوران حول المحور س | راديان/ثانية |
SensorEvent.values[1] |
معدّل الدوران حول المحور ص | ||
SensorEvent.values[2] |
معدّل الدوران حول المحور z | ||
TYPE_GYROSCOPE_UNCALIBRATED |
SensorEvent.values[0] |
معدّل الدوران (بدون تعويض الانحراف) حول المحور x | راديان/ثانية |
SensorEvent.values[1] |
معدّل الدوران (بدون تعويض الانحراف) حول المحور الصادي | ||
SensorEvent.values[2] |
معدّل الدوران (بدون تعويض الانحراف) حول المحور z | ||
SensorEvent.values[3] |
الانحراف المقدَّر حول المحور x | ||
SensorEvent.values[4] |
الانحراف المقدَّر حول المحور الصادي | ||
SensorEvent.values[5] |
الانحراف المقدَّر حول المحور z | ||
TYPE_LINEAR_ACCELERATION |
SensorEvent.values[0] |
قوة التسارع على طول المحور x (باستثناء الجاذبية) | متر/ثانية2 |
SensorEvent.values[1] |
قوة التسارع على طول المحور ص (باستثناء الجاذبية). | ||
SensorEvent.values[2] |
قوة التسارع على طول المحور z (باستثناء الجاذبية) | ||
TYPE_ROTATION_VECTOR |
SensorEvent.values[0] |
مكوّن متّجه الدوران على طول المحور x (x * sin(θ/2)). | بدون وحدة |
SensorEvent.values[1] |
مكوّن متّجه الدوران على طول المحور ص (ص * جا(θ/2)). | ||
SensorEvent.values[2] |
مكوّن متّجه الدوران على طول المحور z (z * sin(θ/2)). | ||
SensorEvent.values[3] |
المكوّن القياسي لمتّجه الدوران ((cos(θ/2)).1 | ||
TYPE_SIGNIFICANT_MOTION |
لا ينطبق | (لا ينطبق) | لا ينطبق |
TYPE_STEP_COUNTER |
SensorEvent.values[0] |
عدد الخطوات التي اتخذها المستخدم منذ آخر إعادة تشغيل أثناء تفعيل أداة الاستشعار | الخطوات |
TYPE_STEP_DETECTOR |
لا ينطبق | (لا ينطبق) | لا ينطبق |
1 مكوّن القيمة العددية هو قيمة اختيارية.
يُعدّ جهاز استشعار متجه الدوران وجهاز استشعار الجاذبية من أكثر أجهزة الاستشعار استخدامًا لرصد الحركة ومراقبتها. تتميّز أداة استشعار متجه الدوران بتعدّد استخداماتها، ويمكن استخدامها في مجموعة كبيرة من المهام المتعلّقة بالحركة، مثل رصد الإيماءات ومراقبة التغيير في الزاوية ومراقبة التغييرات في الاتجاه النسبي. على سبيل المثال، يُعدّ جهاز استشعار متجه الدوران مثاليًا إذا كنت تطوّر لعبة أو تطبيق الواقع المعزّز أو بوصلة ثنائية أو ثلاثية الأبعاد أو تطبيقًا لتثبيت الكاميرا. في معظم الحالات، يُعدّ استخدام أجهزة الاستشعار هذه خيارًا أفضل من استخدام مقياس التسارع وجهاز استشعار المجال المغناطيسي الأرضي أو جهاز استشعار الاتجاه.
أدوات الاستشعار في مشروع مفتوح المصدر لنظام Android
يوفّر "مشروع Android المفتوح المصدر" (AOSP) ثلاثة أجهزة استشعار حركة مستندة إلى البرامج: جهاز استشعار الجاذبية وجهاز استشعار التسارع الخطي وجهاز استشعار متجه الدوران. تم تعديل أدوات الاستشعار هذه في Android 4.0، وهي تستخدم الآن جيروسكوب الجهاز (بالإضافة إلى أدوات استشعار أخرى) لتحسين الثبات والأداء. إذا أردت تجربة أدوات الاستشعار هذه، يمكنك تحديدها باستخدام الطريقة getVendor() والطريقة getVersion()
(المورّد هو Google LLC، ورقم الإصدار هو 3). يجب تحديد أدوات الاستشعار هذه حسب المورّد ورقم الإصدار لأنّ نظام Android يعتبرها أدوات استشعار ثانوية. على سبيل المثال، إذا قدّمت الشركة المصنّعة للجهاز أداة استشعار الجاذبية الخاصة بها، ستظهر أداة استشعار الجاذبية في مشروع Android المفتوح المصدر (AOSP) كأداة استشعار ثانوية. تعتمد أدوات الاستشعار الثلاثة هذه على
جيروسكوب: إذا لم يكن الجهاز يحتوي على جيروسكوب، فلن تظهر أدوات الاستشعار هذه ولن تكون
متاحة للاستخدام.
استخدام أداة استشعار الجاذبية
يوفّر مستشعر الجاذبية متجهًا ثلاثي الأبعاد يشير إلى اتجاه الجاذبية ومقدارها. يُستخدم هذا المستشعر عادةً لتحديد الاتجاه النسبي للجهاز في المساحة. يوضّح لك الرمز التالي كيفية الحصول على مثيل لأداة استشعار الجاذبية التلقائية:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
الوحدات هي نفسها المستخدَمة في أداة استشعار التسارع (متر/ثانية2)، ونظام الإحداثيات هو نفسه المستخدَم في أداة استشعار التسارع.
ملاحظة: عندما يكون الجهاز في وضع السكون، يجب أن تكون بيانات أداة استشعار الجاذبية مطابقة لبيانات مقياس التسارع.
استخدام مقياس التسارع الخطي
يوفّر لك مستشعر التسارع الخطي متجهًا ثلاثي الأبعاد يمثّل التسارع على طول كل محور من محاور الجهاز، باستثناء الجاذبية. يمكنك استخدام هذه القيمة لرصد الإيماءات. يمكن أن تكون القيمة أيضًا بمثابة مدخلات لنظام الملاحة بالقصور الذاتي، والذي يستخدم الحساب الميت. يوضّح الرمز التالي كيفية الحصول على مثيل لأداة استشعار التسارع الخطي التلقائية:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
من الناحية النظرية، يوفّر لك جهاز الاستشعار هذا بيانات التسارع وفقًا للعلاقة التالية:
linear acceleration = acceleration - acceleration due to gravity
يمكنك عادةً استخدام أداة الاستشعار هذه عندما تريد الحصول على بيانات التسارع بدون تأثير الجاذبية. على سبيل المثال، يمكنك استخدام أداة الاستشعار هذه لمعرفة سرعة سيارتك. يحتوي مستشعر التسارع الخطي دائمًا على إزاحة، والتي تحتاج إلى إزالتها. أبسط طريقة للقيام بذلك هي إنشاء خطوة معايرة في تطبيقك. أثناء المعايرة، يمكنك أن تطلب من المستخدم وضع الجهاز على طاولة، ثم قراءة الإزاحات لجميع المحاور الثلاثة. يمكنك بعد ذلك طرح هذا الإزاحة من القراءات المباشرة لمستشعر التسارع للحصول على التسارع الخطي الفعلي.
نظام الإحداثيات لأداة الاستشعار هو نفسه المستخدَم في أداة استشعار التسارع، وكذلك وحدات القياس (متر/ثانية2).
استخدام أداة استشعار متجه الدوران
يمثّل متجه الدوران اتجاه الجهاز كمزيج من الزاوية والمحور، حيث يدور الجهاز بزاوية θ حول محور (س أو ص أو ع). يوضّح لك الرمز التالي كيفية الحصول على نسخة من أداة استشعار متجه الدوران التلقائية:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
يتم التعبير عن العناصر الثلاثة لمتجه الدوران على النحو التالي:
حيث يكون مقدار متّجه الدوران مساويًا لـ sin(θ/2)، ويكون اتجاه متّجه الدوران مساويًا لاتجاه محور الدوران.
الشكل 1: نظام الإحداثيات الذي يستخدمه جهاز استشعار متجه الدوران
تساوي العناصر الثلاثة لمتجه الدوران آخر ثلاثة مكونات من وحدة الكواتيرنيون (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). عناصر متجه الدوران بدون وحدة. يتم تحديد المحاور x وy وz بالطريقة نفسها التي يتم بها تحديد أداة استشعار التسارع. يتم تحديد نظام الإحداثيات المرجعي على أنّه أساس متعامد مباشر (انظر الشكل 1). يتميّز نظام الإحداثيات هذا بالخصائص التالية:
- يتم تعريف X على أنّه الجداء المتّجه Y x Z. وهو مماس للأرض في الموقع الجغرافي الحالي للجهاز ويشير تقريبًا إلى الشرق.
- Y هو المماس للأرض في الموقع الجغرافي الحالي للجهاز ويشير إلى القطب الشمالي المغناطيسي الأرضي.
- يشير المحور Z نحو السماء ويكون عموديًا على مستوى الأرض.
للحصول على نموذج تطبيق يوضّح كيفية استخدام أداة استشعار متّجه الدوران، يُرجى الاطّلاع على RotationVectorDemo.java.
استخدام جهاز استشعار الحركة الملحوظة
يُشغِّل جهاز استشعار الحركة المهمة حدثًا في كل مرة يتم فيها رصد حركة مهمة، ثم يوقف نفسه. الحركة الملحوظة هي حركة قد تؤدي إلى تغيير في الموقع الجغرافي للمستخدم، مثل المشي أو ركوب الدراجة أو الجلوس في سيارة متحركة. يوضّح لك الرمز التالي كيفية الحصول على مثيل لجهاز استشعار الحركة الملحوظة التلقائي وكيفية تسجيل متتبِّع الأحداث:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION) val triggerEventListener = object : TriggerEventListener() { override fun onTrigger(event: TriggerEvent?) { // Do work } } mSensor?.also { sensor -> sensorManager.requestTriggerSensor(triggerEventListener, sensor) }
Java
private SensorManager sensorManager; private Sensor sensor; private TriggerEventListener triggerEventListener; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); triggerEventListener = new TriggerEventListener() { @Override public void onTrigger(TriggerEvent event) { // Do work } }; sensorManager.requestTriggerSensor(triggerEventListener, mSensor);
لمزيد من المعلومات، يُرجى الاطّلاع على TriggerEventListener.
استخدام أداة استشعار عدّ الخطوات
يوفّر مستشعر عدّ الخطوات عدد الخطوات التي اتخذها المستخدم منذ آخر إعادة تشغيل أثناء تفعيل المستشعر. يستغرق عدّاد الخطوات وقتًا أطول (يصل إلى 10 ثوانٍ)، ولكنه أكثر دقة من أداة استشعار رصد الخطوات.
ملاحظة: يجب الإفصاح عن إذن
ACTIVITY_RECOGNITION
ليتمكّن تطبيقك من استخدام أداة الاستشعار هذه على الأجهزة التي تعمل بنظام التشغيل
Android 10 (مستوى واجهة برمجة التطبيقات 29) أو الإصدارات الأحدث.
يوضّح الرمز التالي كيفية الحصول على نسخة من أداة الاستشعار التلقائية لعدّ الخطوات:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
للحفاظ على بطارية الأجهزة التي تشغّل تطبيقك، عليك استخدام فئة JobScheduler لاسترداد القيمة الحالية من أداة استشعار عدّ الخطوات في فاصل زمني محدّد. على الرغم من أنّ أنواع التطبيقات المختلفة تتطلّب فترات زمنية مختلفة لقراءة أداة الاستشعار، يجب جعل هذه الفترة الزمنية أطول ما يمكن ما لم يتطلّب تطبيقك بيانات في الوقت الفعلي من أداة الاستشعار.
استخدام أداة استشعار رصد الخطوات
تُفعِّل أداة استشعار رصد الخطوات حدثًا في كل مرة يخطو فيها المستخدم خطوة. ومن المتوقّع أن يكون وقت الاستجابة أقل من ثانيتين.
ملاحظة: يجب الإفصاح عن إذن
ACTIVITY_RECOGNITION
ليتمكّن تطبيقك من استخدام أداة الاستشعار هذه على الأجهزة التي تعمل بنظام التشغيل
Android 10 (مستوى واجهة برمجة التطبيقات 29) أو الإصدارات الأحدث.
يوضّح الرمز التالي كيفية الحصول على مثيل لأداة الاستشعار التلقائية لرصد الخطوات:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
العمل باستخدام البيانات الأولية
توفّر أدوات الاستشعار التالية لتطبيقك بيانات أولية حول القوى الخطية والدورانية التي يتم تطبيقها على الجهاز. لاستخدام القيم من أدوات الاستشعار هذه بفعالية، عليك فلترة العوامل من البيئة، مثل الجاذبية. قد تحتاج أيضًا إلى تطبيق خوارزمية تجانس على اتجاه القيم لتقليل التشويش.
استخدام مقياس التسارع
يقيس جهاز استشعار التسارع التسارع المطبق على الجهاز، بما في ذلك قوة الجاذبية. يوضّح الرمز التالي كيفية الحصول على مثيل لأداة استشعار التسارع التلقائية:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
ملاحظة: إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، فإنّ أداة الاستشعار هذه محدودة المعدّل.
من الناحية النظرية، يحدّد جهاز استشعار التسارع مقدار التسارع الذي يتم تطبيقه على الجهاز (Ad) من خلال قياس القوى التي يتم تطبيقها على جهاز الاستشعار نفسه (Fs) باستخدام العلاقة التالية:
ومع ذلك، تؤثر قوة الجاذبية دائمًا في التسارع الذي يتم قياسه وفقًا للعلاقة التالية:
لهذا السبب، عندما يكون الجهاز موضوعًا على طاولة (ولا يتسارع)، يقرأ مقياس التسارع مقدار g = 9.81 م/ث2. وبالمثل، عندما يكون الجهاز في حالة سقوط حر وبالتالي يتسارع بسرعة نحو الأرض بمعدل 9.81 م/ث2، يقرأ مقياس التسارع مقدار g = 0 م/ث2. لذلك، لقياس التسارع الحقيقي للجهاز، يجب إزالة مساهمة قوة الجاذبية من بيانات مقياس التسارع. ويمكن تحقيق ذلك من خلال تطبيق فلتر تمرير عالٍ. في المقابل، يمكن استخدام مرشح تمرير منخفض لعزل قوة الجاذبية. يوضّح المثال التالي كيفية إجراء ذلك:
Kotlin
override fun onSensorChanged(event: SensorEvent) { // In this example, alpha is calculated as t / (t + dT), // where t is the low-pass filter's time-constant and // dT is the event delivery rate. val alpha: Float = 0.8f // Isolate the force of gravity with the low-pass filter. gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0] gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1] gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2] // Remove the gravity contribution with the high-pass filter. linear_acceleration[0] = event.values[0] - gravity[0] linear_acceleration[1] = event.values[1] - gravity[1] linear_acceleration[2] = event.values[2] - gravity[2] }
Java
public void onSensorChanged(SensorEvent event){ // In this example, alpha is calculated as t / (t + dT), // where t is the low-pass filter's time-constant and // dT is the event delivery rate. final float alpha = 0.8; // Isolate the force of gravity with the low-pass filter. gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]; gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]; gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]; // Remove the gravity contribution with the high-pass filter. linear_acceleration[0] = event.values[0] - gravity[0]; linear_acceleration[1] = event.values[1] - gravity[1]; linear_acceleration[2] = event.values[2] - gravity[2]; }
ملاحظة: يمكنك استخدام العديد من الأساليب المختلفة لفلترة بيانات أجهزة الاستشعار. تستخدم عينة التعليمات البرمجية أعلاه ثابت فلتر بسيط (ألفا) لإنشاء فلتر تمرير منخفض. يتم اشتقاق ثابت الفلتر هذا من ثابت زمني (t)، وهو تمثيل تقريبي لوقت الاستجابة الذي يضيفه الفلتر إلى أحداث أداة الاستشعار، ومعدّل عرض أحداث أداة الاستشعار (dt). تستخدم عينة التعليمات البرمجية قيمة ألفا تبلغ 0.8 لأغراض توضيحية. إذا كنت تستخدم طريقة التصفية هذه، فقد تحتاج إلى اختيار قيمة ألفا مختلفة.
تستخدم مقاييس التسارع نظام الإحداثيات العادي لأدوات الاستشعار. من الناحية العملية، يعني ذلك أنّ الشروط التالية تنطبق عندما يكون الجهاز موضوعًا بشكل مسطّح على طاولة في اتجاهه الطبيعي:
- إذا دفعت الجهاز من الجانب الأيسر (ليتحرك إلى اليمين)، تكون قيمة التسارع على المحور س موجبة.
- إذا دفعت الجهاز من الأسفل (بحيث يتحرك بعيدًا عنك)، تكون قيمة التسارع على المحور y موجبة.
- إذا دفعت الجهاز نحو السماء بتسارع مقداره A متر/ثانية2، ستكون قيمة التسارع على المحور z مساوية لـ A + 9.81، وهو ما يتوافق مع تسارع الجهاز (+A متر/ثانية2) مطروحًا منه قوة الجاذبية (-9.81 متر/ثانية2).
- ستكون قيمة تسارع الجهاز الثابت +9.81، وهي قيمة تتوافق مع تسارع الجهاز (0 متر/ثانية2 مطروحًا منها قوة الجاذبية التي تبلغ -9.81 متر/ثانية2).
بشكل عام، يُعد مقياس التسارع أداة استشعار جيدة للاستخدام إذا كنت تراقب حركة الجهاز. يحتوي كل هاتف وجهاز لوحي يعمل بنظام التشغيل Android تقريبًا على مقياس تسارع، ويستخدم طاقة أقل بمقدار 10 مرات تقريبًا من أجهزة استشعار الحركة الأخرى. ومن عيوبها أنّه قد يكون عليك استخدام فلاتر الترددات المنخفضة والعالية لإزالة قوى الجاذبية وتقليل الضوضاء.
استخدام الجيروسكوب
يقيس الجيروسكوب معدّل الدوران بالراديان في الثانية حول المحور السيني والصادي والعيني للجهاز. يوضّح لك الرمز التالي كيفية الحصول على مثيل من الجيروسكوب التلقائي:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
ملاحظة: إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، فإنّ أداة الاستشعار هذه محدودة المعدّل.
نظام الإحداثيات لأداة الاستشعار هو نفسه المستخدَم في أداة استشعار التسارع. يكون الدوران موجبًا في اتجاه عكس عقارب الساعة، أي أنّ المراقب الذي ينظر من موقع موجب على المحور x أو y أو z إلى جهاز موضوع في نقطة الأصل سيُبلغ عن دوران موجب إذا بدا أنّ الجهاز يدور عكس عقارب الساعة. هذا هو التعريف الرياضي القياسي للدوران الموجب وليس هو نفسه تعريف الدوران الذي يستخدمه مستشعر الاتجاه.
عادةً ما يتم دمج مخرجات الجيروسكوب بمرور الوقت لحساب دوران يصف تغيير الزوايا بمرور الوقت. على سبيل المثال:
Kotlin
// Create a constant to convert nanoseconds to seconds. private val NS2S = 1.0f / 1000000000.0f private val deltaRotationVector = FloatArray(4) { 0f } private var timestamp: Float = 0f override fun onSensorChanged(event: SensorEvent?) { // This timestep's delta rotation to be multiplied by the current rotation // after computing it from the gyro sample data. if (timestamp != 0f && event != null) { val dT = (event.timestamp - timestamp) * NS2S // Axis of the rotation sample, not normalized yet. var axisX: Float = event.values[0] var axisY: Float = event.values[1] var axisZ: Float = event.values[2] // Calculate the angular speed of the sample val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ) // Normalize the rotation vector if it's big enough to get the axis // (that is, EPSILON should represent your maximum allowable margin of error) if (omegaMagnitude > EPSILON) { axisX /= omegaMagnitude axisY /= omegaMagnitude axisZ /= omegaMagnitude } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f val sinThetaOverTwo: Float = sin(thetaOverTwo) val cosThetaOverTwo: Float = cos(thetaOverTwo) deltaRotationVector[0] = sinThetaOverTwo * axisX deltaRotationVector[1] = sinThetaOverTwo * axisY deltaRotationVector[2] = sinThetaOverTwo * axisZ deltaRotationVector[3] = cosThetaOverTwo } timestamp = event?.timestamp?.toFloat() ?: 0f val deltaRotationMatrix = FloatArray(9) { 0f } SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector); // User code should concatenate the delta rotation we computed with the current rotation // in order to get the updated rotation. // rotationCurrent = rotationCurrent * deltaRotationMatrix; }
Java
// Create a constant to convert nanoseconds to seconds. private static final float NS2S = 1.0f / 1000000000.0f; private final float[] deltaRotationVector = new float[4](); private float timestamp; public void onSensorChanged(SensorEvent event) { // This timestep's delta rotation to be multiplied by the current rotation // after computing it from the gyro sample data. if (timestamp != 0) { final float dT = (event.timestamp - timestamp) * NS2S; // Axis of the rotation sample, not normalized yet. float axisX = event.values[0]; float axisY = event.values[1]; float axisZ = event.values[2]; // Calculate the angular speed of the sample float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ); // Normalize the rotation vector if it's big enough to get the axis // (that is, EPSILON should represent your maximum allowable margin of error) if (omegaMagnitude > EPSILON) { axisX /= omegaMagnitude; axisY /= omegaMagnitude; axisZ /= omegaMagnitude; } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. float thetaOverTwo = omegaMagnitude * dT / 2.0f; float sinThetaOverTwo = sin(thetaOverTwo); float cosThetaOverTwo = cos(thetaOverTwo); deltaRotationVector[0] = sinThetaOverTwo * axisX; deltaRotationVector[1] = sinThetaOverTwo * axisY; deltaRotationVector[2] = sinThetaOverTwo * axisZ; deltaRotationVector[3] = cosThetaOverTwo; } timestamp = event.timestamp; float[] deltaRotationMatrix = new float[9]; SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector); // User code should concatenate the delta rotation we computed with the current rotation // in order to get the updated rotation. // rotationCurrent = rotationCurrent * deltaRotationMatrix; }
توفّر أجهزة الجيروسكوب العادية بيانات دوران أولية بدون أي فلترة أو تصحيح للتشويش والانحراف (الانحياز). في الواقع، ستؤدي الضوضاء والانحراف في الجيروسكوب إلى حدوث أخطاء يجب تعويضها. يمكنك عادةً تحديد الانحراف (الانحياز) والتشويش من خلال مراقبة أدوات استشعار أخرى، مثل أداة استشعار الجاذبية أو مقياس التسارع.
استخدام الجيروسكوب غير المعاير
يشبه الجيروسكوب غير المعاير الجيروسكوب،
إلا أنّه لا يتم تطبيق أي تعويض عن انحراف الجيروسكوب على معدّل الدوران. ولا يزال يتم تطبيق معايرة المصنع وتعويض درجة الحرارة على معدل الدوران. ويُعدّ الجيروسكوب غير المعاير مفيدًا في مرحلة ما بعد المعالجة ودمج بيانات الاتجاه. بشكل عام،
ستكون قيمة gyroscope_event.values[0] قريبة من
uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3].
أي،
calibrated_x ~= uncalibrated_x - bias_estimate_x
ملاحظة: تقدّم أدوات الاستشعار غير المعايرة نتائج أولية أكثر وقد تتضمّن بعض الانحياز، ولكن قياساتها تحتوي على عدد أقل من القفزات من التصحيحات المطبّقة من خلال المعايرة. قد تفضّل بعض التطبيقات هذه النتائج غير المعايرة لأنّها أكثر سلاسة وموثوقية. على سبيل المثال، إذا كان أحد التطبيقات يحاول إجراء دمج بيانات أجهزة الاستشعار، فإنّ إدخال عمليات المعايرة يمكن أن يؤدي إلى تشويه النتائج.
بالإضافة إلى معدلات الدوران، يوفّر الجيروسكوب غير المعاير أيضًا الانحراف المقدَّر حول كل محور. يوضّح لك الرمز التالي كيفية الحصول على نسخة افتراضية من الجيروسكوب غير المعاير:
Kotlin
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);
عيّنات تعليمات برمجية إضافية
يوضّح نموذج BatchStepSensor بشكل أكبر كيفية استخدام واجهات برمجة التطبيقات الموضّحة في هذه الصفحة.