2013-10-08 8 views
7

Per favore, sopportare la mia lunga domanda, sto cercando di renderlo il più chiaro possibile.Utilizzo di quaternion anziché roll, pitch e imbardata per tracciare il movimento del dispositivo

Quello che sto cercando di fare è ottenere l'assetto (roll-and-roll) quando una foto viene scattata usando la fotocamera e quindi salvare i valori di atteggiamento su nsuserdefaults. Dopo il salvataggio, l'orientamento viene modificato e quindi si tenta di portare il telefono nello stesso atteggiamento in cui è stata scattata la foto confrontando costantemente i valori di assetto (salvati e correnti).

Ai fini dell'interfaccia, l'interfaccia utente ha 3 punti (uno per ciascun parametro di atteggiamento) sullo schermo che guidano verso l'orientamento corretto della foto. Al raggiungimento dell'atteggiamento corretto, la bandiera della partita viene mostrata sullo schermo.

Mi è stato trovare i valori di rollio, beccheggio e imbardata come:

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)); 

Quando ho notato che c'è una certa disparità nei valori di imbardata, ho cercato e poteva trovare da qui: link, che

yaw, pitch and roll from a quaternion you will have the same problem as if you were using just yaw, pitch and roll. You have to use quaternions EVERYWHERE in your code and forget about yaw, pitch and roll

Quindi ora devo indovinare tutto di nuovo ... Per favore, potresti essere così gentile da indicarmi un codice di esempio per l'utilizzo di Quaternion per questo scopo?

Ecco il codice Sto lavorando su:

Nel ViewController.m, all'interno dell'immagine: 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)]; 

    } 
    }]; 

prega fatemi sapere se sono necessari ulteriori dettagli su questo.

Qualsiasi suggerimento o consiglio è sempre il benvenuto, :) Sono un noob alla programmazione e aios.

Grazie !!

+0

Volevo solo farti sapere, nelle definizioni dei quaternioni, che hai attivato Roll and Yaw. La formula per il rotolo è per imbardata e la formula per l'imbardata è per rotolo. – inorganik

risposta

4

penso che le seguenti cose sono necessarie per gestire al compito:

  1. Prima di tutto è necessario una buona comprensione dei quaternioni (saltare questo se si è già fatto amicizia con loro). Raccomando OpenGL:Tutorials:Using Quaternions to represent rotation o The Matrix and Quaternions FAQ. È utile ricordare che (x, y, z) rappresentano l'asse da ruotare attorno (non normalizzato) e w = cos (alfa/2) cioè si trova approssimativamente per la quantità di rotazione.

  2. Come CMQuaternion è solo una struttura, quindi è difficile fare tutti i calcoli. Utilizzare invece una classe di quaternioni completamente funzionale come cocoamath (è necessario almeno Quaternion.h, .m e QuaternionOperation.m da trunk).

  3. Ora le considerazioni di base:

    1. la differenza (o talvolta indicati come divisione) tra due quaternioni essendo definiti come spostamento angolare da un orientamento all'altro potrebbe essere la strada da percorrere.Essa è definita come
      d = a -1 * b
      Quindi questo esprime il delta dalla posizione attuale alla posizione di destinazione.

    2. Con questo delta è necessario definire le condizioni da soddisfare per considerare l'orientamento del target raggiunto. La mia prima idea è usare l'angolo delta. Questo può essere facilmente recuperato dal componente w del d quaternione sopra calcolata da:
      alfa = 2 * arccos (w)
      Il dominio di arccos è limitato, ma questo non dovrebbe essere un problema in questo caso abbiamo sono particolarmente interessati ai piccoli valori.

Forse vale la pena di sottolineare che ogni rotazione 3D ha due rappresentazioni quaternioni unitari, q e q. Questo potrebbe essere fonte di confusione, ma non importa.


Aggiornamento: Quindi un po 'di pseudo codice sarebbe simile a:

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 
} 

Sì non è che banale all'inizio. Come ha affermato Ali nella sua risposta, anche la visualizzazione è una questione importante. La mia soluzione risolve solo la parte matematica.

+0

Grazie mille per aver risposto :) Ma se sei così gentile da indicarmi un codice di esempio in cui è fatto un confronto di quaternioni, lo apprezzerei molto. Ho passato attraverso i link e hanno senso per me, ma li trovo difficili da visualizzare e comprendere pienamente il concetto di quaternion. Anche la ricerca della parte delta nel punto 3 è stata simile, non sono chiaro come implementarla effettivamente. – iSeeker

+0

Vedere il mio aggiornamento. Non essere frustrato, mi ci è voluto un po 'di tempo per immergermi nei quaternioni per tutti :-) – Kay

+0

@Ali e Kay Penso che questi siano i valori che ho bisogno per ottenere il movimento del dispositivo su diversi assi ... Sono solo sparare al buio guardando gli nslog sembrano affidabili. 'float xdiff = asin (delta.i); float ydiff = asin (delta.j); float zdiff = asin (delta.k); '. Ho aggiornato il codice qui: [LINK] (http://pastebin.com/faPWUVE2). Sto ancora cercando un lead affidabile .. – iSeeker

1

Non sto dicendo che quanto segue è la soluzione al tuo problema, non ho idea di come sarà facile da usare, ma credo che valga la pena di sparare. Vedo due problemi: come conservi l'atteggiamento e come li visualizzi.

Deposito. Vorrei salvare l'atteggiamento sia come quaternion o come matrice di rotazione; CMAttitude fornisce entrambi. (Personalmente preferisco le matrici di rotazione ai quaternioni come le matrici di rotazione sono più facili da capire a mio avviso. È solo una preferenza personale.)

Visualizzazione. Si stanno utilizzando 3 marcatori per portare il telefono nello stesso atteggiamento di quello salvato. Questi 3 pennarelli provenivano da imbardata, beccheggio e rollio, rovinando efficacemente la stabilità della vostra applicazione. Invece di questi 3 indicatori visualizzerei la rotazione tra l'atteggiamento salvato e quello corrente. Un modo per farlo è proiettare i 3 vettori di base ruotati sullo schermo del telefono. La cosa bella delle matrici di rotazione è che ti danno esattamente questo senza alcun calcolo extra. Ad esempio, per visualizzare la rotazione

| 1 0 0 | 
| 0 1 0 | 
| 0 0 1 | 

questa matrice di rotazione rappresenta, ho solo bisogno di tracciare 3 frecce da [0, 0, 0] a (i) [1, 0, 0], (ii) [0, 1, 0] e (iii) [0, 0, 1]. Cioè, ho solo bisogno di tracciare le righe o le colonne della matrice (dipende anche da cosa vuoi che le righe o le colonne siano migliori).

Per ottenere la rotazione tra una matrice di rotazione salvato S e la matrice di rotazione corrente C è necessario calcolare C T S (in parole: C trasporre volte S). Hai allineato il telefono con l'atteggiamento salvato se la matrice di rotazione è quella mostrata sopra.

Un eccellente tutorial sulle matrici di rotazione è il manoscritto Direction Cosine Matrix IMU: Theory.

Un altro modo di visualizzare la rotazione tra l'atteggiamento salvato e quello corrente è trasformarlo in forma dell'asse dell'angolo. Quindi, proietterei l'asse sullo schermo del telefono. L'utente allinea dapprima il telefono con l'asse, che deve ruotare attorno all'asse per adattarsi all'atteggiamento salvato. I quaternioni rappresentano fondamentalmente la forma dell'angolo dell'asse, sebbene in modo non banale.


Richiede un ulteriore lavoro significativo da parte vostra per modificare questa idea in base alle proprie esigenze; questa non è sicuramente una risposta completa. Tuttavia, spero che questo aiuti un po '.

+0

Grazie mille per aver risposto. Questo mi sembra difficile da visualizzare, ma sicuramente provo. :) – iSeeker

+0

Sono riuscito a trovare una soluzione utilizzando quaternion, ma sicuramente proverò questo metodo e ti farò sapere. Grazie mille per il tuo supporto ... Non avrei mai pensato di poterlo fare ... Lo apprezzo davvero. – iSeeker

Problemi correlati