Search code examples
iosaccelerometercore-motionhighpass-filter

High Pass Filter Equation from iOS accelerometer not returning good results


I am trying to implement High Pass Filter for iOS accelerometer based on Accelerometer Graph project. The code is written in Swift here is the complete class:

import Foundation
import CoreMotion
import SpriteKit

class Accelerometer {

     let motionManager: CMMotionManager

     var x: Double
     var y: Double
     var z: Double
     var lastX: Double
     var lastY: Double
     var lastZ: Double
     let kUpdateFrequency: Double
     let cutOffFrequency: Double
     let dt: Double
     let RC: Double
     let alpha: Double
     let kFilteringFactor = 0.6


init(manager: CMMotionManager){

    motionManager = manager
    motionManager.accelerometerUpdateInterval = 1.0 / 60
    motionManager.startDeviceMotionUpdates()

    x = 0.0
    y = 0.0
    z = 0.0
    lastX = 0.0
    lastY = 0.0
    lastZ = 0.00
    kUpdateFrequency = 60.0
    cutOffFrequency = 5.0
    dt = 1.0 / kUpdateFrequency
    RC = 1.0 / cutOffFrequency
    alpha = RC / (dt+RC)
    getAccelerometerUpdates()

}

func getAccelerometerUpdates() {

    motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { (data, error) -> Void in

        // Filter the raw measurments with high pass filter
        self.highPassFilter(data!)
    })

}


// High pass filter function for accelerometer measurments
func highPassFilter(data: CMAccelerometerData){

    self.x = self.alpha * (self.x + data.acceleration.x - self.lastX)
    self.y = self.alpha * (self.y + data.acceleration.y - self.lastY)
    self.z = self.alpha * (self.z + data.acceleration.z - self.lastZ)

    self.lastX = data.acceleration.x
    self.lastY = data.acceleration.y
    self.lastZ = data.acceleration.z

}

}

The class is a part of a project involving SpriteKit and CoreLocation so I removed some parts from the class since they are not relevant. The problem with this code is that it doesn't work as the example given by Apple. In the Accelerometer Graph all the values are equal to 0 when the phone is stationary and very sensitive to movement which is something what is expected from a high pass filter, however in my case the values are not always 0 when the phone is not moving, it takes time for them to change (even with a very intensive shake), and finally it usually takes few seconds before they stabilize. In short it behaves more like a low pass or Kalman Filter rather than the High pass Filter. Since it's a SpriteKit project I've tried calling the accelerometer updates from GameScene.swift file as well however the results are the same. I also experimented with gravity trying both startDeviceMotionUpdatesToQueue and startDeviceMotionUpdatesToQueue functions but with no luck.


Solution

  • The problem is that you use the Application's main queue for your accelerometer updates. The main queue is used to perform changes to your UI. Using your main queue for accelerometer updates overloads your main queue so not every operation is executed immediately after being submitted to the NSOperationQueue. The solution is to create a new NSOperationQueue

    let queue = NSOperationQueue()
    

    and pass it to motionManager.startAccelerometerUpdatesToQueue instead of NSOperationQueue.mainQueue().

    If you want to perform changes to your UI inside the motionHandler, you have to use gcd's

    dispatch_async(dispatch_get_main_queue() {
        //perform UI changes here
    }