سنسورهای حرکت

پلتفرم اندروید چندین حسگر ارائه می‌دهد که به شما امکان می‌دهد حرکت دستگاه را رصد کنید.

معماری‌های احتمالی حسگرها بر اساس نوع حسگر متفاوت است:

  • حسگرهای گرانش، شتاب خطی، بردار چرخش، حرکت قابل توجه، شمارنده گام و آشکارساز گام یا مبتنی بر سخت‌افزار هستند یا مبتنی بر نرم‌افزار.
  • حسگرهای شتاب‌سنج و ژیروسکوپ همیشه مبتنی بر سخت‌افزار هستند.

بیشتر دستگاه‌های اندروید دارای شتاب‌سنج هستند و بسیاری از آنها اکنون دارای ژیروسکوپ نیز می‌باشند. در دسترس بودن حسگرهای نرم‌افزاری متغیرتر است زیرا آنها اغلب برای استخراج داده‌های خود به یک یا چند حسگر سخت‌افزاری متکی هستند. بسته به دستگاه، این حسگرهای نرم‌افزاری می‌توانند داده‌های خود را یا از شتاب‌سنج و مغناطیس‌سنج یا از ژیروسکوپ استخراج کنند.

حسگرهای حرکتی برای نظارت بر حرکت دستگاه، مانند کج شدن، لرزش، چرخش یا تاب خوردن، مفید هستند. این حرکت معمولاً بازتابی از ورودی مستقیم کاربر است (به عنوان مثال، کاربری که در یک بازی ماشین را هدایت می‌کند یا کاربری که در یک بازی توپ را کنترل می‌کند)، اما می‌تواند بازتابی از محیط فیزیکی که دستگاه در آن قرار دارد نیز باشد (به عنوان مثال، حرکت با شما هنگام رانندگی). در حالت اول، شما حرکت را نسبت به چارچوب مرجع دستگاه یا چارچوب مرجع برنامه خود نظارت می‌کنید؛ در حالت دوم، شما حرکت را نسبت به چارچوب مرجع جهان نظارت می‌کنید. حسگرهای حرکتی به خودی خود معمولاً برای نظارت بر موقعیت دستگاه استفاده نمی‌شوند، اما می‌توانند با حسگرهای دیگر، مانند حسگر میدان ژئومغناطیسی، برای تعیین موقعیت دستگاه نسبت به چارچوب مرجع جهان استفاده شوند (برای اطلاعات بیشتر به حسگرهای موقعیت مراجعه کنید).

تمام حسگرهای حرکتی، آرایه‌های چندبعدی از مقادیر حسگر را برای هر SensorEvent برمی‌گردانند. برای مثال، در طول یک رویداد حسگر واحد، شتاب‌سنج داده‌های نیروی شتاب را برای سه محور مختصات برمی‌گرداند و ژیروسکوپ داده‌های سرعت چرخش را برای سه محور مختصات برمی‌گرداند. این مقادیر داده در یک آرایه float ( values ) به همراه سایر پارامترهای SensorEvent بازگردانده می‌شوند. جدول 1 خلاصه‌ای از حسگرهای حرکتی موجود در پلتفرم اندروید را نشان می‌دهد.

جدول 1. حسگرهای حرکتی که در پلتفرم اندروید پشتیبانی می‌شوند.

سنسور داده‌های رویداد حسگر توضیحات واحدهای اندازه‌گیری
TYPE_ACCELEROMETER SensorEvent.values[0] نیروی شتاب در امتداد محور x (شامل گرانش). متر بر ثانیه ۲
SensorEvent.values[1] نیروی شتاب در امتداد محور y (شامل گرانش).
SensorEvent.values[2] نیروی شتاب در امتداد محور z (شامل گرانش).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] شتاب اندازه‌گیری شده در امتداد محور X بدون هیچگونه جبران بایاس. متر بر ثانیه ۲
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. متر بر ثانیه ۲
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] نرخ چرخش (بدون جبران رانش) حول محور y.
SensorEvent.values[2] نرخ چرخش (بدون جبران رانش) حول محور z.
SensorEvent.values[3] رانش تخمینی حول محور x.
SensorEvent.values[4] رانش تخمینی حول محور y.
SensorEvent.values[5] رانش تخمینی حول محور z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] نیروی شتاب در امتداد محور x (به استثنای گرانش). متر بر ثانیه ۲
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 ناموجود ناموجود ناموجود

۱- مؤلفه اسکالر یک مقدار اختیاری است.

حسگر بردار چرخش و حسگر گرانش، رایج‌ترین حسگرهای مورد استفاده برای تشخیص و نظارت بر حرکت هستند. حسگر بردار چرخش به طور خاص همه‌کاره است و می‌تواند برای طیف گسترده‌ای از وظایف مرتبط با حرکت، مانند تشخیص حرکات، نظارت بر تغییر زاویه‌ای و نظارت بر تغییرات جهت‌گیری نسبی، مورد استفاده قرار گیرد. به عنوان مثال، اگر در حال توسعه یک بازی، یک برنامه واقعیت افزوده، یک قطب‌نمای دو بعدی یا سه‌بعدی یا یک برنامه تثبیت دوربین هستید، حسگر بردار چرخش ایده‌آل است. در بیشتر موارد، استفاده از این حسگرها انتخاب بهتری نسبت به استفاده از شتاب‌سنج و حسگر میدان ژئومغناطیسی یا حسگر جهت‌یابی است.

حسگرهای پروژه متن‌باز اندروید

پروژه متن‌باز اندروید (AOSP) سه حسگر حرکتی مبتنی بر نرم‌افزار ارائه می‌دهد: یک حسگر گرانش، یک حسگر شتاب خطی و یک حسگر بردار چرخش. این حسگرها در اندروید ۴.۰ به‌روزرسانی شدند و اکنون از ژیروسکوپ دستگاه (علاوه بر حسگرهای دیگر) برای بهبود پایداری و عملکرد استفاده می‌کنند. اگر می‌خواهید این حسگرها را امتحان کنید، می‌توانید آنها را با استفاده از متد getVendor() و متد getVersion() شناسایی کنید (فروشنده Google LLC است؛ شماره نسخه ۳ است). شناسایی این حسگرها بر اساس فروشنده و شماره نسخه ضروری است زیرا سیستم اندروید این سه حسگر را حسگرهای ثانویه می‌داند. به عنوان مثال، اگر سازنده دستگاه حسگر گرانش خود را ارائه دهد، حسگر گرانش AOSP به عنوان یک حسگر گرانش ثانویه نمایش داده می‌شود. هر سه این حسگرها به ژیروسکوپ متکی هستند: اگر دستگاهی ژیروسکوپ نداشته باشد، این حسگرها نمایش داده نمی‌شوند و برای استفاده در دسترس نیستند.

استفاده از حسگر جاذبه

حسگر گرانش یک بردار سه‌بعدی ارائه می‌دهد که جهت و بزرگی گرانش را نشان می‌دهد. معمولاً از این حسگر برای تعیین جهت نسبی دستگاه در فضا استفاده می‌شود. کد زیر نحوه دریافت نمونه‌ای از حسگر گرانش پیش‌فرض را به شما نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

واحدها همان واحدهای مورد استفاده توسط حسگر شتاب (m/ ) هستند و سیستم مختصات همان سیستم مورد استفاده توسط حسگر شتاب است.

نکته: وقتی دستگاه در حالت سکون است، خروجی حسگر گرانش باید با خروجی شتاب‌سنج یکسان باشد.

استفاده از شتاب‌سنج خطی

حسگر شتاب خطی، یک بردار سه‌بعدی را در اختیار شما قرار می‌دهد که نشان‌دهنده شتاب در امتداد هر محور دستگاه، به استثنای گرانش، است. می‌توانید از این مقدار برای انجام تشخیص حرکت استفاده کنید. این مقدار همچنین می‌تواند به عنوان ورودی برای یک سیستم ناوبری اینرسی که از محاسبه‌ی کور استفاده می‌کند، عمل کند. کد زیر نحوه‌ی دریافت نمونه‌ای از حسگر شتاب خطی پیش‌فرض را به شما نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

جاوا

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

شما معمولاً از این سنسور زمانی استفاده می‌کنید که می‌خواهید داده‌های شتاب را بدون تأثیر جاذبه به دست آورید. به عنوان مثال، می‌توانید از این سنسور برای دیدن سرعت ماشین خود استفاده کنید. سنسور شتاب خطی همیشه یک انحراف دارد که باید آن را حذف کنید. ساده‌ترین راه برای انجام این کار، ایجاد یک مرحله کالیبراسیون در برنامه شماست. در طول کالیبراسیون می‌توانید از کاربر بخواهید که دستگاه را روی میز قرار دهد و سپس انحرافات را برای هر سه محور بخوانید. سپس می‌توانید آن انحراف را از قرائت‌های مستقیم سنسور شتاب کم کنید تا شتاب خطی واقعی را به دست آورید.

سیستم مختصات سنسور همان سیستمی است که توسط سنسور شتاب استفاده می‌شود، همانطور که واحدهای اندازه‌گیری (m/ ) نیز همینطور هستند.

استفاده از حسگر بردار چرخش

بردار چرخش، جهت‌گیری دستگاه را به صورت ترکیبی از یک زاویه و یک محور نشان می‌دهد که در آن دستگاه به اندازه زاویه θ حول یک محور (x، y یا z) چرخیده است. کد زیر نحوه دریافت نمونه‌ای از حسگر بردار چرخش پیش‌فرض را نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

جاوا

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) و جهت بردار چرخش برابر با جهت محور چرخش است.

شکل ۱. سیستم مختصات مورد استفاده توسط حسگر بردار چرخش.

سه عنصر بردار چرخش برابر با سه مؤلفه آخر یک کواترنیون واحد هستند (cos(θ/2)، x*sin(θ/2)، y*sin(θ/2)، z*sin(θ/2)). عناصر بردار چرخش بدون واحد هستند. محورهای x، y و z به همان روشی که حسگر شتاب تعریف می‌شود، تعریف می‌شوند. سیستم مختصات مرجع به صورت یک مبنای متعامد مستقیم تعریف می‌شود (شکل 1 را ببینید). این سیستم مختصات دارای ویژگی‌های زیر است:

  • X به عنوان حاصلضرب برداری Y در Z تعریف می‌شود. این نقطه در موقعیت فعلی دستگاه، مماس بر زمین است و تقریباً به سمت شرق اشاره دارد.
  • Y در موقعیت فعلی دستگاه، مماس بر زمین است و به سمت قطب شمال ژئومغناطیسی اشاره دارد.
  • Z به سمت آسمان اشاره می‌کند و عمود بر صفحه زمین است.

برای یک برنامه نمونه که نحوه استفاده از حسگر بردار چرخش را نشان می‌دهد، به RotationVectorDemo.java مراجعه کنید.

از حسگر حرکتی قابل توجه استفاده کنید

حسگر حرکت قابل توجه هر بار که حرکت قابل توجهی شناسایی شود، یک رویداد را فعال می‌کند و سپس خود را غیرفعال می‌کند. حرکت قابل توجه، حرکتی است که ممکن است منجر به تغییر مکان کاربر شود؛ به عنوان مثال، پیاده‌روی، دوچرخه‌سواری یا نشستن در ماشین در حال حرکت. کد زیر به شما نشان می‌دهد که چگونه یک نمونه از حسگر حرکت قابل توجه پیش‌فرض دریافت کنید و چگونه یک شنونده رویداد ثبت کنید:

کاتلین

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

جاوا

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 ثانیه) دارد اما دقت آن از حسگر آشکارساز گام‌شمار بیشتر است.

توجه: برای اینکه برنامه شما بتواند از این حسگر در دستگاه‌های دارای اندروید ۱۰ (سطح API ۲۹) یا بالاتر استفاده کند، باید مجوز ACTIVITY_RECOGNITION را اعلام کنید.

کد زیر نحوه دریافت نمونه‌ای از سنسور پیش فرض شمارنده گام را نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

برای حفظ باتری در دستگاه‌هایی که برنامه شما را اجرا می‌کنند، باید از کلاس JobScheduler برای بازیابی مقدار فعلی از سنسور گام‌شمار در یک بازه زمانی خاص استفاده کنید. اگرچه انواع مختلف برنامه‌ها به فواصل زمانی مختلف خواندن سنسور نیاز دارند، اما باید این فاصله زمانی را تا حد امکان طولانی کنید، مگر اینکه برنامه شما به داده‌های بلادرنگ از سنسور نیاز داشته باشد.

از سنسور تشخیص گام استفاده کنید

حسگر تشخیص گام، هر بار که کاربر قدم برمی‌دارد، رویدادی را فعال می‌کند. انتظار می‌رود تأخیر کمتر از ۲ ثانیه باشد.

توجه: برای اینکه برنامه شما بتواند از این حسگر در دستگاه‌های دارای اندروید ۱۰ (سطح API ۲۹) یا بالاتر استفاده کند، باید مجوز ACTIVITY_RECOGNITION را اعلام کنید.

کد زیر نحوه دریافت نمونه‌ای از حسگر تشخیص گام پیش‌فرض را نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

کار با داده‌های خام

حسگرهای زیر داده‌های خام مربوط به نیروهای خطی و چرخشی اعمال شده به دستگاه را در اختیار برنامه شما قرار می‌دهند. برای استفاده مؤثر از مقادیر این حسگرها، باید عواملی مانند جاذبه را از محیط فیلتر کنید. همچنین ممکن است لازم باشد یک الگوریتم هموارسازی را برای کاهش نویز بر روند مقادیر اعمال کنید.

استفاده از شتاب‌سنج

یک حسگر شتاب، شتاب اعمال شده به دستگاه، از جمله نیروی جاذبه را اندازه‌گیری می‌کند. کد زیر نحوه دریافت نمونه‌ای از حسگر شتاب پیش‌فرض را نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

توجه: اگر برنامه شما اندروید ۱۲ (سطح API 31) یا بالاتر را هدف قرار می‌دهد، این حسگر محدودیت سرعت دارد.

از نظر مفهومی، یک حسگر شتاب، شتاب اعمال شده به یک دستگاه (A d ) را با اندازه‌گیری نیروهایی که به خود حسگر (F s ) اعمال می‌شوند، با استفاده از رابطه زیر تعیین می‌کند:

A_D=-(1/جرم)∑F_S

با این حال، نیروی گرانش همیشه بر اساس رابطه زیر بر شتاب اندازه‌گیری شده تأثیر می‌گذارد:

A_D=-g-(1/جرم)∑F_S

به همین دلیل، وقتی دستگاه روی میز قرار دارد (و شتاب نمی‌گیرد)، شتاب‌سنج بزرگی g = 9.81 m/ را نشان می‌دهد. به طور مشابه، وقتی دستگاه در حال سقوط آزاد است و بنابراین با سرعت 9.81 m/ به سمت زمین شتاب می‌گیرد، شتاب‌سنج آن بزرگی g = 0 m/ را نشان می‌دهد. بنابراین، برای اندازه‌گیری شتاب واقعی دستگاه، سهم نیروی گرانش باید از داده‌های شتاب‌سنج حذف شود. این کار را می‌توان با اعمال یک فیلتر بالاگذر انجام داد. برعکس، می‌توان از یک فیلتر پایین‌گذر برای جداسازی نیروی گرانش استفاده کرد. مثال زیر نشان می‌دهد که چگونه می‌توانید این کار را انجام دهید:

کاتلین

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]
}

جاوا

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/ به سمت آسمان هل دهید، مقدار شتاب z برابر با A + 9.81 خواهد بود که مربوط به شتاب دستگاه (+A m/ ) منهای نیروی گرانش (-9.81 m/ ) است.
  • دستگاه ثابت دارای شتابی برابر با +9.81 خواهد بود که مربوط به شتاب دستگاه (0 متر بر ثانیه منهای نیروی جاذبه که -9.81 متر بر ثانیه است) است.

به طور کلی، اگر می‌خواهید حرکت دستگاه را زیر نظر داشته باشید، شتاب‌سنج حسگر خوبی برای استفاده است. تقریباً هر گوشی و تبلت اندرویدی دارای شتاب‌سنج است و حدود ۱۰ برابر کمتر از سایر حسگرهای حرکتی برق مصرف می‌کند. یکی از معایب آن این است که ممکن است مجبور شوید فیلترهای پایین‌گذر و بالاگذر را برای حذف نیروهای گرانشی و کاهش نویز پیاده‌سازی کنید.

استفاده از ژیروسکوپ

ژیروسکوپ سرعت چرخش را بر حسب رادیان بر ثانیه حول محورهای x، y و z دستگاه اندازه‌گیری می‌کند. کد زیر نحوه دریافت نمونه‌ای از ژیروسکوپ پیش‌فرض را به شما نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

توجه: اگر برنامه شما اندروید ۱۲ (سطح API 31) یا بالاتر را هدف قرار می‌دهد، این حسگر محدودیت سرعت دارد.

سیستم مختصات این حسگر همان سیستمی است که برای حسگر شتاب استفاده می‌شود. چرخش در جهت خلاف عقربه‌های ساعت مثبت است؛ یعنی، ناظری که از یک مکان مثبت روی محورهای x، y یا z به دستگاهی که در مبدا مختصات قرار دارد نگاه می‌کند، اگر دستگاه در جهت خلاف عقربه‌های ساعت بچرخد، چرخش مثبت را گزارش خواهد کرد. این تعریف ریاضی استاندارد چرخش مثبت است و با تعریف غلتشی که توسط حسگر جهت‌یابی استفاده می‌شود، یکسان نیست.

معمولاً خروجی ژیروسکوپ با گذشت زمان انتگرال‌گیری می‌شود تا چرخشی که تغییر زاویه‌ها را در طول گام زمانی توصیف می‌کند، محاسبه شود. برای مثال:

کاتلین

// 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;
}

جاوا

// 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

توجه: حسگرهای کالیبره نشده نتایج خام بیشتری ارائه می‌دهند و ممکن است شامل مقداری بایاس باشند، اما اندازه‌گیری‌های آنها شامل پرش‌های کمتری از اصلاحات اعمال شده از طریق کالیبراسیون است. برخی از برنامه‌ها ممکن است این نتایج کالیبره نشده را به عنوان نتایج روان‌تر و قابل اعتمادتر ترجیح دهند. به عنوان مثال، اگر یک برنامه در تلاش است تا ادغام حسگر خود را انجام دهد، اعمال کالیبراسیون می‌تواند در واقع نتایج را تحریف کند.

علاوه بر نرخ چرخش، ژیروسکوپ کالیبره نشده، میزان رانش تخمینی حول هر محور را نیز ارائه می‌دهد. کد زیر نحوه دریافت نمونه‌ای از ژیروسکوپ کالیبره نشده پیش‌فرض را نشان می‌دهد:

کاتلین

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

جاوا

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

نمونه‌های کد اضافی

نمونه BatchStepSensor کاربرد APIهای مطرح‌شده در این صفحه را بیشتر نشان می‌دهد.

شما هم باید بخوانید