20

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

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

您可以更改它以使用磁力计使 x 轴与磁北极或真北极对齐。有了这个,我的 X 轴指向北方,Z 轴指向下方。

我想要做的是让我的 Y 轴(负)指向下方(使其与重力对齐)并且让我的 X 轴指向真正的磁极。

我想要的结果是,当我的手机以垂直(纵向)方向静止不动时,手机的右侧将与北极对齐,我所有的读数(滚动、俯仰、偏航)都将读取为 0。然后用如果我在 X 轴上旋转我的手机,俯仰会改变,如果我围绕 Y 轴旋转,偏航会改变。

到目前为止,我知道如果我将态度的倒数乘以先前存储的态度,我可以设置自己的参考系,(就像我可以手动将手机设置在这个方向一样,保存该态度并简单地将新态度乘以倒数这个存储的一个,我所有的读数都将与我想要的完全一样)。

但是手动设置它不是一个选项,那么我如何以编程方式进行设置呢?

我不认为有一个函数可以创建我自己的姿态参考系,或者如果至少有一个函数可以将姿态乘以旋转矩阵,那么我可能会解决这个问题。(因为我只是将所有的姿态乘以 90 度的音高变化)。

我希望我清楚地解释自己,

我将不胜感激任何建议。谢谢

PD:这些是 iPhone 方向坐标:

在此处输入图像描述

4

2 回答 2

4

伪代码:

  1. 开始设备动作更新
  2. 在后台启动相机预览;)
  3. 将设备的当前重力读数捕获为 CMAcceleration ... 一旦您将重力存储在局部变量中。
  4. 然后你必须取两个向量并得到它们之间的角度,在这种情况下,设备重力为 (0,0,-1) 和真实重力向量......
  5. 然后我们把 theta 变成 thetaPrime... 一个匹配 CoreMotion 参考方向的变换
  6. 设置一个计时器来制作动画......
  7. 在动画期间获取motionManager的deviceMotion属性的rotationMatrix的倒数。
  8. 以正确的顺序应用变换以反映设备的当前姿态(偏航、俯仰、欧拉模式下的滚动或设备四元数旋转......基本上用 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;
}
于 2012-05-27T13:44:41.677 回答
-1

我希望这能帮到您

您可以更改 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/

于 2012-02-04T03:29:51.580 回答