Как преобразовать вектор магнитного поля из координатного пространства датчика в координатное пространство камеры? - PullRequest
0 голосов
/ 16 октября 2019

Я хотел бы собрать вектор магнитного поля (т. Е. X, y, z в микротесла) из датчика положения TYPE_MAGNETIC_FIELD и поместить его в ту же систему координат, что и Рамка мы получаем из ARCore.

Вектор магнитного поля находится в системе координат датчика . Нам нужно включить его в систему координат камеры. Я полагаю, что могу использовать следующие два API, которые предоставляются в каждом кадре камеры (показывая версию NDK, мне больше нравятся документы):

  • getAndroidSensorPose () - Получаетположение системы координат датчика Android в мировом координатном пространстве для этого кадра. Я понимаю, что это «SensorToWorldPose».
  • getPose () - Получает позу физической камеры в мировом пространстве для последнего кадра. Я понимаю, что это «CameraToWorldPose».

Ниже я вычисляю магнитный вектор в системе координат камеры (magnetVectorInCamera). Когда я проверяю его (передавая слабый магнит вокруг телефона и сравнивая его с необработанными значениями x, y, z CLHeading для iOS , я не получаю ожидаемых значений. Есть предложения?

scene.addOnUpdateListener(frameTime -> { processFrame(this.sceneView.getArFrame()) });

public void processFrame(Frame frame) {
    if (frame.getCamera().getTrackingState() != TrackingState.TRACKING) {
        return;
    }

    // Get the magnetic vector in sensor that we stored in the onSensorChanged() delegate
    float[] magneticVectorInSensor = {x,y,z};

    // Get sensor to world
    Pose sensorToWorldPose = frame.getAndroidSensorPose();

    // Get world to camera
    Pose cameraToWorldPose = frame.getCamera().getPose();
    Pose worldToCameraPose = cameraToWorldPose.inverse();

    // Get sensor to camera
    Pose sensorToCameraPose = sensorToWorldPose.compose(worldToCameraPose);

    // Get the magnetic vector in camera coordinate space
    float[] magneticVectorInCamera = sensorToCameraPose.rotateVector(magneticVectorInSensor);
}

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
    int sensorType = sensorEvent.sensor.getType();

    switch (sensorType) {
        case Sensor.TYPE_MAGNETIC_FIELD:
            mMagnetometerData = sensorEvent.values.clone();
            break;
        default:
            return;
    }

    x = mMagnetometerData[0];
    y = mMagnetometerData[1];
    z = mMagnetometerData[2];
}

Вот пример строки журнала, которую я получаю из этого:

V/processFrame: magneticVectorInSensor: [-173.21014, -138.63983, 54.873657]
V/processFrame: sensorToWorldPose: t:[x:-1.010, y:-0.032, z:-0.651], q:[x:-0.28, y:-0.62, z:-0.21, w:0.71]
V/processFrame: cameraToWorldPose: t:[x:-0.941, y:0.034, z:-0.610], q:[x:-0.23, y:0.62, z:0.66, w:-0.35]
V/processFrame: worldToCameraPose: t:[x:-0.509, y:0.762, z:-0.647], q:[x:0.23, y:-0.62, z:-0.66, w:-0.35]
V/processFrame: sensorToCameraPose: t:[x:-0.114, y:0.105, z:-1.312], q:[x:0.54, y:-0.46, z:-0.08, w:-0.70]
V/processFrame: magneticVectorInCamera: [15.159668, 56.381603, 220.96408]

Одна вещь, которая меня смущает - это то, почему моя поза sensoryToCamera меняется при перемещении телефона:

sensorToCameraPose: t:[x:0.068, y:-0.014, z:0.083], q:[x:0.14, y:-0.65, z:-0.25, w:-0.70]
sensorToCameraPose: t:[x:0.071, y:-0.010, z:0.077], q:[x:0.11, y:-0.66, z:-0.23, w:-0.70]
sensorToCameraPose: t:[x:0.075, y:-0.007, z:0.070], q:[x:0.08, y:-0.68, z:-0.20, w:-0.70]
sensorToCameraPose: t:[x:0.080, y:-0.007, z:0.061], q:[x:0.05, y:-0.69, z:-0.18, w:-0.70]
sensorToCameraPose: t:[x:0.084, y:-0.008, z:0.052], q:[x:0.01, y:-0.69, z:-0.17, w:-0.70]
sensorToCameraPose: t:[x:0.091, y:-0.011, z:0.045], q:[x:-0.03, y:-0.69, z:-0.17, w:-0.70]
sensorToCameraPose: t:[x:0.094, y:-0.017, z:0.037], q:[x:-0.09, y:-0.69, z:-0.17, w:-0.70]
sensorToCameraPose: t:[x:0.098, y:-0.026, z:0.027], q:[x:-0.16, y:-0.67, z:-0.17, w:-0.70]
sensorToCameraPose: t:[x:0.100, y:-0.037, z:0.020], q:[x:-0.23, y:-0.65, z:-0.19, w:-0.70]
sensorToCameraPose: t:[x:0.098, y:-0.046, z:0.012], q:[x:-0.30, y:-0.62, z:-0.20, w:-0.70]
sensorToCameraPose: t:[x:0.096, y:-0.055, z:0.005], q:[x:-0.35, y:-0.59, z:-0.19, w:-0.70]
sensorToCameraPose: t:[x:0.092, y:-0.061, z:-0.003], q:[x:-0.41, y:-0.56, z:-0.18, w:-0.70]
sensorToCameraPose: t:[x:0.086, y:-0.066, z:-0.011], q:[x:-0.45, y:-0.52, z:-0.17, w:-0.70]
sensorToCameraPose: t:[x:0.080, y:-0.069, z:-0.018], q:[x:-0.49, y:-0.49, z:-0.16, w:-0.70]
sensorToCameraPose: t:[x:0.073, y:-0.071, z:-0.025], q:[x:-0.53, y:-0.45, z:-0.15, w:-0.70]
sensorToCameraPose: t:[x:0.065, y:-0.072, z:-0.031], q:[x:-0.56, y:-0.42, z:-0.13, w:-0.70]
sensorToCameraPose: t:[x:0.059, y:-0.072, z:-0.038], q:[x:-0.59, y:-0.38, z:-0.13, w:-0.70]
sensorToCameraPose: t:[x:0.053, y:-0.071, z:-0.042], q:[x:-0.61, y:-0.35, z:-0.12, w:-0.70]
sensorToCameraPose: t:[x:0.047, y:-0.069, z:-0.046], q:[x:-0.63, y:-0.32, z:-0.11, w:-0.70]
sensorToCameraPose: t:[x:0.041, y:-0.067, z:-0.048], q:[x:-0.64, y:-0.28, z:-0.10, w:-0.70]
sensorToCameraPose: t:[x:0.037, y:-0.064, z:-0.050], q:[x:-0.65, y:-0.26, z:-0.10, w:-0.70]
sensorToCameraPose: t:[x:0.032, y:-0.060, z:-0.052], q:[x:-0.67, y:-0.23, z:-0.09, w:-0.70]
sensorToCameraPose: t:[x:0.027, y:-0.057, z:-0.054], q:[x:-0.68, y:-0.20, z:-0.08, w:-0.70]

Примечание. Есть несколько других вопросов о преобразовании вектора магнитного поля в глобальное координатное пространство (т. Е. this и this ), но я не смогнайти что-нибудь для перехода в пространство координат камеры.

1 Ответ

0 голосов
/ 22 октября 2019

Были две проблемы с моим кодом выше.

Во-первых, я неправильно использовал compose. Чтобы преобразовать A, а затем B, выполните B.compose (A). С этим исправлением я начал получать согласованные значения sensorToCameraPose.

Во-вторых, после этого исправления у меня был поворот на 90 ° между x и y. Из u / inio в Reddit:

Так что обычно для устройств с форм-фактором телефона будет поворот на 90 ° между системой координат камеры (которая определена, чтобы иметь точку + x в области видимостигоризонтальная ось изображения физической камеры, обычно это длинная ось устройства) и система координат датчика Android (которая имеет + y, направленную в сторону от навигационных кнопок Android, и + x, таким образом, вдоль короткой оси устройства). Разница, которую вы описываете, - это поворот на 88.8 °. Может быть, вы хотите виртуальную позу камеры? Источник

Я тестировал с использованием getDisplayOrientedPose (). Благодаря этому я получаю то, что ожидаю, когда нахожусь в портретном режиме. Но если я переключаюсь в ландшафт, то система координат меняется, и меня отключает поворот на 90 °. Так что я вместо этого сделал вращение сам.

public void processFrame(Frame frame) {
    if (frame.getCamera().getTrackingState() != TrackingState.TRACKING) {
        return;
    }

    // Get the magnetic vector in sensor that we stored in the onSensorChanged() delegate
    float[] magneticVectorInSensor = {x,y,z};

    // Get sensor to world
    Pose sensorToWorldPose = frame.getAndroidSensorPose();

    // Get camera to world
    Pose cameraToWorldPose = frame.getCamera().getPose();

    // +90° rotation about Z
    // https://github.com/google-ar/arcore-android-sdk/issues/535#issuecomment-418845833
    Pose CAMERA_POSE_FIX = Pose.makeRotation(0, 0, ((float) Math.sqrt(0.5f)), ((float) Math.sqrt(0.5f))); 
    Pose rotatedCameraToWorldPose = cameraToWorldPose.compose(CAMERA_POSE_FIX);

    // Get world to camera
    Pose worldToCameraPose = rotatedCameraToWorldPose.inverse();

    // Get sensor to camera
    Pose sensorToCameraPose = worldToCameraPose.compose(sensorToWorldPose);

    // Get the magnetic vector in camera coordinate space
    float[] magneticVectorInCamera = sensorToCameraPose.rotateVector(magneticVectorInSensor);
}
...