Znajdowanie normalnego wektora na urządzenie z systemem iOS

Chciałbym użyć CMAttitude, aby poznać wektor normalny do szyby ekranu iPada/iPhone ' a (w stosunku do ziemi). Jako taki otrzymałbym wektory takie jak:

Tutaj wpisz opis obrazka

Zauważ, że to różni się od orientacji, w tym, że nie obchodzi mnie, jak urządzenie jest obracane wokół osi Z. Więc gdybym trzymał iPada nad głową zwróconą w dół, to czytałby (0, -1,0), a nawet gdy obracałem go wokół głowy (jak helikopter), nadal czytałby (0,-1,0):

Tutaj wpisz opis obrazka

Wydaje mi się, że to może być dość łatwe, ale ponieważ jestem nowy w quaternions i nie do końca rozumiem opcje ramki odniesienia dla ruchu urządzenia, to unikają mnie cały dzień.

Author: Francisco Ryan Tolmasky I, 2012-05-31

2 answers

  1. w Twoim przypadku możemy powiedzieć, że obrót urządzenia jest równy obrotowi urządzenia normalnego (obrót wokół samego normalnego jest po prostu ignorowany, tak jak go podałeś)
  2. CMAttitude które można uzyskać poprzez CMMotionManager.deviceMotion zapewnia rotację względem ramki odniesienia . Jego właściwości quaternion, roation macierze i kąty Eulera są po prostu różnymi reprezentacjami.
  3. ramka odniesienia może być określona podczas uruchamiania urządzenia aktualizacje ruchu przy użyciu metody CMMotionManager startDeviceMotionUpdatesUsingReferenceframe . Do iOS 4 trzeba było używać multiplebyinverseofattitude

Składając to razem, wystarczy pomnożyć kwaternion w we właściwy sposób z wektorem normalnym, gdy urządzenie leży twarzą do góry na stole. Teraz potrzebujemy właściwej drogi mnożenia czwartorzędowego, które reprezentuje obrót: zgodnie z wektorami obrotowymi jest to zrobione autor:

N = q * e * q' gdzie q jest kwaternionem dostarczonym przez CMAttitude [W, (x, y, z)], q' jest jego koniugatem [w, (-x, -y, -z)] i e jest czwartorzędową reprezentacją twarzy normalnej [0, (0, 0, 1)]. Niestety CMQuaternion Apple jest strukturą i dlatego potrzebujesz małej klasy pomocniczej.

Quaternion e = [[Quaternion alloc] initWithValues:0 y:0 z:1 w:0];
CMQuaternion cm = deviceMotion.attitude.quaternion;
Quaternion quat = [[Quaternion alloc] initWithValues:cm.x y:cm.y z:cm.z w: cm.w];
Quaternion quatConjugate = [[Quaternion alloc] initWithValues:-cm.x y:-cm.y z:-cm.z w: cm.w];
[quat multiplyWithRight:e];
[quat multiplyWithRight:quatConjugate];
// quat.x, .y, .z contain your normal

Quaternion.h:

@interface Quaternion : NSObject {
    double w;
    double x;
    double y;
    double z;
}

@property(readwrite, assign)double w;
@property(readwrite, assign)double x;
@property(readwrite, assign)double y;
@property(readwrite, assign)double z;

Quaternion.m:

- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = w*q.w - x*q.x - y*q.y - z*q.z;
    double newX = w*q.x + x*q.w + y*q.z - z*q.y;
    double newY = w*q.y + y*q.w + z*q.x - x*q.z;
    double newZ = w*q.z + z*q.w + x*q.y - y*q.x;
    w = newW;
    x = newX;
    y = newY;
    z = newZ;
    // one multiplication won't denormalise but when multipling again and again 
    // we should assure that the result is normalised
    return self;
}

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 {
        if ((self = [super init])) {
            x = x2; y = y2; z = z2; w = w2;
        }
        return self;
}

Wiem, że quaternions są trochę dziwne na początku, ale kiedy już mam pomysł są naprawdę genialne. To pomogło mi wyobrazić sobie quaternion jako obrót wokół wektora (x, y, z) i w jest (cosinus) kąta.

Jeśli chcesz zrobić z nimi więcej, spójrz na cocoamath projekt open source. Klasa Quaternion i jej rozszerzenie QuaternionOperations są dobrym punktem wyjścia.

Ze względu na kompletność, tak można to zrobić z mnożeniem macierzy, jak również:

N = M * E

Ale Ja wolałby quaternion sposób oszczędza Ci wszystkie problemy trygonometryczne i działa lepiej.

 22
Author: Kay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 16:59:55

Podziękowania dla Kay za punkt wyjścia do rozwiązania. Oto moja realizacja dla każdego, kto jej potrzebuje. Zrobiłem kilka małych tweeksów do Rady Kay w mojej sytuacji. Jako ostrzeżenie, używam prezentacji tylko w krajobrazie. Mam kod, który aktualizuje zmienną _isLandscapeLeft, aby dokonać niezbędnego dostosowania do kierunku wektora.

Quaternion.h

    @interface Quaternion : NSObject{
    //double w;
    //double x;
    //double y;
    //double z;
}

@property(readwrite, assign)double w;
@property(readwrite, assign)double x;
@property(readwrite, assign)double y;
@property(readwrite, assign)double z;

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2;
- (Quaternion*) multiplyWithRight:(Quaternion*)q;
@end

Quaternion.m

#import "Quaternion.h"

@implementation Quaternion


- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = _w*q.w - _x*q.x - _y*q.y - _z*q.z;
    double newX = _w*q.x + _x*q.w + _y*q.z - _z*q.y;
    double newY = _w*q.y + _y*q.w + _z*q.x - _x*q.z;
    double newZ = _w*q.z + _z*q.w + _x*q.y - _y*q.x;
    _w = newW;
    _x = newX;
    _y = newY;
    _z = newZ;
    // one multiplication won't denormalise but when multipling again and again
    // we should assure that the result is normalised
    return self;
}

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 {
    if ((self = [super init])) {
        _x = x2; _y = y2; _z = z2; _w = w2;
    }
    return self;
}


@end

I moja klasa gry, która używa quaternionu do strzelectwo: {]}

-(void)fireWeapon{
    ProjectileBaseClass *bullet = [[ProjectileBaseClass alloc] init];
    bullet.position = SCNVector3Make(0, 1, 0);
    [self.rootNode addChildNode:bullet];

    Quaternion *e = [[Quaternion alloc] initWithValues:0 x:0 y:0 z:1];
    CMQuaternion cm = _currentAttitude.quaternion;
    Quaternion *quat = [[Quaternion alloc] initWithValues:cm.w x:cm.x y:cm.y z:cm.z];
    Quaternion *quatConjugate = [[Quaternion alloc] initWithValues:cm.w x:-cm.x y:-cm.y z:-cm.z];
    quat = [quat multiplyWithRight:e];
    quat = [quat multiplyWithRight:quatConjugate];
    SCNVector3 directionToShoot;
    if (_isLandscapeLeft) {
        directionToShoot = SCNVector3Make(quat.y, -quat.x, -quat.z);

    }else{
        directionToShoot = SCNVector3Make(-quat.y, quat.x, -quat.z);

    }

    SCNAction *shootBullet = [SCNAction moveBy:directionToShoot duration:.1];
    [bullet runAction:[SCNAction repeatActionForever:shootBullet]];
}
 3
Author: Metalboyblue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-13 11:25:30