Подтвердить что ты не робот

Как нарисовать вектор направления в Android

Как я могу получить вектор направления, указывающий направление, которое обратная сторона устройства указывает на координаты Земли?

Например, если место на столе (экран вверх), оно должно читать [0,0, -1], и если оно вертикально обращено к северу, оно должно читать [1,0,0] и т.д.

Я знаю, как вычислить его из заголовка, тангажа и рулона, если они относятся к земным координатам. Чтобы быть ясным, я не ищу скорость angular, а фактический угол тока относительно плоскости, касательной к Земле. Поэтому, если устройство удерживается вертикально и обращено на север, угол "альфа" должен быть равен 0 или 360, угол "бета" должен быть равен 90, а "гамма" должна быть равна 0. Я не могу понять, как получить эти значения либо.

Я читал API весь день, и я все еще не могу найти, как получить любую из этих вещей.

public void onSensorChanged(SensorEvent event) {
    // ?    
}

Спасибо за любые идеи.

4b9b3361

Ответ 1

SensorManager.getRotationMatrix() делает то, что описано ниже, написанное до того, как я узнал об этом. Я оставлю дополнительное объяснение, потому что, если вы хотите исправить разницу между магнитным и истинным севером, вам все равно понадобится.

Грубый алгоритм состоит в том, чтобы получить матрицу вращения, умножить на нее вектор [0,0,-1], а затем скорректировать это на вашу систему координат. Зачем? Android docs предоставляют системы координат для устройства и мира

deviceworld

Примечание [0,0,-1] в Android-устройствах координаты точек перпендикулярны назад от экрана. Если вы умножите матрицу вращения R на этот вектор, вы получите [0,0,-1] в мировых коордах, когда устройство будет стоять на столе, как вам хочется. Когда он вертикально обращен на север, вы получите [0,-1,0], что означает, что вы выбрали систему координат, где x и y меняются местами по отношению к системе Android, но это просто изменение условных обозначений.

Примечание R * [0,0,-1]^T является только третьим столбцом R отрицательным. Из этого я получаю псевдокод:

getRotationMatrix(R);
Let v = first three elements of third column of R.
swap v[0] and v[1]

Это должно получить то, что вы хотите.

Дополнительная информация о том, что делает getRotationMatrix(), следует.


Вам нужны данные аксерометра, чтобы установить направление "вниз" и данные магнитометра, чтобы определить направление "север". Вам придется предположить, что акселерометры воспринимают только гравитацию (устройство неподвижно или движется с постоянной скоростью). Затем вам нужно проецировать вектор магнитометра на плоскость, перпендикулярную гравитационному вектору (поскольку магнитное поле обычно не касается поверхности Земли). Это дает вам две оси. Третий ортогонален, поэтому его можно вычислить с помощью перекрестного произведения. Это дает вам координаты координат земли в системе устройства. Похоже, вы хотите, чтобы координаты обратного: устройства в координатах земли. Для этого просто построим матрицу направляющих косинусов и инвертируем.

Я добавлю, что приведенное выше обсуждение предполагает, что вектор магнитометра указывает на север. Я думаю (из науки о высшей школе!) Он фактически направлен к магнитному югу, но не имеет устройства под рукой, поэтому не может его попробовать. Разумеется, магнитный север/юг отличается от истинного от нуля до 180 градусов в зависимости от того, где вы находитесь на земле. Вы можете получить GPS-координаты и вычислить фактическое смещение.

Если вы не знакомы с математикой, необходимой для этого, я могу объяснить дальше, но это должно быть позже.

Ответ 2

Прочтите эту страницу: http://developer.android.com/reference/android/hardware/Sensor.html

В API 8 и выше существуют "виртуальные" датчики, которые генерируются путем объединения входов всех доступных датчиков и соответствующих фильтров. Датчик "TYPE_ORIENTATION" дает вам всю направленность вашего устройства, но этот интерфейс устарел из-за состояний отказа при определенных ориентациях. Новый датчик - TYPE_ROTATION_VECTOR (API 9 и выше), который дает ориентацию вашего устройства как кватернион. Это действительно лучший датчик для использования, но математика за ним немного тяжелая.

В противном случае вы вызываете SensorManager.getRotationMatrix(), передавая последние данные о гравитации и магнитометре. Это вернет матрицу вращения, которая может быть использована для преобразования вектора из координат устройства в мировые координаты или наоборот (просто транспонируйте матрицу, чтобы инвертировать ее).

Функция getOrientation() может дать вам заголовок, шаг и рулон, но они имеют те же самые состояния отказа, что и датчик TYPE_ORIENTATION.

Examples:

  Device flat on a table, top facing north:
    1  0  0
    0  1  0
    0  0  1

  Tilted up 30 degrees (rotated about X axis)
    1   0      0
    0   0.86  -0.5
    0   0.5    0.86

  Device vertical (rotated about X axis), facing north:
    1  0  0
    0  0 -1
    0  1  0

  Device flat on a table, top facing west:
    0 -1  0
    1  0  0
    0  0  1

  Device rotated about its Y axis, onto its left side, top
  facing north:
    0  0 -1
    0  1  0
    1  0  0

Вот пример кода, который может оказаться полезным:

public void onSensorChanged(SensorEvent event) {
    long now = event.timestamp;     // ns

    switch( event.sensor.getType() ) {
      case Sensor.TYPE_ACCELEROMETER:
        gData[0] = event.values[0];
        gData[1] = event.values[1];
        gData[2] = event.values[2];
        break;
      case Sensor.TYPE_MAGNETIC_FIELD:
        mData[0] = event.values[0];
        mData[1] = event.values[1];
        mData[2] = event.values[2];
        haveData = true;
        break;
    }

    if( haveData ) {
        double dt = (now - last_time) * .000000001;

        SensorManager.getRotationMatrix(R1, Imat, gData, mData);
        getOrientation(R1, orientation);
        pfdView.newOrientation(orientation[2], (float)dt);


        Log.d(TAG, "yaw: " + (int)(orientation[0]*DEG));
        Log.d(TAG, "pitch: " + (int)(orientation[1]*DEG));
        Log.d(TAG, "roll: " + (int)(orientation[2]*DEG));

        acft.compass = orientation[0];

        last_time = now;
    }
}