Обнаружение, когда кто-то начинает ходить, используя данные Core Motion и CMAccelerometer.
Я пытаюсь обнаружить три действия: когда пользователь начинает ходить, бегать или бегать. Я тогда хочу знать, когда остановится. Я успешно обнаружил, когда кто-то ходит, бегает или бегает с помощью следующего кода:
- (void)update:(CMAccelerometerData *)accelData {
[(id) self setAcceleration:accelData.acceleration];
NSTimeInterval secondsSinceLastUpdate = -([self.lastUpdateTime timeIntervalSinceNow]);
if (labs(_acceleration.x) >= 0.10000) {
NSLog(@"walking: %f",_acceleration.x);
}
else if (labs(_acceleration.x) > 2.0) {
NSLog(@"jogging: %f",_acceleration.x);
}
else if (labs(_acceleration.x) > 4.0) {
NSLog(@"sprinting: %f",_acceleration.x);
}
Проблема, с которой я сталкиваюсь, имеет две стороны:
1) обновление вызывается несколько раз каждый раз, когда происходит движение, возможно потому, что оно проверяет так часто, что когда пользователь начинает ходить (т.е. _acceleration.x >= .1000), он все еще> =.1000, когда он вызывает обновление снова.
Пример журнала:
2014-02-22 12:14:20.728 myApp[5039:60b] walking: 1.029846
2014-02-22 12:14:20.748 myApp[5039:60b] walking: 1.071777
2014-02-22 12:14:20.768 myApp[5039:60b] walking: 1.067749
2) Мне трудно понять, как определить, когда пользователь остановился. Кто-нибудь есть совет о том, как реализовать "Обнаружение остановки"
5 ответов
Согласно вашим журналам, accelerometerUpdateInterval
около 0.02
, Обновления могут быть менее частыми, если вы измените указанное свойство CMMotionManager
,
Проверка только x-ускорения не очень точна. Я могу положить устройство на стол таким образом (скажем, на левом краю), чтобы x-ускорение было равно 1 или немного наклонило его. Это приведет к тому, что программа будет в режиме ходьбы (x > 0,1) вместо режима ожидания.
Вот ссылка на публикацию ADVANCED PEDOMETER для отслеживания активности на основе SMARTPHONE. Они отслеживают изменения в направлении вектора ускорения. Это косинус угла между двумя последовательными показаниями вектора ускорения.
Очевидно, что без движения угол между двумя векторами близок к нулю и cos(0) = 1
, Во время других действий d <1. Для фильтрации шума они используют взвешенное скользящее среднее из последних 10 значений d.
После реализации ваши значения будут выглядеть следующим образом (красный - ходовой, синий - бегущий):
Теперь вы можете установить порог для каждого действия, чтобы разделить их. Обратите внимание, что средняя частота шага составляет 2-4 Гц. Вы должны ожидать, что текущее значение будет превышать пороговое значение, по крайней мере, несколько раз в секунду, чтобы идентифицировать действие.
Другие полезные публикации:
- ERSP: энергосберегающий шагомер для смартфона в реальном времени (анализировать пики и результаты)
- Алгоритм шагомера на основе гироскопических данных (определение пороговых значений гироскопа)
ОБНОВИТЬ
_acceleration.x
, _accelaration.y
, _acceleration.z
являются координатами одного и того же вектора ускорения. Вы используете каждую из этих координат в формуле d. Для вычисления d также необходимо сохранить вектор ускорения предыдущего обновления (с индексом i-1 в формуле).
WMA просто учитывает 10 последних значений d с разными весами. Последние значения d имеют больший вес, следовательно, больше влияют на результирующее значение. Вам необходимо сохранить 9 предыдущих значений d, чтобы рассчитать текущее. Вам следует сравнить значение WMA с соответствующим порогом.
Если вы используете iOS7 и iPhone5S, я предлагаю вам взглянуть на CMMotionActivityManager, который доступен в iPhone5S благодаря чипу M7. Он также доступен в нескольких других устройствах:
Вот фрагмент кода, который я собрал, чтобы проверить, когда узнал об этом.
#import <CoreMotion/CoreMotion.h>
@property (nonatomic,strong) CMMotionActivityManager *motionActivityManager;
-(void) inSomeMethod
{
self.motionActivityManager=[[CMMotionActivityManager alloc]init];
//register for Coremotion notifications
[self.motionActivityManager startActivityUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMotionActivity *activity)
{
NSLog(@"Got a core motion update");
NSLog(@"Current activity date is %f",activity.timestamp);
NSLog(@"Current activity confidence from a scale of 0 to 2 - 2 being best- is: %ld",activity.confidence);
NSLog(@"Current activity type is unknown: %i",activity.unknown);
NSLog(@"Current activity type is stationary: %i",activity.stationary);
NSLog(@"Current activity type is walking: %i",activity.walking);
NSLog(@"Current activity type is running: %i",activity.running);
NSLog(@"Current activity type is automotive: %i",activity.automotive);
}];
}
Я проверил это, и это кажется довольно точным. Единственным недостатком является то, что он не даст вам подтверждения, как только вы начнете действие (например, ходьба). Какой-то алгоритм черного ящика ожидает, что вы действительно гуляете или бегаете. Но тогда вы знаете, что у вас есть подтвержденное действие.
Это лучше, чем возиться с акселерометром. Apple позаботилась об этой детали!
Вы можете использовать эту простую библиотеку, чтобы определить, ходит ли пользователь, бежит, находится на транспортном средстве или не двигается. Работает на всех устройствах iOS и не требует чипа M7.
https://github.com/SocialObjects-Software/SOMotionDetector
В репо вы можете найти демо-проект
Я следую этой статье( PDF через RG) в своем проекте внутренней навигации, чтобы определить динамику пользователя (статичность, медленная ходьба, быстрая ходьба) с помощью данных только акселерометра, чтобы помочь определить местоположение.
Вот алгоритм, предложенный в проекте:
А вот моя реализация в Swift 2.0:
import CoreMotion
let motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) { (accelerometerData: CMAccelerometerData?, error: NSError?) -> Void in
if((error) != nil) {
print(error)
} else {
self.estimatePedestrianStatus((accelerometerData?.acceleration)!)
}
}
После всего классического кода Swifty для iOS для запуска CoreMotion, вот метод вычисления чисел и определения состояния:
func estimatePedestrianStatus(acceleration: CMAcceleration) {
// Obtain the Euclidian Norm of the accelerometer data
accelerometerDataInEuclidianNorm = sqrt((acceleration.x.roundTo(roundingPrecision) * acceleration.x.roundTo(roundingPrecision)) + (acceleration.y.roundTo(roundingPrecision) * acceleration.y.roundTo(roundingPrecision)) + (acceleration.z.roundTo(roundingPrecision) * acceleration.z.roundTo(roundingPrecision)))
// Significant figure setting
accelerometerDataInEuclidianNorm = accelerometerDataInEuclidianNorm.roundTo(roundingPrecision)
// record 10 values
// meaning values in a second
// accUpdateInterval(0.1s) * 10 = 1s
while accelerometerDataCount < 1 {
accelerometerDataCount += 0.1
accelerometerDataInASecond.append(accelerometerDataInEuclidianNorm)
totalAcceleration += accelerometerDataInEuclidianNorm
break // required since we want to obtain data every acc cycle
}
// when acc values recorded
// interpret them
if accelerometerDataCount >= 1 {
accelerometerDataCount = 0 // reset for the next round
// Calculating the variance of the Euclidian Norm of the accelerometer data
let accelerationMean = (totalAcceleration / 10).roundTo(roundingPrecision)
var total: Double = 0.0
for data in accelerometerDataInASecond {
total += ((data-accelerationMean) * (data-accelerationMean)).roundTo(roundingPrecision)
}
total = total.roundTo(roundingPrecision)
let result = (total / 10).roundTo(roundingPrecision)
print("Result: \(result)")
if (result < staticThreshold) {
pedestrianStatus = "Static"
} else if ((staticThreshold < result) && (result <= slowWalkingThreshold)) {
pedestrianStatus = "Slow Walking"
} else if (slowWalkingThreshold < result) {
pedestrianStatus = "Fast Walking"
}
print("Pedestrian Status: \(pedestrianStatus)\n---\n\n")
// reset for the next round
accelerometerDataInASecond = []
totalAcceleration = 0.0
}
}
Также я использовал следующее расширение, чтобы упростить значительную настройку фигуры:
extension Double {
func roundTo(precision: Int) -> Double {
let divisor = pow(10.0, Double(precision))
return round(self * divisor) / divisor
}
}
С необработанными значениями от CoreMotion, алгоритм был haywire.
Надеюсь, это кому-нибудь поможет.
РЕДАКТИРОВАТЬ (4/3/16)
Я забыл предоставить свой roundingPrecision
значение. Я определил это как 3. Это просто математика, что эта значительная ценность достаточно прилична. Если вам нравится, вы предоставляете больше.
Еще одна вещь, которую стоит упомянуть, это то, что в данный момент этот алгоритм требует, чтобы iPhone был в ваших руках во время ходьбы. Смотрите картинку ниже. Извините, это был единственный, кого я смог найти.
Вы можете использовать последнюю версию Apple CoreML для машинного обучения, чтобы узнать активность пользователей. Для начала нужно собрать размеченные данные и обучить классификатор. Затем вы можете использовать эту модель в своем приложении для классификации активности пользователей. Вы можете следить за этой серией, если вас интересует классификация действий CoreML.