iOS CoreMotion CMAttitude相对于北极

时间:2011-03-03 16:00:20

标签: iphone cllocationmanager heading

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

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

提前致谢,

亚历山大

4 个答案:

答案 0 :(得分:3)

iOS 5提供了指定的方法。在开发人员文档中查找CMAttitudeReferenceFrameXTrueNorthZVertical。

答案 1 :(得分:1)

伪代码:

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

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

答案 2 :(得分:0)

您需要立即将偏航值校准到磁性航向,然后确保您处于正确的轨道上。查看有关如何补偿不稳定指南针的解释:Compensating compass lag with the gyroscope on iPhone 4

答案 3 :(得分:-1)

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

长话短说,你可能不想自己做数学。幸运的是,iOS在其CLHeading对象中提供了magneticHeading和trueHeading,可从CLLocationManager标题属性中获得。

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