Необычные данные CoreMotion Sensorfusion

#ios #swift #core-motion

#iOS #быстрый #core-motion

Вопрос:

Я работаю над приложением, которое собирает данные CoreMotion, и я использую существующее приложение app Store в качестве ссылки, чтобы убедиться, что я правильно собираю данные.

По большей части все эквивалентно, но я вижу некоторые странные данные в некоторых тестах. Проблема проявляется в значениях крена, тангажа и рыскания, однако исходные данные с акселерометра, гироскопа и магнитометра эквивалентны…

На приведенных ниже диаграммах показаны данные, собранные с 5 устройств на платформе с частотой 100 Гц:

  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)

Во-первых, данные, собранные из SensorLog: Данные SensorLogВо-вторых, данные, собранные из моего приложения: Данные моего приложенияВ-третьих, данные, собранные из моего приложения, но на этот раз при частоте 10 Гц: введите описание изображения здесь

Наблюдения:

  • Проблема, похоже, ограничивается обновлением iPhone 5 и 5s: проблема наблюдалась и в 6s
  • SensorLog способен корректно записывать значения (хотя я видел один случай, когда он генерировал «плохие?» данные), что, по-видимому, устраняет аппаратное ограничение (предыдущие тесты iPhone 4s тоже были в порядке)
  • iPhone 6s отлично справляется со всеми тестами Обновление: больше не имеет значения
  • Частота может повлиять на мое приложение, так как результаты показывают, что при частоте 10 Гц 1 iPhone 5 в порядке, но при частоте 100 Гц нет
  • Необработанные данные эквивалентны на всех устройствах, поэтому неясно, почему данные о слиянии датчиков отключены для некоторых устройств
  • Использование кватернионов не устраняет проблему, поскольку они получены из одних и тех же необработанных данных
  • «Плохие» данные в некоторых случаях, по-видимому, следуют за хорошими данными, но амплитуда слишком велика, как показано ниже

Увеличение масштаба графиков:

Увеличенная диаграмма

И наложение растянутых вручную данных рулона на график выше:

Бросок наложения

Код:

 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")
    }
}
 

Есть ли проблема с тем, как я собираю данные из CoreMotion? Есть ли более эффективный способ сделать это?

Что здесь может происходить?

Обновить:

Я написал простое приложение следующим образом, и я получаю аналогичные результаты:

 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()
    }

}
 

Ответ №1:

Из того, что я вижу, это то, что приложение «Журнал датчиков», о котором вы упомянули, использует еще несколько фильтров данных для очистки своих данных. Потому что даже после слияния данных данные не могут быть настолько чистыми, как показано приложением.

Согласно вашему наблюдению, слияние датчиков телефона отключено. Он не может быть отключен, потому что, если он выключен, вы получите полностью необработанные данные, которые будут непонятны или даже близки к тому, что вы выводите.

Из того, что я могу предложить, это поиск фильтров, используемых для очистки данных гироскопа с помощью акселерометра и магнитометра. Возможно, если вас интересуют только крен, тангаж, рыскание. Поиск фильтров, которые используются для балансировки квадрокоптера. Они могут вам помочь.

Извините, это не ответ, а просто комментарий.Но у меня не так много репутации, чтобы комментировать. итак, вы можете понять.