iBeacon. Мифы и реальность
(картинка с сайта developer.apple.com)
Что это такое?
В середине 2013 года Apple на конференции для разработчиков внезапно рассказала, что они приготовили новую технологию, предназначенную для навигации внутри помещений, что они начали создавать карты музеев, торговых центров и других интересных мест и вообще, всё круто. Поверив на слово крупной компании, многие стали предлагать «решения» по навигации внутри помещений, но мало у кого получилось что-то работоспособное. Оказалось, что в реальности применять эту технологию достаточно непросто.
Я также принял активное участие в исследовании технологии. Удалось развернуть сеть биконов на мероприятиях GeekPicnic в Москве и в Санкт-Петербурге, протестировав возможности технологии. После чего я написал библиотеку, которая, используя небольшое количество маяков, достаточно хорошо позволяет определять местоположение внутри помещений.
В статье я коротко опишу, что такое iBeacon, какие задачи мне пришлось с этой технологией решать, что удалось, что не очень.
Что же такое iBeacon? Это протокол-подмножество Bluetooth Low Energy, который позволяет узнать:
- UUID, Major, Minor для маячка
- силу сигнала от маячка
Выглядит это так:
fb0b57a2-8228-44 cd-913a-94a122ba1206 Major 1 Minor 2
Использовать iBeacon’ы можно на Айфонах, начиная с 4S, Айпадах, начиная с третьего поколения, iPad Mini, iPod Touch (с пятого поколения), поддержку Андроидов нужно искать в конкретных устройствах, а версия ОС должна быть 4.3 или выше. Также можно использовать компьютеры Macintosh.
Опыт реального использования
Когда мы рассматривали возможные использования технологии, получалось очень красиво:
- навигация,
- отслеживание перемещений (товаров, сотрудников),
- контрольно-пропускные действия,
- реклама.
На практике оказалось, что всё не так просто.
Главное разочарование в том, что навигация получается крайне неточная. В следующем разделе я покажу, как можно сделать адекватную навигацию на биконах, но вообще биконы плохо предназначены для навигации.
Вообще, основной алгоритм работы с биконами — когда пользовательское устройство приближается к нему, появляется нотификация. Тут же хочется ловить все биконы подряд. Но, к сожалению, чтобы нотификацию обработать, нужно написать соответствующее приложение и занести в него параметры конкретного бикона (или конкретного типа биконов, для этого обычно используется UUID, которые у всех нужных биконов одинаковый). То есть нельзя просто так взять и повесить, например, рекламный бикон, чтобы он висел и пиликал всем проходящим. Нужно заставить поставить приложение, которое слушает только те биконы, на которые настроено (настроить на все-все-все тоже нельзя, так как startMonitoringForRegion не даст добавить бесконечное количество регионов):
NSString *uuid = @"B9407F30-F5F8-466E-AFF9-25556B57FE6D";
CLBeaconRegion *region = [[CLBeaconRegion alloc]
initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:uuid]
identifier:[NSString stringWithFormat:@"beacon_%@", uuid]];
region.notifyEntryStateOnDisplay = YES;
_locationManager startMonitoringForRegion:region];
[_locationManager startRangingBeaconsInRegion:region];
Пиликать также непросто. Скорость реакции устройства (смартфона) — от секунды до пары минут. То есть, пользователь может пройти мимо бикона, походить еще пару минут, только после этого появится уведомление.
Сами биконы — ломаются. Если закупать их много (и это более-менее необходимость для почти всех вариантов использования), то приходится экономить, следовательно, биконы выходят из строя и их нужно заменять. Нет ничего проще, но после замены нужно перепроставлять соответствие конкретному бикону данных (рекламного текста, или координат). Как следствие, в разработке нельзя обойтись только приложением и биконами. Приходится создавать сервер, который должен хранить соответствие информации биконам, а приложению требуется регулярно обновлять данные.
Возвращаясь к реальному использованию. В 2015 году проходило два мероприятия GeekPicnic, в Москве и Санкт-Петербурге. Это мероприятия на открытом воздухе, на которых собирается много различных докладчиков, интересных артефактов, машин, арт-объектов. За два дня мероприятие посещают 25000 человек.
На каждом мероприятии (которое проходит под открытым небом и в нескольких павильонах) десятки интересных объектов. Искать на карте их не очень удобно, поэтому решено было использовать биконы для их обозначения и уведомлений, когда пользователь к ним подходит. Я писал приложение для Айфона, коллеги потом повторяли его для Андроида.
Сама схема работы получилась примерно следующая:
- по всему мероприятию развешаны биконы
- мобильное приложение выступает сканером, уведомляющим посетителя о том, что он подошел к интересному месту
- данные о связи биконов и текстов интересных мест хранятся на сервере и обновляются в мобильном приложении, когда могут (посетителей очень много и связь с сервером непосредственно на мероприятии не очень)
Чтобы избежать «дребезга» и большого количества нотификаций, сохранялись данные по всем биконам вокруг и находился ближайший, причём он должен быть «существенно ближайшим», пока несколько биконов находятся примерно на одинаковом расстоянии, нотификации не проходит.
Также пришлось решить проблему энергопотребления. Чтобы навигация не включалась сразу после установки приложения, во-первых, сканирование биконов включалось только в определенные дни, а во-вторых, только в определенной области (в радиусе нескольких километров от места проведения мероприятия). Забавно было тестировать оба этих условия, пришлось покататься на машине с включенной отладкой, следя за активностью смартфона (причём, в разных состояниях, активном, режиме сна).
В результате всё получилось, а я с коллегами получил бесценный опыт реализации крупного проекта, использующего биконы.
Навигация внутри помещений
Перейдём к технике. Когда говорят про навигацию, обычно подразумевают нахождение местоположения по расстоянию до нескольких точек (так работает GPS, триангуляция местоположения по вышкам сотовой связи и это именно то, про что обычно говорят в фильмах). Алгоритм работы простой:
- мы знаем, где находятся несколько опорных маяков. Спутники это, вышки или биконы — не важно. Главное, точки должны быть определены, и достаточно точно.
- каким-то образом мы определяем расстояние до минимум трёх точек. В реальном мире трёх недостаточно, стараются использовать больше. От точности этих расстояний также зависит точность вычислений.
- по этим расстояниям вычисляется местоположение приёмника (пользователя).
Каждый, кто пробовал это проделать с биконами, быстро понимал бесполезность идеи. Местоположение биконов можно замерить точно, а вот расстояние до них измеряется по мощности сигнала маяка. Эта мощность очень зависит от давления воздуха, влажности, наличия различных препятствий (включая людей и другую живность). Очень — это значит, в несколько раз. Конечно же, софт пытается это сгладить, применяя фильтры и разные алгоритмы, но это не важно. Алгоритм в таких условиях, фактически, не работает (выдаёт крайне неточные результаты).
Подумав немного, я вспомнил, что есть другой вариант. В отличие от «обычного» алгоритма, он не выдаёт точное местоположение пользователя, а, скорее, показывает, в какой области он находится. Но для местоположения внутри помещений часто этого достаточно.
Алгоритм называется fingerprinting, отпечаток местоположения. В общем случае он выглядит так:
- расставляем биконы как-то «адекватно»,
- бегаем по помещению, запоминая, как выглядит картинка по биконам (какие сильнее, какие слабее), снимая «отпечатки» картины биконов в разных местах.
- после того, как сняты отпечатки, пользователь перемещается и смартфон сравнивает текущий отпечаток с теми, что в его памяти. Подобрав ближайший похожий, мы понимаем, в какой области находится пользователь.
Для того, чтобы алгоритм успешно работал, пришлось повозиться. Но в результате получилось очень неплохо.
Непонятный момент был, как именно сохранять отпечатки. Мощности напрямую нельзя (они плавают очень сильно). Попробовал относительные мощности, получилось сильно лучше.
Плавание мощностей заметно даже, если просто крутиться на одном месте. Стоим, поворачиваемся, и картинка меняется кардинально. Поэтому я стал снимать несколько отпечатков, стоя на одном месте. Сами отпечатки получились такие:
@property (nonatomic) NSString *uuid;
@property (nonatomic) CLBeaconMajorValue major;
@property (nonatomic) CLBeaconMajorValue minor;
@property (nonatomic) CGFloat relativeDistance; // 0-1 (normalized by the powerful)
@property (nonatomic) CGFloat relativePowerDispersion; // 0-1 (normalized by the most powerful)
Я брал эти параметры для каждого видимого бикона в данной точке, и все параметры для всех биконов — и стали отпечатком.
Также хотелось, конечно же, чтобы точка на карте не просто прыгала, а перемещалась по карте. Для этого пришлось интерполировать отпечаток, понимая, между какими областями/точками находится пользователь, и, конечно же, сильно получившийся результат фильтровать. Расстояние между текущим отпечатком и отпечатками регионов вычислялось примерно так:
for (Fingerprint *f in in _regions[regionName].fingerprints) {
CGFloat newDistance = [f normalizedDistanceToFingerprint:aBeaconsFingerprint];
CGFloat distance = (CGFloat) (result[regionName] == nil ?
FLT_MAX :
[result[regionName] doubleValue]);
distance = MIN(newDistance, distance);
result[regionName] = @(distance);
}
А, собственно, само расстояние между отпечатками, так:
- (CGFloat)normalizedDistanceToFingerprint:(NSMutableDictionary *)aBeaconsFingerprint {
// if we do not have a beacon in region fingerprint — add DISTANCE_PENALTY_FOR_ABSENT_BEACON
// if we do not have any beacons, result is "FLT_MAX"
CGFloat result = 0;
BOOL resultIsNotInfinite = NO;
NSArray *regionBeaconIds = [_beaconsFingerprint allKeys];
NSArray *testingBeaconIds = [aBeaconsFingerprint allKeys];
for (NSString *beaconId in regionBeaconIds) {
if (![testingBeaconIds containsObject:beaconId]) {
result += DISTANCE_PENALTY_FOR_ABSENT_BEACON;
} else {
result += fabs(
fabs(((Fingerprint *) _beaconsFingerprint[beaconId]).relativeDistance) -
fabs([aBeaconsFingerprint[beaconId] doubleValue]));
resultIsNotInfinite = YES;
}
}
for (NSString *testingBeaconId in testingBeaconIds) {
if (![regionBeaconIds containsObject:testingBeaconId]) {
result += DISTANCE_PENALTY_FOR_ABSENT_BEACON;
}
}
return resultIsNotInfinite ? result : FLT_MAX;
}
Получилось очень хорошо. Настоящее решение потребует продумать систему замены вышедшего из строя бикона (переснимать все отпечатки — плохое решение, это может занять много времени). А при наличии достаточного количества грамотно расставленных биконов (лучше всего их развешивать ближе к потолку, например, но это не единственная рекомендация) — и точность получается хорошая (±несколько метров).
Выводы
Сейчас шум вокруг технологии iBeacon поутих. Но задачи никуда не делись. По-прежнему требуется навигация внутри помещений. По-прежнему нужна возможность сообщить посетителям магазина о новых товарах. И делать это сейчас можно не только рекламными банерами, но и вот такими маячками.
Конечно же, реальное их использование не столь прямолинейно, и требуется решать множество задач, чтобы всё заработало, как нужно. Главный вывод более, чем года работы с технологией — она, с определёнными оговорками, жизнеспособна. А дальше уже нужно смотреть, подходит ли она для конкретного применения или нет. Увы, серебряной пули пока не получилось.