DeviceMotion относительно мира - multiplyByInverseOfAttitude - PullRequest
10 голосов
/ 31 октября 2011

Как правильно использовать CMAttitude: multiplyByInverseOfAttitude?

Предполагается, что устройство iOS5 лежит на столе, после запуска CMMotionManager с:

CMMotionManager *motionManager = [[CMMotionManager alloc]init];
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:
    CMAttitudeReferenceFrameXTrueNorthZVertical];

Позже получаются объекты CMDeviceMotion:

CMDeviceMotion *deviceMotion = [motionManager deviceMotion];

Я ожидаю, что [deviceMotion position] отражает поворот устройства из Истинного Севера.

По наблюдениям, [deviceMotion userAcceleration] сообщает об ускорении в системе отсчета устройства. То есть перемещение устройства из стороны в сторону (удерживая его на столе ровно) регистрирует ускорение по оси x. Поворот устройства на 90 ° (все еще в горизонтальном положении) и перемещение устройства из стороны в сторону по-прежнему сообщает об ускорении x.

Как правильно преобразовать [deviceMotion userAcceleration] для получения ускорения Север-Юг / Восток-Запад, а не влево-вправо / вперед-назад?

CMAttitude multiplyByInverseOfAttitude кажется ненужным, поскольку опорная рамка уже указана, и из документации неясно, как применить отношение к CMAcceleration.

Ответы [ 4 ]

21 голосов
/ 04 ноября 2011

Вопрос не возник бы, если бы у CMDeviceMotion имелся аксессор для userAcceleration в координатах системы отсчета.Итак, я использовал категорию для добавления необходимого метода:

В CMDeviceMotion + TransformToReferenceFrame.h:

#import <CoreMotion/CoreMotion.h>

@interface CMDeviceMotion (TransformToReferenceFrame)
-(CMAcceleration)userAccelerationInReferenceFrame;
@end

и в CMDeviceMotion + TransformToReferenceFrame.m:

#import "CMDeviceMotion+TransformToReferenceFrame.h"

@implementation CMDeviceMotion (TransformToReferenceFrame)

-(CMAcceleration)userAccelerationInReferenceFrame
{
    CMAcceleration acc = [self userAcceleration];
    CMRotationMatrix rot = [self attitude].rotationMatrix;

    CMAcceleration accRef;
    accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
    accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
    accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;

    return accRef;
}

@end

и в Swift 3

extension CMDeviceMotion {

    var userAccelerationInReferenceFrame: CMAcceleration {
        let acc = self.userAcceleration
        let rot = self.attitude.rotationMatrix

        var accRef = CMAcceleration()
        accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
        accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
        accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;

        return accRef;
    }
}

Теперь код, который ранее использовал [deviceMotion userAcceleration], может вместо этого использовать [deviceMotion userAccelerationInReferenceFrame].

3 голосов
/ 04 ноября 2011

Я попытался реализовать решение после прочтения ссылки, приведенной выше.

Шаги следующие:

  • принимать матрицу ротации поворотов при каждом обновлении.
  • вычислить обратную матрицу.
  • умножить обратную матрицу для вектора UserAcceleration.

результирующий вектор будет проекцией вектора.

-x север, + x юг

-y восток, + y weast

мой код еще не совершенен, я работаю над этим.

2 голосов
/ 29 марта 2016

Согласно документации Apple, CMAttitude относится к ориентации тела относительно данной системы отсчета. И userAcceleration или gravity - это значение кадра устройства. Таким образом, чтобы получить значение системы отсчета. Мы должны сделать, как @Batti сказал

  1. принимать матрицу ротации ориентации каждый раз.
  2. вычислите обратную матрицу.
  3. умножение обратной матрицы для вектора UserAcceleration.

Вот версия Swift

import CoreMotion
import GLKit

extension CMDeviceMotion {

    func userAccelerationInReferenceFrame() -> CMAcceleration {

        let origin = userAcceleration
        let rotation = attitude.rotationMatrix
        let matrix = rotation.inverse()

        var result = CMAcceleration()
        result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
        result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
        result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;

        return result
    }

    func gravityInReferenceFrame() -> CMAcceleration {

        let origin = self.gravity
        let rotation = attitude.rotationMatrix
        let matrix = rotation.inverse()

        var result = CMAcceleration()
        result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
        result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
        result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;

        return result
    }
}

extension CMRotationMatrix {

    func inverse() -> CMRotationMatrix {

        let matrix = GLKMatrix3Make(Float(m11), Float(m12), Float(m13), Float(m21), Float(m22), Float(m23), Float(m31), Float(m32), Float(m33))
        let invert = GLKMatrix3Invert(matrix, nil)

        return CMRotationMatrix(m11: Double(invert.m00), m12: Double(invert.m01), m13: Double(invert.m02),
                            m21: Double(invert.m10), m22: Double(invert.m11), m23: Double(invert.m12),
                            m31: Double(invert.m20), m32: Double(invert.m21), m33: Double(invert.m22))

    }

}

Надеюсь, это немного поможет

0 голосов
/ 31 октября 2011

Система отсчета связана со значением ориентации, посмотрите значение ориентации угла рыскания;Если вы не используете опорную рамку, при запуске приложения это значение всегда будет равно нулю. Вместо этого, если вы используете опорную рамку CMAttitudeReferenceFrameXTrueNorthZVertical, значение рыскания указывает угол между осью x и истинным севером.С помощью этой информации вы можете определить положение телефона в координатах Земли и, следовательно, положение осей акселерометра относительно кардинальных точек.

...