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

يوفّر نظام 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] قوة الجاذبية على طول محور ص
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) ثلاثة أدوات استشعار للحركة تستند إلى البرامج: أداة استشعار القوة المغناطيسية ، وأداة استشعار التسارع الخطي، وأداة استشعار متّجه الدوران. تم تعديل هذه الحساسات في الإصدار 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);

الوحدات هي نفسها المستخدمة في قياس التسارع (m/s2)، ونظام الإحداثيات هو نفسه المستخدَم في قياس التسارع.

ملاحظة: عندما يكون الجهاز في وضع السكون، يجب أن يكون ناتج جهاز استشعار الجاذبية مطابقًا لناتج مقياس التسارع.

استخدام مقياس التسارع الخطي

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

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

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

إنّ نظام تحديد المواقع في أداة الاستشعار هو نفسه النظام المستخدَم في أداة استشعار التسارع، وكذلك وحدات القياس (m/s2).

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

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

حيث يكون مقدار متجه الدوران مساويًا لدالة 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) باستخدام العلاقة التالية:

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

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