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

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

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

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

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

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

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

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

أداة استشعار بيانات أحداث أجهزة الاستشعار الوصف وحدات القياس
TYPE_ACCELEROMETER SensorEvent.values[0] قوة التسارع على طول محور x (بما في ذلك الجاذبية) m/s2
SensorEvent.values[1] قوة التسارع على طول محور y (بما في ذلك الجاذبية)
SensorEvent.values[2] قوة التسارع على طول محور z (بما في ذلك الجاذبية)
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] التسارع المقاس على طول محور X بدون أي تعويض عن الانحياز m/s2
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 m/s2
SensorEvent.values[1] قوة الجاذبية على طول محور y
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] معدل الدوران (بدون تعويض الانحراف) حول المحور الصادي
SensorEvent.values[2] معدل الدوران (بدون تعويض الانحراف) حول محور z
SensorEvent.values[3] الانحراف المقدَّر حول محور x
SensorEvent.values[4] الانحراف المقدَّر حول المحور الصادي
SensorEvent.values[5] الانحراف المقدَّر حول محور z
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] قوة التسارع على طول محور x (باستثناء الجاذبية) m/s2
SensorEvent.values[1] قوة التسارع على طول محور y (باستثناء الجاذبية)
SensorEvent.values[2] قوة التسارع على طول محور z (باستثناء الجاذبية)
TYPE_ROTATION_VECTOR SensorEvent.values[0] مكوّن متجه الدوران على محور x (x * sin(θ/2)) بدون وحدة
SensorEvent.values[1] مكوّن متجّه الالتفاف على طول محور ص (ص * 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) ثلاثة مستشعرات للحركة مستندة إلى البرامج: جهاز استشعار الجاذبية ومستشعر تسارع خطّي ومستشعر لمتجه الدوران. وقد تم تحديث أدوات الاستشعار هذه في 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).

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

يمثل متجه الدوران اتجاه الجهاز كمزيج من زاوية ومحور، حيث تم تدوير الجهاز بزاوية LIVE حول محور (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(hi/2), y*sin(reset/2), z*sin(المستهدفة/2)

حيث يكون مقدار متجه الدوران مساويًا لدالة sin(θ/2)، ويكون اتجاه متجه الدوران مساويًا لاتجاه محور الدوران.

الشكل 1: نظام الإحداثيات المستخدَم من خلال أداة استشعار متجه الدوران

العناصر الثلاثة لمتجه الدوران تساوي المكونات الثلاثة الأخيرة للوحدة الرباعية (cos(main/2) وx*sin(reset/2), y*sin(الهدف/2), z*sin(subtitle/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) باستخدام العلاقة التالية:

A_D=-(1/mass)∑F_S

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

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

لهذا السبب، عندما يكون الجهاز على طاولة (وليس في وضع تسارع)، يشير مقياس التسارع إلى المقدار 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;
}

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

استخدام الجيروسكوب غير المعاد معايرة

يشبه الجيروسكوب غير المعادَل الجيروسكوب، باستثناء أنّه لا يتم تطبيق أي تعويض لانحراف الجيروسكوب على معدّل الدوران. لا تزال عملية المعايرة التي تم إجراؤها في المصنع وعملية تعويض درجة الحرارة سارية على معدّل الدوران. يكون الجيرسكوب الذي لم يتم ضبطه مفيداً لإجراء المعالجة اللاحقة ودمج بيانات التوجيه. بشكل عام، سيكون 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 بشكلٍ أكبر استخدام واجهات برمجة التطبيقات التي تتناولها هذه الصفحة.

ننصحك أيضًا بقراءة