ios 使用 Core Motion 和 CMAccelerometer Data 检测某人何时开始行走
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21957588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detecting when someone begins walking using Core Motion and CMAccelerometer Data
提问by Apollo
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.
1)每次发生运动时都会多次调用更新,可能是因为它检查的频率如此之高,以至于当用户开始行走时(即_acceleration.x >= .1000),当它再次调用更新时,它仍然是 >= .1000。
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"
2)我很难弄清楚如何检测用户何时停止。有人对如何实施“停止检测”有建议吗
回答by vokilam
According to your logs, accelerometerUpdateInterval
is about 0.02
. Updates could be less frequent if you change mentioned property of CMMotionManager
.
根据您的日志,accelerometerUpdateInterval
大约是0.02
. 如果您更改CMMotionManager
.
Checking only x-acceleration isn't very accurate. I can put a device on a table in a such way (let's say on left edge) that x-acceleration will be equal to 1, or tilt it a bit. This will cause a program to be in walkingmode (x > 0.1) instead of idle.
仅检查 x 加速度不是很准确。我可以将一个设备以这样的方式(假设在左边缘)放在桌子上,x 加速度将等于 1,或者稍微倾斜它。这将导致程序处于步行模式 (x > 0.1) 而不是idle。
Here's a link to ADVANCED PEDOMETER FOR SMARTPHONE-BASED ACTIVITY TRACKINGpublication. They track changes in the direction of the vector of acceleration. This is the cosine of the angle between two consecutive acceleration vector readings.
这是ADVANCED PEDOMETER FOR SMARTPHONE-BASED ACTIVITY TRACKING出版物的链接。它们跟踪加速度矢量方向的变化。这是两个连续加速度矢量读数之间夹角的余弦值。
Obviously, without any motion, angle between two vectors is close to zero and cos(0) = 1
. During other activities d< 1. To filter out noise, they use a weighted moving average of the last 10 values of d.
显然,在没有任何运动的情况下,两个向量之间的夹角接近于零和cos(0) = 1
。在其他活动期间d< 1。为了滤除噪音,他们使用d的最后 10 个值的加权移动平均值。
After implementing this, your values will look like this (red - walking, blue - running):
执行此操作后,您的值将如下所示(红色 - 行走,蓝色 - 跑步):
Now you can set a threshold for each activity to separate them. Note that average step frequency is 2-4Hz. You should expect current value to be over the threshold at least few times in a second in order to identify the action.
现在您可以为每个活动设置一个阈值以将它们分开。请注意,平均步进频率为 2-4Hz。您应该期望当前值在一秒钟内至少超过阈值几次,以便识别操作。
Another helpful publications:
另一个有用的出版物:
- ERSP: An Energy-efficient Real-time Smartphone Pedometer(analyze peaks and throughs)
- A Gyroscopic Data based Pedometer Algorithm(threshold detection of gyro readings)
- ERSP:一种节能的实时智能手机计步器(分析峰值和通过)
- 基于陀螺仪数据的计步器算法(陀螺仪读数的阈值检测)
UPDATE
更新
_acceleration.x
, _accelaration.y
, _acceleration.z
are coordinates of the same acceleration vector. You use each of these coordinates in dformula. In order to calculate dyou also need to store acceleration vector of previous update (with i-1 index in formula).
_acceleration.x
, _accelaration.y
,_acceleration.z
是同一个加速度矢量的坐标。您在d公式中使用这些坐标中的每一个。为了计算d,您还需要存储先前更新的加速度向量(公式中的 i-1 索引)。
WMA just take into account 10 last dvalues with different weights. Most recent dvalues have more weight, therefore, more impact on resulting value. You need to store 9 previous dvalues in order to calculate current one. You should compare WMA value to corresponding threshold.
WMA 只考虑具有不同权重的10 个最后d值。最近的d值具有更大的权重,因此对结果值的影响更大。您需要存储 9 个先前的d值才能计算当前值。您应该将 WMA 值与相应的阈值进行比较。
回答by Khaled Barazi
if you are using iOS7 and iPhone5S, I suggest you look into CMMotionActivityManager which is available in iPhone5S because of the M7 chip. It is also available in a couple of other devices:
如果您使用的是 iOS7 和 iPhone5S,我建议您查看 iPhone5S 中可用的 CMMotionActivityManager,因为它是 M7 芯片。它也可用于其他一些设备:
Here is a code snippet I put together to test when I was learning about it.
这是我在学习时放在一起测试的代码片段。
#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);
}];
}
I tested it and it seems to be pretty accurate. The only drawback is that it will not give you a confirmation as soon as you start an action (walking for example). Some black box algorithm waits to ensure that you are really walking or running. But then you know you have a confirmed action.
我测试了它,它似乎非常准确。唯一的缺点是它不会在您开始动作(例如步行)时立即给您确认。一些黑盒算法会等待以确保您真的在走路或跑步。但是你知道你有一个确认的动作。
This beats messing around with the accelerometer. Apple took care of that detail!
这比搞乱加速度计要好。苹果照顾到了这个细节!
回答by arturdev
You can use this simple library to detect if user is walking, running, on vehicle or not moving. Works on all iOS devices and no need M7 chip.
您可以使用这个简单的库来检测用户是在走路、跑步、在车上还是没有移动。适用于所有 iOS 设备,无需 M7 芯片。
https://github.com/SocialObjects-Software/SOMotionDetector
https://github.com/SocialObjects-Software/SOMotionDetector
In repo you can find demo project
在 repo 中,您可以找到演示项目
回答by Can
I'm following this paper(PDF via RG) in my indoor navigation project to determine user dynamics(static, slow walking, fast walking) via merely accelerometer data in order to assist location determination.
我正在我的室内导航项目中关注这篇论文(PDF via RG),仅通过加速度计数据来确定用户动态(静态、慢走、快走),以帮助确定位置。
Here is the algorithm proposed in the project:
这是项目中提出的算法:
And here is my implementation in Swift 2.0:
这是我在 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)!)
}
}
After all of the classic Swifty iOS code to initiate CoreMotion, here is the method crunching the numbers and determining the state:
在所有用于启动 CoreMotion 的经典 Swifty iOS 代码之后,这里是处理数字和确定状态的方法:
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
}
}
Also I've used the following extension to simplify significant figure setting:
我还使用了以下扩展来简化有效数字设置:
extension Double {
func roundTo(precision: Int) -> Double {
let divisor = pow(10.0, Double(precision))
return round(self * divisor) / divisor
}
}
With raw values from CoreMotion, the algorithm was haywire.
使用来自 CoreMotion 的原始值,该算法失控了。
Hope this helps someone.
希望这可以帮助某人。
EDIT (4/3/16)
编辑 (4/3/16)
I forgot to provide my roundingPrecision
value. I defined it as 3. It's just plain mathematics that that much significant value is decent enough. If you like you provide more.
我忘了提供我的roundingPrecision
价值。我将其定义为 3。这只是简单的数学运算,如此重要的值就足够了。如果你喜欢你提供更多。
Also one more thing to mention is that at the moment, this algorithm requires the iPhone to be in your hand while walking. See the picture below. Sorry this was the only one I could find.
还有一件事要提到的是,目前,该算法要求您在走路时将 iPhone 握在手中。见下图。抱歉,这是我唯一能找到的。