13 votes

Mouvement de l'appareil par rapport au monde - multiplyByInverseOfAttitude

Quelle est la bonne façon d'utiliser CMAttitude:multiplyByInverseOfAttitude ?

Dans le cas d'un appareil iOS5 posé à plat sur une table, après avoir démarré CMMotionManager avec :

CMMotionManager *motionManager = [[CMMotionManager alloc]init];
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:
    CMAttitudeReferenceFrameXTrueNorthZVertical];

Ensuite, les objets CMDeviceMotion sont obtenus :

CMDeviceMotion *deviceMotion = [motionManager deviceMotion];

Je m'attends à ce que [deviceMotion attitude] reflète la rotation de l'appareil par rapport au Nord géographique.

Par observation, [deviceMotion userAcceleration] indique l'accélération dans le cadre de référence de l'appareil. En d'autres termes, déplacer l'appareil d'un côté à l'autre (en le maintenant à plat sur la table) enregistre l'accélération dans l'axe des x. Le fait de tourner l'appareil de 90° (toujours à plat) et de le déplacer d'un côté à l'autre indique toujours une accélération sur l'axe x.

Quelle est la bonne façon de transformer [deviceMotion userAcceleration] pour obtenir une accélération nord-sud/est-ouest plutôt que gauche-droite/avant-arrière ?

CMAttitude multiplyByInverseOfAttitude semble inutile puisqu'un cadre de référence a déjà été spécifié et que la documentation n'indique pas clairement comment appliquer l'attitude à CMAcceleration.

24voto

Zaq Points 377

La question ne se serait pas posée si CMDeviceMotion disposait d'un accesseur pour l'accélération de l'utilisateur dans les coordonnées du cadre de référence. J'ai donc utilisé une catégorie pour ajouter la méthode requise :

Dans CMDeviceMotion+TransformToReferenceFrame.h :

#import <CoreMotion/CoreMotion.h>

@interface CMDeviceMotion (TransformToReferenceFrame)
-(CMAcceleration)userAccelerationInReferenceFrame;
@end

et dans CMDeviceMotion+TransformToReferenceFrame.m :

#import "CMDeviceMotion+TransformToReferenceFrame.h"

@implementation CMDeviceMotion (TransformToReferenceFrame)

-(CMAcceleration)userAccelerationInReferenceFrame
{
    CMAcceleration acc = [self userAcceleration];
    CMRotationMatrix rot = [self attitude].rotationMatrix;

    CMAcceleration accRef;
    accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
    accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
    accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;

    return accRef;
}

@end

et dans Swift 3

extension CMDeviceMotion {

    var userAccelerationInReferenceFrame: CMAcceleration {
        let acc = self.userAcceleration
        let rot = self.attitude.rotationMatrix

        var accRef = CMAcceleration()
        accRef.x = acc.x*rot.m11 + acc.y*rot.m12 + acc.z*rot.m13;
        accRef.y = acc.x*rot.m21 + acc.y*rot.m22 + acc.z*rot.m23;
        accRef.z = acc.x*rot.m31 + acc.y*rot.m32 + acc.z*rot.m33;

        return accRef;
    }
}

Désormais, le code qui utilisait auparavant [deviceMotion userAcceleration] peut utiliser [deviceMotion userAccelerationInReferenceFrame] à la place.

4voto

xmkevinchen Points 1098

Selon la documentation d'Apple, CMAttitude désigne l'orientation d'un corps par rapport à un cadre de référence donné. Et soit userAcceleration o gravity est la valeur de la trame de l'appareil. Ainsi, pour obtenir la valeur du cadre de référence. Nous devrions faire comme @Batti a dit

  1. prendre la matrice de rotation de l'attitude à chaque mise à jour.
  2. calculer la matrice inverse.
  3. multiplier la matrice inverse pour le vecteur UserAcceleration.

Voici la version Swift

import CoreMotion
import GLKit

extension CMDeviceMotion {

    func userAccelerationInReferenceFrame() -> CMAcceleration {

        let origin = userAcceleration
        let rotation = attitude.rotationMatrix
        let matrix = rotation.inverse()

        var result = CMAcceleration()
        result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
        result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
        result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;

        return result
    }

    func gravityInReferenceFrame() -> CMAcceleration {

        let origin = self.gravity
        let rotation = attitude.rotationMatrix
        let matrix = rotation.inverse()

        var result = CMAcceleration()
        result.x = origin.x * matrix.m11 + origin.y * matrix.m12 + origin.z * matrix.m13;
        result.y = origin.x * matrix.m21 + origin.y * matrix.m22 + origin.z * matrix.m23;
        result.z = origin.x * matrix.m31 + origin.y * matrix.m32 + origin.z * matrix.m33;

        return result
    }
}

extension CMRotationMatrix {

    func inverse() -> CMRotationMatrix {

        let matrix = GLKMatrix3Make(Float(m11), Float(m12), Float(m13), Float(m21), Float(m22), Float(m23), Float(m31), Float(m32), Float(m33))
        let invert = GLKMatrix3Invert(matrix, nil)

        return CMRotationMatrix(m11: Double(invert.m00), m12: Double(invert.m01), m13: Double(invert.m02),
                            m21: Double(invert.m10), m22: Double(invert.m11), m23: Double(invert.m12),
                            m31: Double(invert.m20), m32: Double(invert.m21), m33: Double(invert.m22))

    }

}

J'espère que cela vous aidera un peu

3voto

Batti Points 415

J'ai essayé de mettre en œuvre une solution après avoir lu l'article cité ci-dessus.

Les étapes sont les suivantes :

  • prendre la matrice de rotation de l'attitude à chaque mise à jour.
  • calculer la matrice inverse.
  • multiplier la matrice inverse pour le vecteur UserAcceleration.

le vecteur résultant sera la projection du vecteur.

-x nord, +x sud

-y east, +y weast

mon code n'est pas encore parfait, j'y travaille.

0voto

Batti Points 415

Le cadre de référence est lié à la valeur de l'attitude, regardez la valeur de l'attitude de l'angle de lacet ; Si vous n'utilisez pas de cadre de référence, lorsque vous démarrez votre application, cette valeur est toujours zéro, par contre si vous utilisez le cadre de référence CMAttitudeReferenceFrameXTrueNorthZVertical la valeur de lacet indique l'angle entre l'axe des x et le nord vrai. Cette information vous permet d'identifier l'attitude du téléphone dans les coordonnées de la terre et donc la position des axes de l'accéléromètre par rapport aux points cardinaux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X