iOS CoreMotion CMAttitude 相对于北极

Posted

技术标签:

【中文标题】iOS CoreMotion CMAttitude 相对于北极【英文标题】:iOS CoreMotion CMAttitude relative to north pole 【发布时间】:2011-07-08 04:19:49 【问题描述】:

我目前正在使用 CoreMotion 的 DeviceMotion 来获取 iPhone 的方向(滚动、俯仰、偏航)。现在我想拥有这些相对于地理北极的值;所以我需要一个包含滚动、俯仰和偏航值的 CMAttitude 参考对象,如果 iPhone 的背面朝向北极(3D),则会报告这些值。 CLLocationManager 只返回 Tesla 中的磁航向 (x, y, z)。

您知道如何将这些值转换为滚动、俯仰和偏航吗?

提前致谢,

亚历山大

【问题讨论】:

【参考方案1】:

ios 5 提供了指定方法。在开发者文档中查找 CMAttitudeReferenceFrameXTrueNorthZVertical。

【讨论】:

【参考方案2】:

伪代码:

    开始设备动态更新 在后台启动相机预览;) 将当前重力读数从设备捕获为 CMAcceleration...一旦您将重力存储在局部变量中。 然后你必须取这 2 个向量并得到它们之间的角度,在本例中是 (0,0,-1) 的设备重力和真实的重力向量... 然后我们将 theta 转换为 thetaPrime...与 CoreMotion 参考方向匹配的变换 设置一个计时器来制作动画...... 在动画期间获取motionManager的deviceMotion属性的rotationMatrix的逆。 以正确的顺序应用变换以反映设备的当前姿态(偏航、俯仰、欧拉模式下的滚动或设备四元数旋转...基本相同的 3 种不同方式)

代码如下:

- (void) initMotionCapture

    firstGravityReading = NO;
    referenceAttitude = nil;

    if (motionManager == nil)
    
        self.motionManager = [CMMotionManager new];
    
    motionManager.deviceMotionUpdateInterval = 0.01;
    self.gravityTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0 target:self selector:@selector(getFirstGravityReading) userInfo:nil repeats:YES];



- (void) getFirstGravityReading

    CMAcceleration currentGravity; 

    CMDeviceMotion *dm = motionManager.deviceMotion;
    referenceAttitude = dm.attitude;
    currentGravity = dm.gravity;

    [motionManager startDeviceMotionUpdates];

    if (currentGravity.x !=0 && currentGravity.y !=0 && currentGravity.z !=0)
    
        NSLog(@"Gravity = (%f,%f,%f)", currentGravity.x, currentGravity.y, currentGravity.z);

        firstGravityReading = YES;
        [gravityTimer invalidate];
        self.gravityTimer = nil;
        [self setupCompass];
    


- (void) setupCompass

    //Draw your cube... I am using a quartz 3D perspective hack!
    CATransform3D initialTransform = perspectiveTransformedLayer.sublayerTransform;
    initialTransform.m34 = 1.0/-10000;


    //HERE IS WHAT YOU GUYS NEED... the vector equations!
    NSLog(@"Gravity = (%f,%f,%f)", currentGravity.x, currentGravity.y, currentGravity.z);

    //we have current gravity vector and our device gravity vector of (0, 0, -1)
    // get the dot product
    float dotProduct = currentGravity.x*0 + currentGravity.y*0 + currentGravity.z*-1;
    float innerMagnitudeProduct = currentGravity.x*currentGravity.x + currentGravity.y + currentGravity.y + currentGravity.z*currentGravity.z;
    float magnitudeCurrentGravity = sqrt(innerMagnitudeProduct);
    float magnitudeDeviceVector = 1; //since (0,0,-1) computes to: 0*0 + 0*0 + -1*-1 = 1

    thetaOffset = acos(dotProduct/(magnitudeCurrentGravity*magnitudeDeviceVector));
    NSLog(@"theta(degrees) = %f", thetaOffset*180.0/M_PI);

    //Now we have the device angle to the gravity vector (0,0,-1)
    //We must transform these coordinates to match our 
    //device's attitude by transforming to theta prime
    float theta_deg = thetaOffset*180.0/M_PI;
    float thetaPrime_deg = -theta_deg + 90; // ThetaPrime = -Theta + 90 <==> y=mx+b

    NSLog(@"thetaPrime(degrees) = %f", thetaOffset*180.0/M_PI);

    deviceOffsetRotation = CATransform3DMakeRotation((thetaPrime_deg) * M_PI / 180.0, 1, 0, 0);
    initialTransform = CATransform3DConcat(deviceOffsetRotation, initialTransform);

    perspectiveTransformedLayer.sublayerTransform = initialTransform;

    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];



- (void) tick

    CMRotationMatrix rotation;

    CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
    CMAttitude *attitude = deviceMotion.attitude;

    if (referenceAttitude != nil)
    
        [attitude multiplyByInverseOfAttitude:referenceAttitude];
    
    rotation = attitude.rotationMatrix;

    CATransform3D rotationalTransform = perspectiveTransformedLayer.sublayerTransform;

    //inverse (or called the transpose) of the attitude.rotationalMatrix
    rotationalTransform.m11 = rotation.m11;
    rotationalTransform.m12 = rotation.m21;
    rotationalTransform.m13 = rotation.m31;

    rotationalTransform.m21 = rotation.m12;
    rotationalTransform.m22 = rotation.m22;
    rotationalTransform.m23 = rotation.m32;

    rotationalTransform.m31 = rotation.m13;
    rotationalTransform.m32 = rotation.m23;
    rotationalTransform.m33 = rotation.m33;

    rotationalTransform = CATransform3DConcat(deviceOffsetRotation, rotationalTransform);
    rotationalTransform = CATransform3DConcat(rotationalTransform, CATransform3DMakeScale(1.0, -1.0, 1.0));


    perspectiveTransformedLayer.sublayerTransform = rotationalTransform;

【讨论】:

【参考方案3】:

您需要不时根据磁航向校准偏航值,以确保您在正确的轨道上。查看有关如何补偿摇晃的指南针的说明:Compensating compass lag with the gyroscope on iPhone 4

【讨论】:

【参考方案4】:

特斯拉值是磁场强度,衡量在三个轴的每一个轴上感受到多少磁“拉力”。只有将此信息与加速度计数据相结合,并进行大量数学运算,您才能获得实际航向(设备相对于磁北“指向”的方式)。然后添加来自 GPS 的信息并进行更多数学运算,以获得真正的航向(相对于地理北极)。

长话短说,您可能不想自己计算。幸运的是,iOS 在其 CLHeading 对象中同时提供了 MagneticHeading 和 trueHeading,可从 CLLocationManager 标题属性中获得。

要获得描述设备如何倾斜的俯仰和横滚,还需要对来自磁力计和加速度计的相同原始数据进行数学运算。抱歉,我不知道任何用于俯仰和滚动的 iOS API。

【讨论】:

以上是关于iOS CoreMotion CMAttitude 相对于北极的主要内容,如果未能解决你的问题,请参考以下文章

iOS5如何使用CMAttitudeReferenceFrameXTrueNorthZVertical从CMAttitude获取设备方向?

iOS开发-CoreMotion框架

CoreMotion - 确定iOS设置中是不是禁用运动的方法?

ios中陀螺仪CoreMotion的用法

使用 swift 3 在 IOS 10 coremotion 中选择姿态参考系

为啥 CoreMotion 授权消息在 iOS 8 后台出现“<APP>想要访问您的运动活动”