20

I'm trying to detect three actions: when a user begins walking, jogging, or running. I then want to know when the stop. I've been successful in detecting when someone is walking, jogging, or running with the following code:

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

The problem I run into is two-fold:

1) update is called multiple times every time there's a motion, probably because it checks so frequently that when the user begins walking (i.e. _acceleration.x >= .1000) it is still >= .1000 when it calls update again.

Example Log:

    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) I'm having difficulty figuring out how to detect when the user stopped. Does anybody have advice on how to implement "Stop Detection"

4

5 回答 5

26

根据您的日志,accelerometerUpdateInterval大约是0.02. 如果您更改CMMotionManager.

仅检查 x 加速度不是很准确。我可以将设备放在桌子上(比如说在左边),x 加速度将等于 1,或者稍微倾斜一下。这将导致程序处于步行模式 (x > 0.1) 而不是idle

这是指向基于智能手机的活动跟踪出版物的高级计步器的链接。它们跟踪加速度矢量方向的变化。这是两个连续加速度矢量读数之间角度的余弦值。

cos(角度)公式

显然,在没有任何运动的情况下,两个向量之间的夹角接近于零并且cos(0) = 1。在其他活动期间d < 1。为了滤除噪声,他们使用d的最后 10 个值的加权移动平均值。

WMA10公式

实施后,您的值将如下所示(红色 - 步行,蓝色 - 跑步):

WMA(d)

现在您可以为每个活动设置一个阈值以将它们分开。请注意,平均步进频率为 2-4Hz。您应该期望当前值在一秒钟内至少几次超过阈值,以便识别操作。

另一个有用的出版物:

更新

_acceleration.x, _accelaration.y,_acceleration.z是同一加速度矢量的坐标。您在d公式中使用这些坐标中的每一个。为了计算d,您还需要存储先前更新的加速度向量(公式中带有 i-1 索引)。

WMA 只考虑 10 个不同权重的 last d值。最近的d值具有更大的权重,因此对结果值的影响更大。您需要存储 9 个之前的d值才能计算当前的 d 值。您应该将 WMA 值与相应的阈值进行比较。

于 2014-03-11T20:26:37.080 回答
17

如果您使用的是 iOS7 和 iPhone5S,我建议您查看由于 M7 芯片而在 iPhone5S 中可用的 CMMotionActivityManager。它也可用于其他几个设备:

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

我测试了它,它似乎非常准确。唯一的缺点是它不会在您开始动作(例如走路)时立即给您确认。一些黑盒算法会等待以确保您真的是在走路或跑步。但是你知道你有一个确认的动作。

这比乱用加速度计好。苹果处理了这个细节!

于 2014-02-22T17:50:39.670 回答
7

您可以使用这个简单的库来检测用户是在步行、跑步、在车辆上还是不动。适用于所有 iOS 设备,无需 M7 芯片。

https://github.com/SocialObjects-Software/SOMotionDetector

在 repo 你可以找到演示项目

于 2014-03-08T18:48:32.657 回答
5

我在我的室内导航项目中遵循这篇论文PDF via 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 的原始值,算法是混乱的。

希望这可以帮助某人。

编辑(2016 年 4 月 3 日)

我忘了提供我的roundingPrecision价值。我把它定义为 3。这只是简单的数学,这么多重要的价值就足够体面了。如果你喜欢你提供更多。

还有一件事要提到的是,目前,该算法要求 iPhone 在您行走时在您的手中。见下图。抱歉,这是我唯一能找到的。

走路时的 iPhone 状态

我的 GitHub 存储库托管行人状态

于 2015-11-07T11:17:56.330 回答
0

您可以使用 Apple 最新的机器学习框架 CoreML 来了解用户活动。首先,您需要收集标记数据并训练分类器。然后,您可以在您的应用中使用此模型对用户活动进行分类。如果您对 CoreML 活动分类感兴趣,可以关注本系列。

https://medium.com/@tyler.hutcherson/activity-classification-with-create-ml-coreml3-and-skafos-part-1-8f130b5701f6

于 2021-02-25T13:58:33.347 回答