web-dev-qa-db-ja.com

Core MotionとCMAccelerometerデータを使用して、誰かが歩行を開始したことを検出する

ユーザーが歩行、ジョギング、ランニングを開始したときの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)ユーザーが停止したことを検出する方法を理解するのが困難です。 「停止検出」の実装方法に関するアドバイスはありますか

19
Apollo

ログによると、accelerometerUpdateIntervalは約_0.02_です。上記のCMMotionManagerのプロパティを変更すると、更新の頻度が少なくなる可能性があります。

Xアクセラレーションのみをチェックすることはあまり正確ではありません。 x加速度が1になるか、少し傾けるような方法でデバイスをテーブルに置くことができます(たとえば、左端にしましょう)。これにより、プログラムはidleではなくwalkingモード(x> 0.1)になります。

ここにリンクがあります スマートフォンベースのアクティビティ追跡の高度な歩数計 の出版物。加速度のベクトルの方向の変化を追跡します。これは、2つの連続する加速度ベクトル測定値間の角度の余弦です。

cos(angle) formula

明らかに、動きがない場合、2つのベクトル間の角度はゼロとcos(0) = 1に近くなります。他のアクティビティ中d<1.ノイズを除去するには、dの最後の10個の値の加重移動平均を使用します。

WMA10 formula

これを実装すると、値は次のようになります(赤-ウォーキング、青-ランニング)。

WMA(d)

これで、各アクティビティにしきい値を設定して、アクティビティを分離できます。平均ステップ周波数は2-4Hzであることに注意してください。アクションを識別するために、現在の値が1秒間に少なくとも数回しきい値を超えることが予想されます。

他の役立つ出版物:

更新

__acceleration.x_、__accelaration.y_、__acceleration.z_は同じ加速度ベクトルの座標です。これらの各座標をd式で使用します。 dを計算するには、前の更新の加速度ベクトルを格納する必要があります(式にi-1インデックスを使用)。

WMAは、重みが異なる10個の最後のd値のみを考慮します。最新のd値は重みが大きいため、結果の値への影響が大きくなります。現在の値を計算するには、以前の9つのd値を保存する必要があります。 WMA値を対応するしきい値と比較する必要があります。

23
vokilam

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

私はそれをテストしました、そしてそれはかなり正確であるようです。唯一の欠点は、アクション(ウォーキングなど)を開始してもすぐに確認が行われないことです。一部のブラックボックスアルゴリズムは、実際にウォーキングまたはランニングしていることを確認するために待機します。しかし、あなたはあなたが確認された行動を持っていることを知っています。

これは、加速度計をいじり回すビートです。 Appleがその詳細を処理しました!

16
Khaled Barazi

この単純なライブラリを使用して、ユーザーが歩いている、走っている、車内にいる、または動いていないかどうかを検出できます。すべてのiOSデバイスで動作し、M7チップは不要です。

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

リポジトリでは、デモプロジェクトを見つけることができます

7
arturdev

私は屋内ナビゲーションプロジェクトで この論文RGを介してPDF )に従って、単に加速度計データを介してユーザーダイナミクス(静的、スローウォーキング、ファーストウォーキング)を決定し、場所の決定を支援します。

プロジェクトで提案されているアルゴリズムは次のとおりです。

enter image description here

そしてここに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を手に持っている必要があることです。下の図を参照してください。すみません、これは私が見つけた唯一のものでした。

iPhone status while walking

歩行者の状況をホストしているMy GitHub Repo

5
Can