В статье рассмотрено принципы работы iPhone акселерометра, показаны примеры приложений, использующие акселерометр в качестве главного компонента, наведены рекомендации по использованию акселерометра. Также показано, как использовать акселерометр в веб-приложениях. Часть материалов была взята из статьи «Скроллинг при помощи акселерометра».
Обратимся к Википедии:
Apple — не первая компания, которая внедрила акселерометр в мобильный телефон, но первая, у которой это получилось хорошо.
Перед тем, как перейти к технической части, посмотрим на готовые приложения, которые используют акселерометр.
C помощью iBeer можно попить виртуальное пиво:
iBoobs — программа, которая не прошла отдел цензуры в Apple.
Обзор других программ можно посмотреть по ссылкам внизу.
Датчик ускорения внутри iPhone использует три элемента: кремниевое тело, набор кремниевых пружин и электрический ток. Кремниевые пружины определяют положение кремниевого тела с помощью электрического тока. При повороте iPhone возникает колебание электрического тока, проходящего по кремниевым пружинам. Датчик ускорения фиксирует эти колебания и сообщает iPhone о необходимом изменении картинки на дисплее.
iPhone использует MEMS motion sensor 3-axis — ± 2g/± 8g smart digital output piccolo accelerometer, спецификацию по которому можно загрузить отсюда.
В состоянии покоя, когда устройство не двигается, величина замеряемого ускорения равна силе тяжести и принята за единицу. Соотношения величин проекций этой силы на оси координат дают нам углы поворота устройства в пространстве. Если iPhone находится в движении, то величину ускорения, с которым разгоняется аппарат, можно посредством дополнительных преобразований вычислить на основе значений проекций.
Заметьте, это именно ускорение, а не скорость движения устройства. То есть, если ваш iPhone начнет падать на землю, то проекции величины ускорения примут значение 0 по всем осям — ваш iPhone будет в невесомости :) А если вы поднимаетесь с должным ускорением на лифте вверх, ��о значение силы тяжести на время ускорения увеличится.
Сразу хочу заметить, что координаты X, Y, Z не показывают координаты (положение) устройства в пространстве, как это можно было бы предположить. На самом деле они означают следующее:

Координатные оси iPhone акселерометра
Для случая произвольной ориентации iPhone данные о величине ускорения распределяются по осям согласно проекции вектора ускорения.

Расчет вектора при произвольном положении iPhone
Всю необходимую информацию аккумулирует объект класса UIAcceleration, возвращающий данные по всем осям, а также временной маркер, позволяющий определить относительное время замера указанных
величин. Напрямую подступиться к данным этого класса нельзя, эту информацию можно получить только через делегат UIAccelerometerDelegate, предоставляющего для реализации один единственный метод
accelerometer:didAccelerate:, в который возвращается объект класса UIAcceleration. Назначение делегата и инициализация вызовов метода accelerometer:didAccelerate: происходит при помощи класса UIAccelerometer.
Для того, чтобы подключить акселерометр, необходимо в методе applicationDidFinishLaunching написать следующий код:
Метод setUpdateInterval устанавливать частоту обновления данных, kUpdateFrequency — коэффициент, который показывает, как часто нужно получать данные.
Например, в случае #define kUpdateFrequency 60.0 получим 60 «опросов» в секунду.
Кроме того, в заголовочном файле класса вашего делегата нужно указать протокол UIAccelerometerDelegate:
Логику обработки данных акселерометра нужно добавить в метод didAccelerate в классе делегата:
Замечания:
Настройка акселерометра:
Для остановки получения значений необходимо вызвать следующий код:
Геометрически можно показать работу акселерометра следующим образом:

Из рисунка видно, что угол может быть рассчитан с помощью функции арктангенса, т.е.:
float angle = atan2(y, -x);
Положения акселерометра можно посмотреть на рисунке:

С помощью информации об угле поворота можно менять ориентацию экрана:
Скачать пример приложения можно здесь.
Возьмем за точку отсчета положение нашего устройства в пространстве, при котором угол между задней стенкой и землей составляет 45 градусов. В этом случае проекции силы тяжести на ось Y будет составлять -0.7. Если мы отклоняем аппарат чуть ближе к вертикальному положению, то примем, что при достижении угла в 30 градусов от вертикали мы должны перелистнуть список к концу. И наоборот, при достижении угла в 30 и менее градусов от горизонтального положения, мы должны перелистнуть список к началу.
В первом случае абсолютная величина проекции силы тяжести на ось Y, направленная вдоль аппарата, станет равна 0.86. Те, кто не понял откуда взялось это значение, вспоминаем геометрию и вычисление проекции на ось координат вектора единичной длины. Во втором случае это же значение равно 0.5. Для реализации прокрутки мы воспользуемся методом scrollToRowAtIndexPath:atScrollPosition:animated: класса UITableView.
В основном, используются два фильтра — высокочастотный (high-pass) и низкочастотный (low-pass). Эти фильтры можно использовать для отсеивания эффектов «дрожания»,
медленных поворотов и т.д.
Низкочастотный фильтр используется для нахождения ориентации устройства, высокочастотный — для определения тряски.
Самый простой низкочастотный фильтр реализует следующий код:
Самый простой высокочастотный фильтр реализует следующий код:
Отличная программа для исследования работы акселерометра — показывает изменение значений по трем координатам. Можно скачать с официального сайта Apple.
В обычном режиме отображает именно ту информацию, которую получает. Также можно использовать фильтры, которые будут отсекать обыкновенные повороты, а реагировать лишь на тряску (что, в основном, и бывает нужно в реальных приложениях).

iBells (ссылка на appstore) — это развлекательная программа, которая реагирует на дейсвия пользователя и проигрывает звуки колокольчиков.
Яв��яясь разработчиков этой программы, нам нужно было решить такие задачи:
Корректное реагирование на тряску можно добиться используя высокочастотный фильтр:
kFilteringFactor — коэффициент «заглушения» реакции на случайные колебания. Этот параметр нужно подбирать индивидуально в зависимости от требований.
Интервал измеряется просто:
Переменная lastPlayedTime фиксирует время последнего проигрывания, minTimeDelta — минимальный промежуток, по истечению которого можно проигрывать музыкальный файл.
Недавно я нашел еще один способ определить тряску:
В Safari был добавлен новый метод onorientationchange, который срабатывает при изменении положения на 90%. Ниже приведен javascript код, с помощью которого можно менять положение (orientation) веб-страницы.
Примеры веб-страницы можно посмотреть здесь и здесь.
По понятным причинам тестировать приложения в симуляторе не представляется возможным. Для этого нужно использовать реальное устройство.
По ссылке можно посмотреть (а здесь скачать), как все таки можно передавать данные в симулятор. Но, так как там все равно используется реальное устройство, практической
пользы в этом нет. Just for fun.
P. S. Автор статьи Александр Краковецкий (sashaeve),пожалуйста дайте ему инвайт спасибо Speakus за инвайт.
Что такое акселерометр?
Обратимся к Википедии:
А��селерометр (от лат. accelero — ускоряю и μετρέω — измеряю) — прибор, измеряющий проекцию кажущегося ускорения. Кажущееся ускорение есть равнодействующая сил не гравитационной природы, действующая на массу и отнесённая к величине этой массы. Акселерометр может применяться как для измерения проекций абсолютного линейного ускорения, так и для косвенных измерений проекции гравитационного ускорения. Последнее свойство используется для создания инклинометров. Акселерометры входят в состав инерциальных навигационных систем, где полученные с их помощью измерения интегрируют, получая инерциальную скорость и координаты носителя. Электронные акселерометры часто встраиваются в мобильные устройства (в частности, в телефоны) и применяются в качестве шагомеров, датчиков для определения положения в пространстве, автоматического поворота дисплея и других целей. В игровых приставках акселерометры используются для управления без использования кнопок — путем поворотов в пространстве, встряхиваний и т. д.
Apple — не первая компания, которая внедрила акселерометр в мобильный телефон, но первая, у которой это получилось хорошо.
Как можно использовать акселерометр?
Перед тем, как перейти к технической части, посмотрим на готовые приложения, которые используют акселерометр.
Несколько базовых примеров:
C помощью iBeer можно попить виртуальное пиво:
iBoobs — программа, которая не прошла отдел цензуры в Apple.
Обзор других программ можно посмотреть по ссылкам внизу.
Немного теории
Датчик ускорения внутри iPhone использует три элемента: кремниевое тело, набор кремниевых пружин и электрический ток. Кремниевые пружины определяют положение кремниевого тела с помощью электрического тока. При повороте iPhone возникает колебание электрического тока, проходящего по кремниевым пружинам. Датчик ускорения фиксирует эти колебания и сообщает iPhone о необходимом изменении картинки на дисплее.
iPhone использует MEMS motion sensor 3-axis — ± 2g/± 8g smart digital output piccolo accelerometer, спецификацию по которому можно загрузить отсюда.
В состоянии покоя, когда устройство не двигается, величина замеряемого ускорения равна силе тяжести и принята за единицу. Соотношения величин проекций этой силы на оси координат дают нам углы поворота устройства в пространстве. Если iPhone находится в движении, то величину ускорения, с которым разгоняется аппарат, можно посредством дополнительных преобразований вычислить на основе значений проекций.
Заметьте, это именно ускорение, а не скорость движения устройства. То есть, если ваш iPhone начнет падать на землю, то проекции величины ускорения примут значение 0 по всем осям — ваш iPhone будет в невесомости :) А если вы поднимаетесь с должным ускорением на лифте вверх, ��о значение силы тяжести на время ускорения увеличится.
Интерфейс акселерометра
Несколько фактов:
- является частью UIKit фреймворка
- предоставляет информацию по трем осям
- можно настроить частоту обновления данных (приблизительно 10-100 Гц)
Классы:
- UIAccelerometer
- UIAcceleration
Протокол:
- UIAccelerometerDelegate
Координатные оси
Сразу хочу заметить, что координаты X, Y, Z не показывают координаты (положение) устройства в пространстве, как это можно было бы предположить. На самом деле они означают следующее:
- координата X (Roll) показывает информацию о повороте устройства влево или вправо;
- координата Y (Pitch) дает нам следующую информацию: iPhone находится в вертикальном
положении (-1), лежит в горизонтальной плоскости (0) или находится в
вертикальном положении, только вверх ногами (+1); - координата Z (Face up/face down) показывает, в каком положении находится устройство: лицом вверх (-1,
при нулевых значениях по другим осям), в вертикальном положении (0) или
лицом вниз (+1).

Координатные оси iPhone акселерометра
Для случая произвольной ориентации iPhone данные о величине ускорения распределяются по осям согласно проекции вектора ускорения.

Расчет вектора при произвольном положении iPhone
Подключаем акселерометр в проект
Всю необходимую информацию аккумулирует объект класса UIAcceleration, возвращающий данные по всем осям, а также временной маркер, позволяющий определить относительное время замера указанных
величин. Напрямую подступиться к данным этого класса нельзя, эту информацию можно получить только через делегат UIAccelerometerDelegate, предоставляющего для реализации один единственный метод
accelerometer:didAccelerate:, в который возвращается объект класса UIAcceleration. Назначение делегата и инициализация вызовов метода accelerometer:didAccelerate: происходит при помощи класса UIAccelerometer.
Для того, чтобы подключить акселерометр, необходимо в методе applicationDidFinishLaunching написать следующий код:
[[UIAccelerometer sharedAccelerometer] setUpdateInterval: 1.0 / kUpdateFrequency]; [[UIAccelerometer sharedAccelerometer] setDelegate:self];
Метод setUpdateInterval устанавливать частоту обновления данных, kUpdateFrequency — коэффициент, который показывает, как часто нужно получать данные.
Например, в случае #define kUpdateFrequency 60.0 получим 60 «опросов» в секунду.
Кроме того, в заголовочном файле класса вашего делегата нужно указать протокол UIAccelerometerDelegate:
@interface AppDelegate : NSObject<UIApplicationDelegate> @interface accelerometerAppDelegate : NSObject <UIApplicationDelegate, UIAccelerometerDelegate>
Логику обработки данных акселерометра нужно добавить в метод didAccelerate в классе делегата:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// Get the event data
UIAccelerometValue x, y, z;
x = acceleration.x;
y = acceleration.y;
z = acceleration.z;
}Замечания:
- можно объявлять лишь один делегат для приложения
- данные приходят асинхронно к основному потоку
Настройка акселерометра:
- Диапазон частоты — 10 -100 Гц.
- Рекомендуемая частота для игр: 30-60 Гц, для определения ориентации — 10-30 Гц.
Для остановки получения значений необходимо вызвать следующий код:
- (void) disableAccelerometerEvents
{
UIAccelerometer * acc = [UIAccelerometer sharedAccelerometer];
acc.delegate = nil;
}Угол наклона
Геометрически можно показать работу акселерометра следующим образом:

Из рисунка видно, что угол может быть рассчитан с помощью функции арктангенса, т.е.:
float angle = atan2(y, -x);
Положения акселерометра можно посмотреть на рисунке:

С помощью информации об угле поворота можно менять ориентацию экрана:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// Get the current device angle
float xx = -[acceleration x];
float yy = [acceleration y];
float angle = atan2(yy, xx);
// Add 1.5 to the angle to keep the label constantly horizontal to the viewer.
[interfaceOrientationLabel setTransform:CGAffineTransformMakeRotation(angle+1.5)];
// Read my blog for more details on the angles. It should be obvious that you
// could fire a custom shouldAutorotateToInterfaceOrientation-event here.
if(angle >= -2.25 && angle <= -0.75)
{
if(deviceOrientation != UIInterfaceOrientationPortrait)
{
deviceOrientation = UIInterfaceOrientationPortrait;
[interfaceOrientationLabel setText:@"UIInterfaceOrientationPortrait"];
}
}
else if(angle >= -0.75 && angle <= 0.75)
{
if(deviceOrientation != UIInterfaceOrientationLandscapeRight)
{
deviceOrientation = UIInterfaceOrientationLandscapeRight;
[interfaceOrientationLabel setText:@"UIInterfaceOrientationLandscapeRight"];
}
}
else if(angle >= 0.75 && angle <= 2.25)
{
if(deviceOrientation != UIInterfaceOrientationPortraitUpsideDown)
{
deviceOrientation = UIInterfaceOrientationPortraitUpsideDown;
[interfaceOrientationLabel setText:@"UIInterfaceOrientationPortraitUpsideDown"];
}
}
else if(angle <= -2.25 || angle >= 2.25)
{
if(deviceOrientation != UIInterfaceOrientationLandscapeLeft)
{
deviceOrientation = UIInterfaceOrientationLandscapeLeft;
[interfaceOrientationLabel setText:@"UIInterfaceOrientationLandscapeLeft"];
}
}
}Скачать пример приложения можно здесь.
Пример 1. Прокрутка с помощью акселерометра
Возьмем за точку отсчета положение нашего устройства в пространстве, при котором угол между задней стенкой и землей составляет 45 градусов. В этом случае проекции силы тяжести на ось Y будет составлять -0.7. Если мы отклоняем аппарат чуть ближе к вертикальному положению, то примем, что при достижении угла в 30 градусов от вертикали мы должны перелистнуть список к концу. И наоборот, при достижении угла в 30 и менее градусов от горизонтального положения, мы должны перелистнуть список к началу.
В первом случае абсолютная величина проекции силы тяжести на ось Y, направленная вдоль аппарата, станет равна 0.86. Те, кто не понял откуда взялось это значение, вспоминаем геометрию и вычисление проекции на ось координат вектора единичной длины. Во втором случае это же значение равно 0.5. Для реализации прокрутки мы воспользуемся методом scrollToRowAtIndexPath:atScrollPosition:animated: класса UITableView.
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
double absY = fabs(acceleration.y);
if (absY <= 0.5) {
// Прокрутка к началу списка
[viewController.tableView
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
else if (absY >= 0.86)
{
// Прокрутка к концу списка
[viewController.tableView
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([list count] — 1) inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
}Фильтры
В основном, используются два фильтра — высокочастотный (high-pass) и низкочастотный (low-pass). Эти фильтры можно использовать для отсеивания эффектов «дрожания»,
медленных поворотов и т.д.
Низкочастотный фильтр используется для нахождения ориентации устройства, высокочастотный — для определения тряски.
Самый простой низкочастотный фильтр реализует следующий код:
#define FILTERFACTOR 0.1 value = (newAcceleration * FILTERFACTOR) + (previousValue * (1.0 - FILTERFACTOR)); previousValue = value;
Самый простой высокочастотный фильтр реализует следующий код:
#define FILTERFACTOR 0.1 value = newAcceleration - (newAcceleration * FILTERFACTOR) + (previousValue * (1.0 - FILTERFACTOR)); previousValue = value;
Пример 2. AccelerometrGraph
Отличная программа для исследования работы акселерометра — показывает изменение значений по трем координатам. Можно скачать с официального сайта Apple.
В обычном режиме отображает именно ту информацию, которую получает. Также можно использовать фильтры, которые будут отсекать обыкновенные повороты, а реагировать лишь на тряску (что, в основном, и бывает нужно в реальных приложениях).
Пример 3. iBells
iBells (ссылка на appstore) — это развлекательная программа, которая реагирует на дейсвия пользователя и проигрывает звуки колокольчиков.
Яв��яясь разработчиков этой программы, нам нужно было решить такие задачи:
- корректное реагирование на действия пользователей, т.е. именно в тот момент когда пользователь трясет устройства;
- реализация минимальной задержки, в течении которой музыкальный файл не должен проигрываться (это нужно для того, чтобы исключить очень частые срабатывания).
Корректное реагирование на тряску можно добиться используя высокочастотный фильтр:
- (void)accelerateWithX:(float)x Y:(float)y Z:(float)z
{
// Use double filtering for passed coords
acceleration[0] = x * kFilteringFactor + acceleration[0] * (1.0 - kFilteringFactor);
x = x - acceleration[0];
acceleration[1] = y * kFilteringFactor + acceleration[1] * (1.0 - kFilteringFactor);
y = y - acceleration[1];
acceleration[2] = z * kFilteringFactor + acceleration[2] * (1.0 - kFilteringFactor);
z = z - acceleration[2];
}kFilteringFactor — коэффициент «заглушения» реакции на случайные колебания. Этот параметр нужно подбирать индивидуально в зависимости от требований.
Интервал измеряется просто:
NSDate *now = [NSDate date];
NSTimeInterval interval = [now timeIntervalSinceDate:self.lastPlayedTime];
// Check playback time condition
if (interval > minTimeDelta)
{
[self play];
}Переменная lastPlayedTime фиксирует время последнего проигрывания, minTimeDelta — минимальный промежуток, по истечению которого можно проигрывать музыкальный файл.
Недавно я нашел еще один способ определить тряску:
// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
@interface L0AppDelegate : NSObject {
BOOL histeresisExcited;
UIAcceleration* lastAcceleration;
}
@property(retain) UIAcceleration* lastAcceleration;
@end
@implementation L0AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[UIAccelerometer sharedAccelerometer].delegate = self;
}
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
/* SHAKE DETECTED. DO HERE WHAT YOU WANT. */
} else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
// and proper @synthesize and -dealloc boilerplate code
@endИспользование акселерометра в веб-приложениях
В Safari был добавлен новый метод onorientationchange, который срабатывает при изменении положения на 90%. Ниже приведен javascript код, с помощью которого можно менять положение (orientation) веб-страницы.
function updateOrientation() {
/*
window.orientation returns a value that indicates whether iPhone is in portrait mode,
landscape mode with the screen turned to the left, or landscape mode
with the screen turned to the right.
*/
var orientation = window.orientation;
switch(orientation)
{
case 0:
/*
If in portrait mode, sets the body's class attribute to portrait.
Consequently, all style definitions matching the body[class="portrait"]
declaration in the iPhoneOrientation.css file will be selected and used
to style "Handling iPhone or iPod touch Orientation Events".
*/
document.body.setAttribute("class","portrait");
/*
Add a descriptive message on "Handling iPhone or iPod touch Orientation Events"
*/
document.getElementById("currentOrientation").innerHTML
= "Now in portrait orientation (Home button on the bottom).";
break;
case 90:
/*
If in landscape mode with the screen turned to the left,
sets the body's class attribute to landscapeLeft.
In this case, all style definitions matching the body[class="landscapeLeft"]
declaration in the iPhoneOrientation.css file will be
selected and used to style "Handling iPhone or iPod touch Orientation Events".
*/
document.body.setAttribute("class","landscapeLeft");
document.getElementById("currentOrientation").innerHTML
= "Now in landscape orientation and turned to the left (Home button to the right).";
break;
case -90:
/*
If in landscape mode with the screen turned to the right,
sets the body's class attribute to landscapeRight.
Here, all style definitions matching the body[class="landscapeRight"]
declaration in the iPhoneOrientation.css file will be selected and used
to style "Handling iPhone or iPod touch Orientation Events".
*/
document.body.setAttribute("class","landscapeRight");
break;
}
window.onorientationchange = updateOrientation;
<div id="currentOrientation" style="font-size: 40px;">
Now in portrait orientation (Home button on the bottom).</div>Примеры веб-страницы можно посмотреть здесь и здесь.
Акселерометр и симулятор
По понятным причинам тестировать приложения в симуляторе не представляется возможным. Для этого нужно использовать реальное устройство.
По ссылке можно посмотреть (а здесь скачать), как все таки можно передавать данные в симулятор. Но, так как там все равно используется реальное устройство, практической
пользы в этом нет. Just for fun.
Ссылки и дополнительные материалы
- 100+ iPhone Games That Use the Accelerometer
- Top 12 iPhone Accelerometer Apps
- 10 Creative Ways to Use the Accelerometer on the iPhone
- Управление без навигационных клавиш?! Или iPhone Accelerometer
- iPhone Coding: Using the Accelerometer
- UIAcceleration Class Reference
- UIAccelerometer Class Reference
- UIDevice Class Reference
- AccelerometerGraph
- BubbleLevel
- iBells на AppStore
- Сообщество iPhone разработчиков в Украине
- Сообщество пользователей и разработчиков для iPhone в Украине
P. S. Автор статьи Александр Краковецкий (sashaeve),
