Получение вектора направления в Android

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

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

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

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

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

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


person Joey    schedule 17.06.2012    source источник


Ответы (2)


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

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

deviceмир

Обратите внимание, что [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 и вычислить фактическое смещение.

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

person Gene    schedule 17.06.2012
comment
Большое спасибо, это действительно полезно. Вы оба дали такие хорошие ответы, я бы хотел, чтобы был способ выбрать их обоих. Похоже, я могу получить вектор гравитации только с помощью Sensor.TYPE_GRAVITY — они сделали это за меня. Хотел бы я, чтобы было так же легко получить северный вектор. - person Joey; 17.06.2012
comment
Я закончил тем, что получил это, используя ваше объяснение. Оказывается, я передаю значения, возвращенные датчиком гравитации и датчиком магнитного поля, непосредственно в getRotationMatrix(), и он все работает. Затем просто умножил эту матрицу на [0,0,-1] и получил dv. - person Joey; 17.06.2012

Прочтите эту страницу: 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;
    }
}
person Edward Falk    schedule 17.06.2012
comment
Спасибо за ваш ответ - у меня только два вопроса, если я использую TYPE_ROTATION_VECTOR, выражается ли квантернион в мировых координатах? Он уже интегрирован или мне нужно интегрировать его, чтобы получить текущий вектор? - person Joey; 17.06.2012
comment
Мировые координаты устройства, я полагаю. Уже преобразован в единицу кватерниона, и иногда термин w опускается. Честно говоря, я никогда не использовал его сам. Код, который я показываю выше, взят из навигационного приложения, которое я написал для версии 1.5, до того, как стали доступны новые типы датчиков. - person Edward Falk; 17.06.2012