توفّر منصة 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] |
التسارع المقاس على طول المحور X بدون أي تعويض عن الانحياز | م/ث2 |
SensorEvent.values[1] |
التسارع المقاس على طول المحور Y بدون أي تعويض عن الانحياز | ||
SensorEvent.values[2] |
التسارع المقاس على طول المحور Z بدون أي تعويض عن الانحياز | ||
SensorEvent.values[3] |
التسارع المقاس على طول المحور X مع تعويض الانحياز المقدَّر | ||
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] |
معدّل الدوران حول المحور x | راديان/ثانية |
SensorEvent.values[1] |
معدّل الدوران حول المحور y | ||
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] |
قوة التسارع على طول المحور y (باستثناء الجاذبية) | ||
SensorEvent.values[2] |
قوة التسارع على طول المحور z (باستثناء الجاذبية) | ||
TYPE_ROTATION_VECTOR |
SensorEvent.values[0] |
مكوّن متّجه الدوران على طول المحور x (x * sin(θ/2)) | بدون وحدة |
SensorEvent.values[1] |
مكوّن متّجه الدوران على طول المحور y (y * sin(θ/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) ثلاثة أجهزة استشعار للحركة مستندة إلى البرامج، وهي: مستشعر الجاذبية ومستشعر التسارع الخطي ومستشعر متجه الدوران. تم تعديل هذه المستشعرات في الإصدار 4.0 من نظام التشغيل Android، وهي تستخدم الآن جيروسكوب الجهاز (بالإضافة إلى مستشعرات أخرى) لتحسين الثبات والأداء. إذا أردت تجربة هذه المستشعرات، يمكنك تحديدها باستخدام الطريقة getVendor()
والطريقة getVersion()
(المورّد هو Google LLC ورقم الإصدار هو 3). ويجب تحديد هذه المستشعرات حسب المورّد ورقم الإصدار لأنّ نظام 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 لأغراض توضيحية. إذا كنت تستخدم طريقة الفلترة هذه، قد تحتاج إلى اختيار قيمة ألفا مختلفة.
تستخدم مقاييس التسارع نظام الإحداثيات العادي للمستشعر. من الناحية العملية، يعني ذلك أنّ الشروط التالية تنطبق عندما يكون الجهاز موضوعًا بشكل مسطّح على طاولة في وضعه الطبيعي:
- إذا دفعت الجهاز من الجانب الأيسر (بحيث يتحرّك إلى اليمين)، ستكون قيمة التسارع على المحور x موجبة.
- إذا دفعت الجهاز من الأسفل (بحيث يتحرك بعيدًا عنك)، ستكون قيمة التسارع على المحور y موجبة.
- إذا دفعت الجهاز نحو السماء بمعدّل تسارع A متر في الثانية2، ستكون قيمة التسارع على المحور z مساوية لـ A + 9.81، وهو ما يتوافق مع تسارع الجهاز (+A متر في الثانية2) مطروحًا منه قوة الجاذبية (-9.81 متر في الثانية2).
- سيكون لجهاز ثابت قيمة تسارع تبلغ +9.81، وهو ما يتوافق مع تسارع الجهاز (0 متر/ثانية2 ناقص قوة الجاذبية التي تبلغ -9.81 متر/ثانية2).
بشكل عام، يُعدّ مقياس التسارع أداة استشعار جيدة للاستخدام في حال تتبُّع حركة الجهاز. تتضمّن معظم الهواتف والأجهزة اللوحية التي تعمل بنظام التشغيل Android مقياس تسارع، ويستهلك هذا المقياس طاقة أقل بنحو 10 مرات من أدوات استشعار الحركة الأخرى. أحد العيوب هو أنّك قد تحتاج إلى تنفيذ فلاتر تمرير منخفض وفلاتر تمرير مرتفع للتخلص من قوى الجاذبية وتقليل الضوضاء.
استخدام الجيروسكوب
يقيس الجيروسكوب معدّل الدوران بوحدة راديان/ثانية حول المحاور x وy وz للجهاز. يوضّح الرمز البرمجي التالي كيفية الحصول على مثيل من الجيروسكوب التلقائي:
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 أيضًا كيفية استخدام واجهات برمجة التطبيقات الموضّحة في هذه الصفحة.