Получение Y поворота ARKit pointOfView

Я пытаюсь получить реальный угол обзора в сцене ARKit (0 - 360 градусов). Я использую углы Эйлера из SCNNode в pointOfView.

print("\(pointOfView.eulerAngles.y.radiansToDegrees)")

Проблема в том, что когда я смотрю на север, я получаю 0 в результате, а когда смотрю на юг, я также получаю 0. Когда я смотрю на северо-восток, я получаю -45 градусов, а когда я смотрю на юго-восток, я также получаю -45 градусов. Похоже, что SCNNode не может определять между Севером и Югом, только между Западом и Востоком. Любой совет?

Обычно мне нужно реализовать радарный обзор в моей сцене реального мира ARKit. Ожидаемое поведение: север: 0, восток: 90, юг: 180, запад: 270.

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


person awolCZ    schedule 16.04.2018    source источник
comment
вы получили какое-нибудь решение ??   -  person Balaj Narasimhan    schedule 13.07.2018


Ответы (1)


Я только что работал над похожей ситуацией. То, что вы делаете после, я называю «заголовком», который не так легко определить четко, как вы думаете.

Краткая справка: к вашему сведению, есть два типа вращения, «Эйлера», которые относятся к пространству реального мира, но страдают от того, что они называют Gimbal Lock на крайних «шагах». И затем есть углы поворота относительно оси устройства, хранящиеся в свойстве преобразования ARCamera.

Чтобы проиллюстрировать разницу, euler.y всегда означает, как устройство обращено (кроме случаев, когда оно плоское, и в этом случае блокировка карданного вала сбивает его, отсюда и наша проблема), тогда как преобразование y всегда означает вращение вокруг вертикальной оси через телефон (что, просто чтобы сделать вещи еще более запутанными, основан на устройстве, удерживаемом ландшафтным в ARKit).

(Примечание: если вы привыкли к CoreMotion, вы могли заметить, что в ARKit Gimbal Lock происходит, когда устройство находится в горизонтальном положении, тогда как в CM оно находится в вертикальном положении).

Итак, как получить «заголовок», который работает независимо от того, является ли устройство плоским или вертикальным? Приведенное ниже решение (извините, это objective-c!) Делает следующее:

  1. Возьмите два вектора нормали, один вдоль оси Z телефона (прямо из экрана) и другой, который выступает из нижней части телефона, который я называю осью -Y (хотя на самом деле это ось + X, когда удерживается альбомная ориентация).

  2. Поверните вектор с помощью преобразования устройства (не Эйлера), спроецируйте его на плоскость XZ и получите угол проецируемых векторов относительно оси Z.

  3. Когда телефон находится в вертикальном положении, идеальным заголовком будет Z Normal, но когда телефон плоский, лучше всего использовать Y normal. В промежутках мы будем «плавно затухать» на основе «наклона» телефона, то есть euler.x.

  4. Одна небольшая проблема заключается в том, что когда пользователь держит телефон немного ниже плоскости, направление, указанное в Z Normal, переворачивается. На самом деле мы этого не хотим (больше с точки зрения UX, чем с математической), поэтому давайте обнаружим этот «наклон вниз» и перевернем zHeading 180˚, когда это произойдет.

Конечный результат - стабильная и плавная heading независимо от ориентации устройства. Это даже работает, когда устройство меняют, перемещая между портретной и альбомной ориентацией ... Ура!

// Create a Quaternion representing the devices curent rotation (NOT the same as the euler angles!)
GLKMatrix3 deviceRotM = GLKMatrix4GetMatrix3(SCNMatrix4ToGLKMatrix4(SCNMatrix4FromMat4(camera.transform)));
GLKQuaternion Q = GLKQuaternionMakeWithMatrix3(deviceRotM);

// We want to use the phone's Z normal (in the phone's reference frame) projected onto XZ to get the angle when the phone is upright BUT the Y normal when it's horizontal. We'll crossfade between the two based on the phone tilt (euler x)...
GLKVector3 phoneZNormal = GLKQuaternionRotateVector3(Q, GLKVector3Make(0, 0, 1));
GLKVector3 phoneYNormal = GLKQuaternionRotateVector3(Q, GLKVector3Make(1, 0, 0)); // why 1,0,0? Rotation=(0,0,0) is when the phone is landscape and upright. We want the vector that will point to +Z when the phone is portrait and flat

float zHeading = atan2f(phoneZNormal.x, phoneZNormal.z);
float yHeading = atan2f(phoneYNormal.x, phoneYNormal.z);

// Flip the zHeading if phone is tilting down, ie. the normal pointing down the device suddenly has a +y component
BOOL isDownTilt = phoneYNormal.y > 0;
if (isDownTilt) {
    zHeading = zHeading + M_PI;
    if (zHeading > M_PI) {
        zHeading -= 2 * M_PI;
    }
}

float a = fabs(camera.eulerAngles.x / M_PI_2);
float heading = a * yHeading + (1 - a) * zHeading;

NSLog(@"euler: %3.1f˚   %3.1f˚   %3.1f˚    zHeading=%3.1f˚    yHeading=%3.1f˚    heading=%3.1f˚    a=%.2f    status:%li:%li  zNorm=(%3.2f, %3.2f, %3.2f)    yNorm=(%3.2f, %3.2f, %3.2f)", GLKMathRadiansToDegrees(camera.eulerAngles.x), GLKMathRadiansToDegrees(camera.eulerAngles.y), GLKMathRadiansToDegrees(camera.eulerAngles.z), GLKMathRadiansToDegrees(zHeading), GLKMathRadiansToDegrees(yHeading), GLKMathRadiansToDegrees(heading), a, camera.trackingState, camera.trackingStateReason, phoneZNormal.x, phoneZNormal.y, phoneZNormal.z, phoneYNormal.x, phoneYNormal.y, phoneYNormal.z);
person Hari Honor    schedule 19.07.2018
comment
Есть идеи, как преобразовать simd_float4x4 / matrix_float4x4 в GLKMatrix4? О - person Sam Bing; 31.07.2018