iOS CoreMotion CMAttitude относительно северного полюса - PullRequest
2 голосов
/ 03 марта 2011

В настоящее время я использую DeviceMotion CoreMotion для определения ориентации (крена, тангажа, рыскания) iPhone. Теперь я хотел бы иметь эти значения относительно географического северного полюса; поэтому мне нужен эталонный объект CMAttitude, содержащий значения крена, тангажа и рыскания, о которых будет сообщено, если задняя часть iPhone будет обращена к северному полюсу (3D). CLLocationManager возвращает только магнитный курс (x, y, z) в Тесле.

У вас есть идея, как преобразовать эти значения в крен, тангаж и рыскание?

Заранее спасибо,

Александр

Ответы [ 4 ]

3 голосов
/ 23 июня 2011

iOS 5 предоставляет назначенный метод.Найдите CMAttitudeReferenceFrameXTrueNorthZVertical в документации разработчика.

1 голос
/ 27 мая 2012

Псевдокод:

  1. запуск обновлений движения устройства
  2. запуск предварительного просмотра камеры в фоновом режиме;)
  3. захват текущего значения силы тяжести с устройства в видеCMAcceleration ... как только вы получите гравитацию, сохраните ее в локальной переменной.
  4. Затем вы должны взять 2 вектора и получить угол между ними, в этом случае гравитация устройства (0,0, -1) и вектор реальной гравитации ...
  5. затем мы превращаем тэту в thetaPrime ... преобразование, которое соответствует эталонной ориентации CoreMotion
  6. Настройка таймера для анимации ....
  7. во время анимации получите инверсию вращенияMatrix свойства deviceMotion motionManager.
  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;
}
0 голосов
/ 25 августа 2011

Вам нужно будет время от времени калибровать значение рыскания по магнитному курсу, чтобы убедиться, что вы находитесь на правильном пути. Посмотрите это объяснение о том, как компенсировать дрожание компаса: Компенсация задержки компаса с помощью гироскопа на iPhone 4

0 голосов
/ 03 марта 2011

Значения Тесла - это напряженность магнитного поля, мера того, как сильно ощущается магнитное «притяжение» в каждой из трех осей. Только комбинируя эту информацию с данными акселерометра и выполняя кучу причудливой математики, вы можете получить реальный курс (в каком направлении устройство «указывает» относительно магнитного севера). Затем добавьте информацию из GPS и сделайте больше математических операций, чтобы получить истинный курс (относительно географического северного полюса).

Короче говоря, вы, вероятно, не хотите делать математику самостоятельно. К счастью, iOS предоставляет и MagnheHeading, и TrueHeading в своем объекте CLHeading, доступном из свойства заголовка CLLocationManager.

Чтобы получить высоту звука, которая описывает наклон устройства, также необходимо выполнить математику с теми же необработанными данными из магнитометра и акселерометра. Извините, я не знаю ни одного iOS API для игры в тон.

...