مستشعرات الحركة

يوفر نظام Android الأساسي العديد من أدوات الاستشعار التي تتيح لك مراقبة حركة الجهاز.

تتفاوت البُنى الأساسية المحتمَلة لأجهزة الاستشعار حسب نوعها:

  • إنّ أدوات استشعار الجاذبية والتسارع الخطي ومتّجه الدوران والحركة المهمة وعدّاد الخطوات وأدوات الكشف عن الخطوات إما مستنِدة إلى الأجهزة أو مستندة إلى البرامج.
  • تعتمد أجهزة استشعار مقياس التسارع والجيروسكوب دائمًا على الأجهزة.

وتحتوي معظم الأجهزة التي تعمل بنظام التشغيل Android على مقياس تسارع، ويتضمّن الكثير منها الآن جيروسكوب. يُعد توافر أجهزة الاستشعار المستندة إلى البرامج أكثر متغيرًا لأنها تعتمد غالبًا على جهاز استشعار واحد أو أكثر من الأجهزة لاستخلاص بياناتها. واعتمادًا على الجهاز، يمكن لهذه المستشعرات المستندة إلى البرامج أن تستمد بياناتها إما من مقياس التسارع ومقياس المغناطيسية أو من الجيروسكوب.

تُعد أجهزة استشعار الحركة مفيدة لمراقبة حركة الجهاز، مثل الإمالة أو الاهتزاز أو التدوير أو التأرجح. عادةً ما تكون الحركة انعكاسًا لإدخال المستخدم مباشرةً (على سبيل المثال، عندما يقود المستخدم سيارة في لعبة أو يتحكم في كرة داخل لعبة)، ولكنها قد تكون أيضًا انعكاسًا للبيئة المادية التي يجلس فيها الجهاز (على سبيل المثال، التحرك معك أثناء قيادة السيارة). في الحالة الأولى، يتم مراقبة الحركة بالنسبة إلى الإطار المرجعي للجهاز أو الإطار المرجعي للتطبيق. وفي الحالة الثانية، يتم مراقبة الحركة بالنسبة إلى الإطار المرجعي في العالم. لا تُستخدم أجهزة استشعار الحركة في حد ذاتها عادةً لمراقبة موضع الجهاز، ولكن يمكن استخدامها مع أدوات الاستشعار الأخرى، مثل مستشعر المجال المغناطيسي الجغرافي، لتحديد موضع الجهاز بالنسبة إلى الإطار المرجعي للعالم (راجِع أجهزة استشعار الموضع للحصول على مزيد من المعلومات).

تعرض كل أجهزة استشعار الحركة صفائف متعددة الأبعاد لقيم أداة الاستشعار لكل SensorEvent. على سبيل المثال، أثناء حدث استشعار واحد، يعرض مقياس التسارع بيانات قوة التسارع للمحاور الثلاثة ومعدّل إرجاع الجيروسكوب لبيانات الدوران لمحاور الإحداثيات الثلاثة. ويتم عرض قيم البيانات هذه في مصفوفة float (values) مع معلَمات SensorEvent الأخرى. يلخّص الجدول 1 أجهزة استشعار الحركة المتوفرة على نظام Android الأساسي.

الجدول 1. أجهزة استشعار الحركة المتاحة على نظام Android الأساسي

أداة استشعار بيانات أحداث أداة الاستشعار الوصف وحدات القياس
TYPE_ACCELEROMETER SensorEvent.values[0] قوة التسارع على طول المحور س (بما في ذلك الجاذبية). م/ث2
SensorEvent.values[1] قوة التسارع على طول المحور ص (بما في ذلك الجاذبية).
SensorEvent.values[2] قوة التسارع على طول المحور z (بما في ذلك الجاذبية).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] قياس التسارع على طول المحور س دون أي تعويض للتحيز. م/ث2
SensorEvent.values[1] تم قياس التسارع على طول المحور ص دون أي تعويض للتحيز.
SensorEvent.values[2] قياس التسارع على طول المحور Z دون أي تعويض للتحيز.
SensorEvent.values[3] قياس التسارع على طول المحور س مع تعويض التحيز المقدر.
SensorEvent.values[4] تم قياس التسارع على طول المحور ص مع تعويض التحيز المقدر.
SensorEvent.values[5] تم قياس التسارع على طول المحور Z مع تعويض التحيز المقدر.
TYPE_GRAVITY SensorEvent.values[0] قوة الجاذبية على المحور x. م/ث2
SensorEvent.values[1] قوة الجاذبية على المحور ص.
SensorEvent.values[2] قوة الجاذبية على المحور z.
TYPE_GYROSCOPE SensorEvent.values[0] معدل الدوران حول المحور x. راد/ثانية
SensorEvent.values[1] معدل الدوران حول المحور ص.
SensorEvent.values[2] معدل الدوران حول المحور z.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] معدل الدوران (بدون تعويض الدوران) حول المحور x. راد/ثانية
SensorEvent.values[1] معدل الدوران (بدون تعويض الانحراف) حول المحور y.
SensorEvent.values[2] معدل الدوران (بدون تعويض الدوران) حول المحور z.
SensorEvent.values[3] الانحراف المُقدّر حول المحور x.
SensorEvent.values[4] الانجراف المقدر حول المحور ص.
SensorEvent.values[5] الانجراف المقدّر حول المحور z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] قوة التسارع على طول المحور س (باستثناء الجاذبية). م/ث2
SensorEvent.values[1] قوة التسارع على طول المحور ص (باستثناء الجاذبية).
SensorEvent.values[2] قوة التسارع على طول المحور z (باستثناء الجاذبية).
TYPE_ROTATION_VECTOR SensorEvent.values[0] مكوّن متجه التدوير على طول المحور x (x * sin(الوارد/2)). بلا وحدة
SensorEvent.values[1] مكوّن متجه الدوران على طول المحور ص (y * sin(الوارد/2)).
SensorEvent.values[2] مكوّن متجه التدوير على طول المحور z (z * sin(الوارد/2)).
SensorEvent.values[3] المكوِّن العددي لمتجه الدوران ((cos(main/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 يعتبر هذه المستشعرات الثلاثة هذه أجهزة استشعار ثانوية. على سبيل المثال، إذا وفّرت الشركة المصنّعة للجهاز أداة استشعار الجاذبية الخاصة بها، يظهر جهاز استشعار الجاذبية 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).

استخدام أداة استشعار متجه الدوران

يمثل متجه الدوران اتجاه الجهاز كمزيج من زاوية ومحور، حيث تم تدوير الجهاز بزاوية ƒ حول محور (x، y، z). يوضح لك التعليمة البرمجية التالية كيفية الحصول على مثيل لمستشعر متجه الدوران الافتراضي:

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

يتم التعبير عن العناصر الثلاثة لمتجه الدوران على النحو التالي:

x*sin(regex/2), y*sin(result/2), z*sin(reset/2)

عندما يكون حجم متجه الدوران يساوي sin(reset/2)، ويكون اتجاه متجه الدوران مساويًا لاتجاه محور الدوران.

الشكل 1. نظام التنسيق المستخدم بواسطة مستشعر متجه الدوران.

العناصر الثلاثة لمتجه الدوران تساوي المكوّنات الثلاثة الأخيرة للوحدة الرباعية (cos(CONVERSION/2) وx*sin(reset/2), y*sin(المستهدفة/2), z*sin(المستهدفة/2)). عناصر متجه الدوران لا وحدة. تم تحديد المحاور x وy وz بالطريقة ذاتها التي يتم بها تحديد محاور استشعار التسارع. يتم تعريف نظام الإحداثيات المرجعي على أنه أساس خصاب طبيعي مباشر (انظر الشكل 1). خصائص النظام الإحداثي هذا هي الخصائص التالية:

  • يُعرّف س على أنه ناتج الخط المتجه ص × ع. يكون عرضيًا للأرض في الموقع الحالي للجهاز ويشير إلى الشرق تقريبًا.
  • يُعدّ ص عرضيًا للأرض في الموقع الجغرافي الحالي للجهاز ويشير إلى القطب الشمالي المغناطيسي الجغرافي.
  • يشير 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);

ملاحظة: إذا كان تطبيقك يستهدف الإصدار Android 12 (المستوى 31 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تكون أداة الاستشعار هذه محدودة للمعدل.

من الناحية النظرية، يحدّد جهاز استشعار التسارع التسارع المطبّق على الجهاز (د) من خلال قياس القوى المطبَّقة على أداة الاستشعار نفسها (F) باستخدام العلاقة التالية:

A_D=-(1/ماس)∑F_S

ومع ذلك، تؤثر قوة الجاذبية دائمًا في التسارع الذي يتم قياسه وفقًا للعلاقة التالية:

A_D=-g-(1/mass)∑F_S

لهذا السبب، عندما يكون الجهاز على طاولة (وليس في وضع تسارع)، يشير مقياس التسارع إلى المقدار g = 9.81 م/ث2. وبالمثل، عندما يكون الجهاز في سقوط حر، وبالتالي يتسارع بسرعة نحو الأرض بسرعة 9.81 م/ث2، يظهر مقياس التسارع المقدار غرامي = 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 m/s 2، تساوي قيمة التسارع z A + 9.81، والتي تتجاوب مع تسارع الجهاز (+A m/s2) مطروحًا منه قوة الجاذبية (-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);

ملاحظة: إذا كان تطبيقك يستهدف الإصدار Android 12 (المستوى 31 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تكون أداة الاستشعار هذه محدودة للمعدل.

نظام الإحداثيات الخاص بجهاز الاستشعار هو نفسه المستخدَم في جهاز استشعار التسارع. التدوير إيجابي في اتجاه عكس عقارب الساعة؛ أي أن المراقب الذي ينظر من موقع موجب على المحور س أو ص أو ع في جهاز موضوع على الأصل سيبلغ عن دوران إيجابي إذا بدا أن الجهاز يدور عكس عقارب الساعة. هذا هو التعريف الرياضي القياسي للدوران الإيجابي وليس هو نفسه تعريف اللفة الذي تستخدمه أداة استشعار الاتجاه.

وعادة ما يتم دمج ناتج الجيروسكوب مع مرور الوقت لحساب الدوران، وذلك من خلال وصف تغيُّر الزوايا بمرور الوقت. على سبيل المثال:

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 استخدام واجهات برمجة التطبيقات الواردة في هذه الصفحة.

يجب عليك أيضًا قراءة