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.
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
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