Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unusual CoreMotion Sensorfusion Data

I'm working on an app which collects CoreMotion data and I'm using an existing app store application as a reference to ensure I'm collecting data correctly.

For the most part, everything is equivalent but I'm seeing some strange data in certain tests. The problem is showing up in the roll, pitch and yaw values, however, the raw data from the accelerometer, gyroscope, and magnetometer is equivalent...

The charts below plots the data collected from 5 devices on a rig @100Hz:

  1. iPhone 5 (iOS 10.0.2)
  2. iPhone 5 (iOS 10.0.2)
  3. iPhone 5 (iOS 10.0.2)
  4. iPhone 5s (iOS 10.0.2)
  5. iPhone 6s (iOS 10.0.2)

First, the data collected from SensorLog: SensorLog Data Second, data collected from my app: My App Data Third, data collected from my app but this time @10Hz: enter image description here

Observations:

  • Problem seems to be limited to iPhone 5 & 5s Update: Problem has been observed in 6s too
  • SensorLog is able to record values correctly (although I have seen one instance of it generating 'bad?' data) which would seem to eliminate a hardware limitation (previous iPhone 4s tests ok too)
  • iPhone 6s performs fine in all tests Update: No longer the case
  • My app may be affected by the frequency as the results show that @10Hz, 1 iPhone 5 is ok but @100Hz none are
  • Raw data is equivalent in all devices so it's not clear why sensor fusion data is off for some devices
  • Using Quaternions does not fix the problem as they are derived from the same raw data
  • The 'bad' data in some cases appears to be following the good data but the amplitude is way too large as demonstrated below

Zooming in on the plots:

Zoomed Chart

And overlaying the manually stretched out roll data on the plot above:

Overlay roll

Code:

func start(_ interval: TimeInterval = 0.1) {
    self.interval = interval

    logTimer = Timer.new(every: interval, {
        self.motionData.currentRecord = self.motionDataRecord
        self.motionData.createCoreDataRecord()
        NotificationCenter.default.post(name: .motionHelperDidUpdateData, object: nil)
    })
    logTimer.start()

    startCoreLocation()
    startAccelerometer()
    startDeviceMotion()
    startGyroscope()
    startMagnetometer()
}

func startCoreLocation() {
    switch CLLocationManager.authorizationStatus() {
    case .authorizedAlways:
        locationManager.startUpdatingLocation()
        locationManager.startUpdatingHeading()
    case .notDetermined:
        locationManager.requestAlwaysAuthorization()
    case .authorizedWhenInUse, .restricted, .denied:
        delegate?.reRequestAlwaysAuthorization()
    }
}

func startAccelerometer() {
    if motionManager.isAccelerometerAvailable {
        motionManager.accelerometerUpdateInterval = interval
        motionManager.startAccelerometerUpdates(to: queue) {
            [weak self] (data, error) in

            guard let weakSelf = self else { return }

            if error != nil {
                print("Accelerometer Error: %@", error)
            }
            guard let data = data else { return }

            weakSelf.motionDataRecord.accelerometer = data
        }
    } else {
        print("The accelerometer is not available")
    }

}

func startGyroscope() {
    if motionManager.isGyroAvailable {
        motionManager.gyroUpdateInterval = interval
        motionManager.startGyroUpdates(to: queue) {
            [weak self] (data, error) in

            guard let weakSelf = self else { return }

            if error != nil {
                print("Gyroscope Error: %@", error)
            }
            guard let data = data else { return }

            weakSelf.motionDataRecord.gyro = data
        }
    } else {
        print("The gyroscope is not available")
    }
}

func startMagnetometer() {
    if motionManager.isMagnetometerAvailable {
        motionManager.magnetometerUpdateInterval = interval
        motionManager.startMagnetometerUpdates(to: queue) {
            [weak self] (data, error) in

            guard let weakSelf = self else { return }

            if error != nil {
                print("Magnetometer Error: %@", error)
            }
            guard let data = data else { return }

            weakSelf.motionDataRecord.magnetometer = data
        }
    } else {
        print("The magnetometer is not available")
    }
}

func startDeviceMotion() {
    if motionManager.isDeviceMotionAvailable {
        motionManager.deviceMotionUpdateInterval = interval
        motionManager.startDeviceMotionUpdates(using: attitudeReferenceFrame, to: queue) {
            [weak self] (data, error) in

            guard let weakSelf = self else { return }

            if error != nil {
                print("Device Motion Error: %@", error)
            }
            guard let data = data else { return }

            weakSelf.motionDataRecord.deviceMotion = data
        }
    } else {
        print("Device motion is not available")
    }
}

Is there a problem with the way I am collecting the data from CoreMotion? Is there a more efficient way to do it?

What could be happening here?

UPDATE:

I've written a bare bones app as follows and I'm getting similar results:

class ViewController: UIViewController {

    @IBOutlet weak var startStop: UIButton!

    var isRunning = false

    let manager: CMMotionManager = {
        let manager = CMMotionManager()
        manager.deviceMotionUpdateInterval = 1/100
        return manager
    }()

    @IBAction func handleStartStop(_ sender: AnyObject) {
        if isRunning {
            stopMotionUpdates()
            startStop.setTitle("Start", for: .normal)
        } else {
            startMotionUpdates()
            startStop.setTitle("Stop", for: .normal)
        }
        isRunning = !isRunning
    }

    func startMotionUpdates() {
        manager.startDeviceMotionUpdates(using: .xTrueNorthZVertical, to: .main) { (data, error) in
            print("Roll: \(data!.attitude.roll), Pitch: \(data!.attitude.pitch), Yaw: \(data!.attitude.yaw)")
        }
    }

    func stopMotionUpdates() {
        manager.stopDeviceMotionUpdates()
    }

}
like image 905
doovers Avatar asked Oct 14 '16 00:10

doovers


1 Answers

From what i see is that the app 'Sensor Log' you mentioned is using some more data filters to clean their data. Because their is no way that even after Sensor fusion data is so clean as shown by app.

As per your observation that the phone Sensor Fusion is off. It can not be off because if it is off you will get totally raw data that would not be understandable or even near to what your output is.

From what i can suggest is that you search for Filters used for cleaning gyro data with the help of Accelerometer and Magnetometer. Probably if you are only interested in Roll, Pitch, Yaw . Search for filters which are used to balance quad-copter. They might help you.

Sorry this is not an answer, just a comment.But i don't have much reputation to comment. so, you can understand.

like image 104
saad_nad Avatar answered Sep 30 '22 18:09

saad_nad