如何创建一个新的CMAttitude参考框架,使重力在Y轴上?

20

我希望能够更改设备运动管理器参考框架(用于陀螺仪),以便让我的重力矢量在Y轴上。

通常,当你启动设备运动管理器更新时,你只会有手机的z轴与重力对齐。

您可以使用磁力计将x轴对准磁性或真北极,从而更改此设置。这样,我的X轴指向北方,我的Z轴指向下方。

我想做的是将我的Y轴(负)指向下方(以使其与重力对齐),并且将我的X轴指向真实的磁北极。

我想要的结果是,当我将手机保持静止时,垂直(竖屏)方向上的手机将右侧对齐于地球北极,我的所有读数(滚动、俯仰和偏航)都将读为0。然后,如果我沿着X轴旋转手机,俯仰就会发生变化,如果我沿着Y轴旋转,偏航就会发生变化。

到目前为止,我知道我可以设置自己的参考框架,如果我乘以先前存储的姿态的逆,(例如,我可以手动设置我的手机在此方向上,保存该姿态,并且只需将新姿态乘以此存储的姿态的逆,所有读数将完全与我想要的一样)。

但手动设置不是选项,那么如何通过程序实现呢?

我认为没有一个函数可以创建我的姿态参考框架,或者至少有一个函数可以将姿态乘以旋转矩阵,那么我就可以解决这个问题。 (因为我只需用俯仰角度中的90度更改所有姿态即可)。

我希望我表达清楚了,

我将感激任何建议。 谢谢

附:这些是iPhone方向坐标:

enter image description here


你知道如何在iOS4上相对于真北方向采取姿态吗?iOS5有很好的方法可以实现这一点。 - vale4674
没有抱歉或想法,这就是为什么我无法创造自己的参考态度... - Pochi
这种东西在网上没有好的资料..太糟糕了。 - vale4674
我在这里找到了一个线程:https://dev59.com/FlTTa4cB1Zd3GeqPrVjW 通过使用 gravity.z(检查它是否为正或负),我可以基于Y轴模拟俯仰值。你可以试试看。 - Wayne Liu
@LuisOscar 你找到解决方案了吗?我刚打开了同样的问题,意识到一个月前看到了你的问题。 - vale4674
显示剩余3条评论
2个回答

4
伪代码:
  1. 开始设备运动更新
  2. 在后台开始相机预览 ;)
  3. 从设备中捕获当前的重力读数作为CMAcceleration...一旦你有了重力,就将其存储在本地变量中。
  4. 然后你必须取出2个向量,并得到它们之间的角度,在这种情况下是设备重力(0,0,-1)和真实重力向量之间的角度...
  5. 然后我们将theta转换为thetaPrime...一个与CoreMotion参考方向匹配的变换
  6. 设置一个计时器来进行动画...
  7. 在动画期间获取motionManager的deviceMotion属性的旋转矩阵的逆。
  8. 按正确的顺序应用变换以反映设备的当前姿态(yaw、pitch、roll在欧拉模式下或设备四元数旋转...基本上是三种不同的说法)
以下是代码:
- (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;
}

这是否正确,换句话说:当应用程序启动时,CM参考帧根据iPhone的当前姿态进行设置。也就是说,当手机以相同的角度保持时,滚动、偏航和俯仰的值不能保证相同。但是,通过读取原始加速度计数据,可以获得一个变换,将其应用于deviceMotion.attitude属性时,可以得到设备相对于重力的真实方向。 - Benji XVI
1
对于那些不熟悉向量的人,这应该等同于将初始重力读数转换为一组三个欧拉角——设备实际方向相对于您选择的参考框架(例如[0,0,-1])。将mgr.deviceMotion.attitude同时提供的角度与这些角度之间的差称为“方向差异”。 因此在计算设备的瞬时实际态度时,您可以简单地将方向差异添加到deviceMotion.attitude中的角度即可。 - Benji XVI
magnitudeCurrentGravity的公式应该是将currentGravity乘以自身,而不是相加。 - armcknight
@Orbitus007,看这部分 innerMagnitudeProduct 中的 currentGravity.y + currentGravity.y,这个加法似乎是错误的。难道不应该是乘法吗? - superjos
此外,CMAcceleration currentGravity; 不应该是一个局部变量,而应该是一个成员/字段吗?或者我有什么遗漏的吗?谢谢。 - superjos

-1

I hope this will help you

您可以更改由CMAttitude实例使用的参考帧。为此,请缓存包含该参考帧的姿态对象,并将其作为参数传递给multiplyByInverseOfAttitude:方法。接收消息的姿态参数将更改以表示相对于该参考帧的姿态变化。

为了了解这可能有多有用,考虑一场棒球比赛,用户旋转设备进行挥杆。通常,在投球开始时,球棒会处于某个静止方向。之后,球棒将以一个方向呈现,该方向是根据设备的姿态从投球开始时的位置发生的变化来确定的。

-(void) startPitch {

// referenceAttitude is an instance variable

referenceAttitude = [motionManager.deviceMotion.attitude retain];

}

- (void)drawView {

CMAttitude *currentAttitude = motionManager.deviceMotion.attitude;

[currentAttitude multiplyByInverseOfAttitude: referenceAttitude];

// render bat using currentAttitude .....

[self updateModelsWithAttitude:currentAttitude];

[renderer render];

}

更多信息请查看下面的链接,那里有你想要的东西。

http://db-in.com/blog/2011/04/cameras-on-opengl-es-2-x/


3
这并不能解决问题。这是来自于苹果开发文档的内容,当然我可以缓存那个参考姿态,但如果你检查这个方法,它需要运行时,因为它正在使用设备运动来调用当前的姿态,我想知道如何创建自己的姿态参考框架,以便我可以在不同的方向上“启动”它。 - Pochi
2
这是从苹果公司的网站抄袭而来,没有任何归属。 - aledalgrande

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接