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

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

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

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

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

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

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

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

سنسور داده های رویداد حسگر توضیحات واحدهای اندازه گیری
TYPE_ACCELEROMETER SensorEvent.values[0] نیروی شتاب در امتداد محور x (شامل گرانش). m/s 2
SensorEvent.values[1] نیروی شتاب در امتداد محور y (از جمله گرانش).
SensorEvent.values[2] نیروی شتاب در امتداد محور z (شامل گرانش).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] شتاب اندازه گیری شده در امتداد محور X بدون هیچ گونه جبران سوگیری. m/s 2
SensorEvent.values[1] شتاب اندازه گیری شده در امتداد محور Y بدون هیچ گونه جبران سوگیری.
SensorEvent.values[2] شتاب اندازه گیری شده در امتداد محور Z بدون هیچ گونه جبران بایاس.
SensorEvent.values[3] شتاب اندازه گیری شده در امتداد محور X با جبران تعصب تخمینی.
SensorEvent.values[4] شتاب اندازه گیری شده در امتداد محور Y با جبران تعصب تخمینی.
SensorEvent.values[5] شتاب اندازه گیری شده در امتداد محور Z با جبران تعصب تخمینی.
TYPE_GRAVITY SensorEvent.values[0] نیروی گرانش در امتداد محور x. m/s 2
SensorEvent.values[1] نیروی گرانش در امتداد محور y.
SensorEvent.values[2] نیروی گرانش در امتداد محور z.
TYPE_GYROSCOPE SensorEvent.values[0] سرعت چرخش حول محور x. راد/ثانیه
SensorEvent.values[1] سرعت چرخش حول محور y.
SensorEvent.values[2] سرعت چرخش حول محور z.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] سرعت چرخش (بدون جبران رانش) حول محور x. راد/ثانیه
SensorEvent.values[1] سرعت چرخش (بدون جبران رانش) حول محور y.
SensorEvent.values[2] نرخ چرخش (بدون جبران رانش) حول محور z.
SensorEvent.values[3] رانش تخمینی حول محور x.
SensorEvent.values[4] رانش تخمینی حول محور y.
SensorEvent.values[5] رانش تخمینی حول محور z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] نیروی شتاب در امتداد محور x (بدون احتساب گرانش). m/s 2
SensorEvent.values[1] نیروی شتاب در امتداد محور y (بدون احتساب گرانش).
SensorEvent.values[2] نیروی شتاب در امتداد محور z (بدون احتساب گرانش).
TYPE_ROTATION_VECTOR SensorEvent.values[0] جزء بردار چرخش در امتداد محور x (x * sin(θ/2)). بدون واحد
SensorEvent.values[1] جزء بردار چرخش در امتداد محور y (y * sin(θ/2)).
SensorEvent.values[2] جزء بردار چرخش در امتداد محور z (z * sin(θ/2)).
SensorEvent.values[3] جزء اسکالر بردار چرخش ((cos(θ/2)). 1
TYPE_SIGNIFICANT_MOTION N/A N/A N/A
TYPE_STEP_COUNTER SensorEvent.values[0] تعداد مراحلی که کاربر از آخرین راه‌اندازی مجدد هنگام فعال شدن سنسور انجام داده است. مراحل
TYPE_STEP_DETECTOR N/A N/A N/A

1 جزء اسکالر یک مقدار اختیاری است.

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

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

پروژه متن باز اندروید (AOSP) سه حسگر حرکتی مبتنی بر نرم افزار را ارائه می دهد: یک سنسور گرانش، یک سنسور شتاب خطی و یک حسگر بردار چرخش. این سنسورها در اندروید 4.0 به روز شدند و اکنون از ژیروسکوپ یک دستگاه (علاوه بر سنسورهای دیگر) برای بهبود پایداری و عملکرد استفاده می کنند. اگر می‌خواهید این حسگرها را امتحان کنید، می‌توانید با استفاده از متد getVendor() و متد getVersion() آنها را شناسایی کنید (فروشنده Google LLC است؛ شماره نسخه 3 است). شناسایی این سنسورها توسط فروشنده و شماره نسخه ضروری است زیرا سیستم اندروید این سه سنسور را حسگرهای ثانویه می داند. به عنوان مثال، اگر یک سازنده دستگاه حسگر گرانشی خود را ارائه دهد، سنسور گرانش 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/s 2 ) و سیستم مختصات همان واحدهایی است که سنسور شتاب استفاده می کند.

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

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

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

کاتلین

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/s 2 ) هستند.

از سنسور بردار چرخش استفاده کنید

بردار چرخش جهت دستگاه را به صورت ترکیبی از یک زاویه و یک محور نشان می دهد که در آن دستگاه از یک زاویه θ حول یک محور (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) و جهت بردار چرخش برابر جهت محور چرخش است.

شکل 1. سیستم مختصات مورد استفاده توسط سنسور بردار چرخش.

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

  • X به عنوان محصول برداری Y x 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 ثانیه) اما دقت بیشتری نسبت به سنسور ردیاب استپ دارد.

توجه: باید مجوز ACTIVITY_RECOGNITION را اعلام کنید تا برنامه شما از این حسگر در دستگاه‌های دارای Android 10 (سطح API 29) یا بالاتر استفاده کند.

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

کاتلین

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 برای بازیابی مقدار فعلی از حسگر گام شمار در یک بازه زمانی خاص استفاده کنید. اگرچه انواع مختلف برنامه‌ها به فواصل زمانی متفاوتی برای خواندن حسگر نیاز دارند، باید این فاصله زمانی را تا جایی که ممکن است طولانی کنید، مگر اینکه برنامه شما به داده‌های بی‌درنگ از سنسور نیاز داشته باشد.

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

حسگر ردیاب گام هر بار که کاربر قدمی برمی دارد، یک رویداد را راه اندازی می کند. انتظار می رود تاخیر کمتر از 2 ثانیه باشد.

توجه: باید مجوز ACTIVITY_RECOGNITION را اعلام کنید تا برنامه شما از این حسگر در دستگاه‌های دارای Android 10 (سطح API 29) یا بالاتر استفاده کند.

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

کاتلین

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

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

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

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

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

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

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

کاتلین

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

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

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

ژیروسکوپ سرعت چرخش را بر حسب راد بر ثانیه حول محور 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);

توجه: اگر برنامه شما Android 12 (سطح 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های پوشش داده شده در این صفحه را بیشتر نشان می دهد.

شما هم باید مطالعه کنید