ユーザーが歩行、ジョギング、ランニングを開始したときの3つのアクションを検出しようとしています。次に、いつ停止したかを知りたい。次のコードを使用して、誰かが歩いている、ジョギングしている、または走っているときの検出に成功しています。
- (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);
}
私が遭遇する問題は2つあります。
1)モーションがあるたびにupdateが複数回呼び出されます。これは頻繁にチェックされるため、ユーザーが歩き始めたとき(つまり、_acceleration.x> = .1000)、updateを再度呼び出したときに、まだ> = .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)ユーザーが停止したことを検出する方法を理解するのが困難です。 「停止検出」の実装方法に関するアドバイスはありますか
ログによると、accelerometerUpdateInterval
は約_0.02
_です。上記のCMMotionManager
のプロパティを変更すると、更新の頻度が少なくなる可能性があります。
Xアクセラレーションのみをチェックすることはあまり正確ではありません。 x加速度が1になるか、少し傾けるような方法でデバイスをテーブルに置くことができます(たとえば、左端にしましょう)。これにより、プログラムはidleではなくwalkingモード(x> 0.1)になります。
ここにリンクがあります スマートフォンベースのアクティビティ追跡の高度な歩数計 の出版物。加速度のベクトルの方向の変化を追跡します。これは、2つの連続する加速度ベクトル測定値間の角度の余弦です。
明らかに、動きがない場合、2つのベクトル間の角度はゼロとcos(0) = 1
に近くなります。他のアクティビティ中d<1.ノイズを除去するには、dの最後の10個の値の加重移動平均を使用します。
これを実装すると、値は次のようになります(赤-ウォーキング、青-ランニング)。
これで、各アクティビティにしきい値を設定して、アクティビティを分離できます。平均ステップ周波数は2-4Hzであることに注意してください。アクションを識別するために、現在の値が1秒間に少なくとも数回しきい値を超えることが予想されます。
他の役立つ出版物:
更新
__acceleration.x
_、__accelaration.y
_、__acceleration.z
_は同じ加速度ベクトルの座標です。これらの各座標をd式で使用します。 dを計算するには、前の更新の加速度ベクトルを格納する必要があります(式にi-1インデックスを使用)。
WMAは、重みが異なる10個の最後のd値のみを考慮します。最新のd値は重みが大きいため、結果の値への影響が大きくなります。現在の値を計算するには、以前の9つのd値を保存する必要があります。 WMA値を対応するしきい値と比較する必要があります。
iOS7およびiPhone5Sを使用している場合は、M7チップのためにiPhone5Sで使用可能なCMMotionActivityManagerを確認することをお勧めします。他のいくつかのデバイスでも利用できます。
これは、私がそれについて学んでいたときにテストするためにまとめたコードスニペットです。
#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
リポジトリでは、デモプロジェクトを見つけることができます
私は屋内ナビゲーションプロジェクトで この論文 ( RGを介してPDF )に従って、単に加速度計データを介してユーザーダイナミクス(静的、スローウォーキング、ファーストウォーキング)を決定し、場所の決定を支援します。
プロジェクトで提案されているアルゴリズムは次のとおりです。
そしてここに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)!)
}
}
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
}
}
また、重要な数値設定を簡略化するために次の拡張機能を使用しました。
extension Double {
func roundTo(precision: Int) -> Double {
let divisor = pow(10.0, Double(precision))
return round(self * divisor) / divisor
}
}
CoreMotionからの生の値を使用した場合、アルゴリズムは不適切でした。
これが誰かを助けることを願っています。
EDIT(4/3/16)
roundingPrecision
値を指定するのを忘れました。私はそれを3と定義しました。その非常に重要な値が十分まともなのは、単なる数学です。あなたが好きならあなたはより多くを提供します。
さらにもう1つ言及する必要があるのは、現時点では、このアルゴリズムでは、歩きながらiPhoneを手に持っている必要があることです。下の図を参照してください。すみません、これは私が見つけた唯一のものでした。