使用四元数代替滚动,俯仰和偏航来跟踪设备运动

iSe*_*ker 7 iphone objective-c quaternions ios core-motion

请忍受我的长期问题,我想尽可能清楚.

我想要做的是,当使用相机拍摄照片时获得姿态(滚动倾斜和偏航),然后将姿态值保存到nsuserdefaults.保存后,方向会改变,然后通过不断比较姿态值(保存和当前)来尝试使手机处于相同的姿势.

出于界面的目的,用户界面在屏幕上具有3个点(每个姿态参数一个),其指导拍摄照片的正确方向.达到正确的态度后,屏幕上会显示匹配标志.

我一直在寻找滚动,俯仰和偏航值:

CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
myRoll = radiansToDegrees(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z)) ;
myPitch = radiansToDegrees(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z));
myYaw = radiansToDegrees(2*(quat.x*quat.y + quat.w*quat.z));
Run Code Online (Sandbox Code Playgroud)

当我注意到偏航值存在一些差异时,我搜索并可以从这里找到:链接,那个

来自四元数的偏航,俯仰和滚动,你将遇到同样的问题,就好像你只使用偏航,俯仰和滚转一样.你必须使用四元无处不在你的代码,而忘记了偏航,俯仰和滚转

所以现在我想我必须再次对所有内容进行编码...请你能够如此友好地指出我为此目的使用Quaternion的示例代码吗?

这是我正在处理的代码:

在ViewController.m中,在图像中:didFinishSavingWithError:

[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {

        CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
        double tempYaw = radiansToDegrees(asin(2*(quat.x*quat.y + quat.w*quat.z)));
        double tempRoll = radiansToDegrees(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z)) ;
        double tempPitch = radiansToDegrees(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z));

        if (savingGyroOrientation == YES) {

            NSLog(@"Roll = %f degrees",tempRoll);
            NSLog(@"Pitch = %f degrees",tempPitch);
            NSLog(@"Yaw = %f degrees",tempYaw);

            [self.deviceStatus setDouble:tempRoll forKey:@"DeviceRoll"];
            [self.deviceStatus setDouble:tempPitch forKey:@"DevicePitch"];
            [self.deviceStatus setDouble:tempYaw forKey:@"DeviceYaw"];
            [self.deviceStatus synchronize];

            savingGyroOrientation = NO;
            checkingGyroOrientation = YES;
            self.savingLabel.hidden = YES;
            self.startTimerButton.hidden = NO;

        }
        savingGyroOrientation = NO;
        checkingGyroOrientation = YES;
        self.savingLabel.hidden = YES;
        self.startTimerButton.hidden = NO;
    }

     if (timerRunning == YES) {
         if (checkingGyroOrientation == YES) {

             self.takePicButton.hidden = YES;

             int xRoll, yPitch, xYaw, yYaw;
             // Roll Checking
             if (tempRoll >= [self.deviceStatus doubleForKey:@"DeviceRoll"]-1 && tempRoll <= [self.deviceStatus doubleForKey:@"DeviceRoll"]+1 ) {

                 [self.rollDot setFrame:CGRectMake(150, 195, 20, 20)];
                 self.rollToR.hidden = YES;
                 self.rollToL.hidden = YES;
                 self.rollDot.hidden = NO;
                 rollOk = YES;
             }else{
                 rollOk = NO;
                 self.rollDot.hidden = YES;
                 self.rollToR.hidden = NO;
                 self.rollToL.hidden = NO;

                 if (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"] < 0) {
                     xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"]);
                     self.rollToR.hidden = YES;
                     if (xRoll <= 0) {
                         [self.rollToL setFrame:CGRectMake(0, 195, 20, 20)];
                     }else if (xRoll>= 300){
                         [self.rollToL setFrame:CGRectMake(300, 195, 20, 20)];
                     }else{
                         [self.rollToL setFrame:CGRectMake(xRoll, 195, 20, 20)];
                     }
                 }
                 if (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"] > 0){
                     xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"]);
                     self.rollToL.hidden = YES;
                     if (xRoll <= 0) {
                         [self.rollToR setFrame:CGRectMake(0, 195, 20, 20)];
                     }else if (xRoll>=300){
                         [self.rollToR setFrame:CGRectMake(300, 195, 20, 20)];
                     }else{
                         [self.rollToR setFrame:CGRectMake(xRoll, 195, 20, 20)];
                     }
                 }
                 if (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"] > 180){
                     xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"]-360);
                     self.rollToR.hidden = YES;
                     if (xRoll <= 0) {
                         [self.rollToL setFrame:CGRectMake(0, 195, 20, 20)];
                     }else if (xRoll>=300){
                         [self.rollToL setFrame:CGRectMake(300, 195, 20, 20)];
                     }else{
                         [self.rollToL setFrame:CGRectMake(xRoll, 195, 20, 20)];
                     }
                 }
                 if (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"] < -180){
                     xRoll = 150 + (tempRoll - [self.deviceStatus doubleForKey:@"DeviceRoll"]+360);
                     self.rollToL.hidden = YES;
                     if (xRoll <= 0) {
                         [self.rollToR setFrame:CGRectMake(0, 195, 20, 20)];
                     }else if (xRoll >= 300){
                         [self.rollToR setFrame:CGRectMake(300, 195, 20, 20)];
                     }else{
                         [self.rollToR setFrame:CGRectMake(xRoll, 195, 20, 20)];
                     }
                 }
             }
             //Pitch Checking
             if (tempPitch >= [self.deviceStatus doubleForKey:@"DevicePitch"]-1 && tempPitch <= [self.deviceStatus doubleForKey:@"DevicePitch"]+1) {
                 [self.pitchDot setFrame:CGRectMake(150, 195, 20, 20)];
                 self.pitchDot.hidden = NO;
                 self.pitchToDown.hidden = YES;
                 self.pitchToUp.hidden = YES;
                 pitchOk = YES;
             }else{
                 pitchOk = NO;
                 self.pitchDot.hidden = YES;
                 self.pitchToDown.hidden = NO;
                 self.pitchToUp.hidden = NO;
                 if (tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"] < 0) {
                     yPitch = 195+(tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"]);
                     //                         NSLog(@"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:@"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"]));
                     self.pitchToDown.hidden = YES;
                     if (yPitch <= 0) {
                         [self.pitchToUp setFrame:CGRectMake(150, 0, 20, 20)];
                     }else if (yPitch >= 390) {
                         [self.pitchToUp setFrame:CGRectMake(150, 390, 20, 20)];
                     }else{
                         [self.pitchToUp setFrame:CGRectMake(150, yPitch, 20, 20)];
                     }
                 }
                 if (tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"] > 0){
                     yPitch = 195+(tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"]);
                     //                         NSLog(@"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:@"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"]));
                     self.pitchToUp.hidden = YES;
                     if (yPitch <= 0) {
                         [self.pitchToDown setFrame:CGRectMake(150, 0, 20, 20)];
                     }else if (yPitch >= 390) {
                         [self.pitchToDown setFrame:CGRectMake(150, 390, 20, 20)];
                     }else{
                         [self.pitchToDown setFrame:CGRectMake(150, yPitch, 20, 20)];
                     }
                 }
                 if (tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"] < -180){
                     yPitch = 195+tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"] + 360;
                     //                         NSLog(@"tempPitch is %0.02f Difference is %0.02f, yPitch is %0.02f",tempPitch, tempPitch-[self.deviceStatus doubleForKey:@"DevicePitch"],195+(tempPitch - [self.deviceStatus doubleForKey:@"DevicePitch"]));
                     //                         NSLog(@"*yPitch is %d",yPitch);
                     self.pitchToUp.hidden = YES;
                     self.pitchToDown.hidden = NO;
                     if (yPitch <= 0 ) {
                         [self.pitchToDown setFrame:CGRectMake(150, 0, 20, 20)];
                     }else if (yPitch >= 390) {
                         [self.pitchToDown setFrame:CGRectMake(150, 390, 20, 20)];
                     }else{
                         [self.pitchToDown setFrame:CGRectMake(150, yPitch, 20, 20)];
                     }
                 }
             }
             if (tempYaw >= [self.deviceStatus doubleForKey:@"DeviceYaw"]-2 && tempYaw <= [self.deviceStatus doubleForKey:@"DeviceYaw"]+2) {

                 [self.yawDot setFrame:CGRectMake(150, 195, 20, 20)];
                 self.yawDot.hidden = NO;
                 self.rotateRight.hidden = YES;
                 self.rotateLeft.hidden = YES;
                 yawOk = YES;
             }else{
                 yawOk = NO;
                 self.yawDot.hidden = YES;
                 self.rotateRight.hidden = NO;
                 self.rotateLeft.hidden = NO;

                 if (tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"] < 0 ) {
                     xYaw = 150+(tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     yYaw = 195-1.3*(tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"current yaw is %0.02f Difference is %0.02f",tempYaw, tempYaw-[self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"saved Yaw is %0.02f",[self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"xYaw is %d, yYaw is %d",xYaw,yYaw);
                     self.rotateRight.hidden = YES;
                     if (xYaw <=0 && yYaw >=390) {
                         [self.rotateLeft setFrame:CGRectMake(0, 390, 20, 20)];
                     }else{
                         [self.rotateLeft setFrame:CGRectMake(xYaw, yYaw, 20, 20)];
                     }

                 }if (tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"] > 0){
                     xYaw = 150+(tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     yYaw = 195-1.3*(tempYaw - [self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"current yaw is %0.02f Difference is %0.02f",tempYaw, tempYaw-[self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"saved Yaw is %0.02f",[self.deviceStatus doubleForKey:@"DeviceYaw"]);
                     NSLog(@"*xYaw is %d, yYaw is %d",xYaw,yYaw);
                     self.rotateLeft.hidden = YES;
                     if (xYaw >=300 && yYaw <=0) {
                         [self.rotateRight setFrame:CGRectMake(300, 0, 20, 20)];
                     }else{
                         [self.rotateRight setFrame:CGRectMake(xYaw, yYaw, 20, 20)];
                     }
                 }
             }

             if (rollOk == YES && pitchOk == YES && yawOk ==YES) {
                 self.orientationOkay.hidden = NO;
                 self.centerCircle.hidden = YES;
                 self.rollDot.hidden = YES;
                 self.pitchDot .hidden =YES;
                 self.yawDot.hidden = YES;
                 [self.clickTimer invalidate];
                 self.clickTimer = nil;
                 self.takePicButton.hidden = NO;
                 timerRunning = NO;
                 [self.motionManager stopDeviceMotionUpdates];
                 [self.deviceStatus removeObjectForKey:@"DeviceRoll"];
                 [self.deviceStatus removeObjectForKey:@"DevicePitch"];
                 [self.deviceStatus removeObjectForKey:@"DeviceYaw"];
                 [self.deviceStatus removeObjectForKey:@"DeviceAngle"];

             }else{

                 self.orientationOkay.hidden = YES;
                 if (flagger == YES) {
                     self.centerCircle.hidden = NO;
                 }
                 else{
                     self.centerCircle.hidden = YES;
                 }
             }
         }
     }else{
         self.rotateRight.hidden = YES;
         self.rotateLeft.hidden = YES;
         self.rollToL.hidden = YES;
         self.rollToR.hidden = YES;
         self.pitchToDown.hidden = YES;
         self.pitchToUp.hidden = YES;
         self.rollDot.hidden = NO;
         self.pitchDot .hidden =NO;
         self.yawDot.hidden = NO;
         [self.yawDot setFrame:CGRectMake(0, 390, 20, 20)];
         [self.rollDot setFrame:CGRectMake(0, 195, 20, 20)];
         [self.pitchDot setFrame:CGRectMake(150, 0, 20, 20)];

     }
     }];
Run Code Online (Sandbox Code Playgroud)

如果需要进一步的细节,请告诉我.

任何建议或建议总是欢迎,:)我是编程和ios的菜鸟.

谢谢!!

Kay*_*Kay 5

我认为以下事情是管理任务所必需的:

  1. 首先,你需要对四元数有一个很好的理解(如果你已经与他们交了朋友,请跳过这个).我推荐使用OpenGL:教程:使用四元数来表示旋转矩阵和四元数常见问题解答.有助于记住,(x,y,z)表示绕其旋转的轴(未标准化)和w = cos(α/ 2),即大致表示旋转量.

  2. 由于CMQuaternion只是一个结构,因此很难进行所有计算.使用全功能的四元数类而不是像cocoamath(你需要至少Quaternion.h,.m和QuaternionOperation.m来自trunk).

  3. 现在基本考虑因素:

    1. 两个四元数之间的差异(或有时称为除法)被定义为从一个方向到另一个方位的角位移可能是要走的路.它定义为
      d = a -1*b
      因此,它表示从当前位置到目标位置的增量.

    2. 拥有此增量后,您需要定义满足的条件,以便考虑达到的目标方向.我的第一个想法是使用delta角.这可以很容易地从上面计算的d四元数的w分量中检索出来:
      alpha = 2*arccos(w)
      arccos的域是受限制的,但在这种情况下这应该不是问题,因为我们对小值特别感兴趣.

也许值得强调的是,每个3D旋转都有两个单位四元数表示q-q.这可能令人困惑,但无所谓.


更新: 所以一些伪代码看起来像:

CMQuaternion cmQ = attitude.quaternion;
// Get an instance of cocoamath's Quaternion for our currently reported quaternion
Quaternion current = [Quaternion initWithRe:(double)cmQ.w i:(double)cmQ.x j:(double)cmQ.y k:(double)cmQ.z];
// the complex conjugate and normalised to be on the safe side
Quaternion inverse = [current inverse];
// build the delta, assuming you have your stored direction as class member targetQuaternion
Quaternion diff = [inverse multiply:targetQuaternion];
float alpha = 2 * acos (diff.Re);
// maxDeltaAngle is your class member variable defining the angle from which you assume the position as restored (radians)
if (fabs (alpha) < maxDeltaAngle) {
    // do my fancy camera things
}
Run Code Online (Sandbox Code Playgroud)

是的,一开始并不是那么微不足道.正如阿里在回答中所说,可视化也是一个重要问题.我的解决方案只是解决了原始数学部分.