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

Как получить ориентацию телефона Android, соответствующую человеческой ориентации?

Я создаю приложение карты, в том числе стрелку местоположения, которая показывает вам, с каким способом вы сталкиваетесь, например:

Map with a blue arrow

Я получаю ориентацию непосредственно из SensorManager.getOrientation(), используя первое возвращаемое значение: азимут. Когда телефон удерживается так, чтобы экран указывал над горизонтом, а на портрете стрелка отлично работала. Однако:

  • Когда телефон удерживается так, что экран указывает ниже горизонта, стрелка указывает на 180 градусов от направления, в котором находится пользователь.
  • Когда телефон удерживается так, чтобы экран выглядел ровно с горизонтом, стрелка не имеет понятия, как он указывает. Азимут не возвращает значимых результатов вообще.
  • Когда телефон наклонен влево или вправо (или удерживается в ландшафтном режиме), стрелка наклоняется влево или вправо.

Тщательно сконструированное и научное изображение ниже показывает, что я имею в виду (где синий - пользователь, красный - направление стрелки, экран примерно обращен к лицу пользователя, а Google Maps делает именно то, что я хочу):

Graph of what happens vs what I want

(Обратите внимание, что с помощью Google Maps он не выполняет последние два действия в списке, если автоматическое вращение выключено. Но я даже не на этом этапе. Одно за раз.)

Похоже, что он просто использует направление направления оси Y, как показано здесь: http://developer.android.com/reference/android/hardware/SensorEvent.html, когда я хочу, чтобы он использовал обратное направления направления оси Z, большую часть времени, и Y, когда телефон плоский. Однако, учитывая значения, возвращаемые getOrientation(), мне пришлось бы писать сложные случаи, чтобы исправить некоторые из проблем, и случай использования телефона с горизонтальным горизонтом неразрешимый. Я уверен, что есть более простой способ.

Здесь мой код (где lastAcceleration и lastMagneticField оба исходили от внутреннего датчика):

float[] rotationMatrix = new float[9];
if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){
    float[] orientMatrix = new float[3];
    SensorManager.getOrientation(rotationMatrix, orientMatrix);

    orientation = orientMat[0]*180/(float)Math.PI;
}

Что я делаю неправильно? Есть ли более простой способ сделать это?

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

4b9b3361

Ответ 1

Вы вызвали remapCoordinateSystem? В противном случае вы получаете только правильное значение, когда телефон держится вертикально. Для случая Когда телефон удерживается так, чтобы экран выглядел ровно с горизонтом, вы не можете заставить пользователя столкнуться. Поскольку для того, чтобы получить обратную сторону, вы должны проецировать значение z на показание датчика в плоскость xy в мировой координате, и оно равно нулю, когда устройство удерживается горизонтально.

Чтобы быть более точным, если вы хотите, чтобы телефон стоял, телефон должен быть наклонен по меньшей мере на 25 градусов от горизонтали, и вам нужно позвонить в remapCoordinateSystem. Следующий код даст вам то, что вы хотите для последних 2 изображений выше.
Код

float[] rotationMatrix = new float[9];  

if(SensorManager.getRotationMatrix(rotationMatrix, null, lastAcceleration, lastMagneticField)){
    float[] orientMatrix = new float[3];
    float remapMatrix = new float[9];
    SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapMatrix);
    SensorManager.getOrientation(remapMatrix, orientMatrix);

    orientation = orientMat[0]*180/(float)Math.PI;
}  

Функция getOrientation дает вам правильные значения при условии, что телефон лежит ровно. Таким образом, если телефон удерживается вертикально, вам необходимо переделать координату, чтобы получить плоскую позицию. Геометрически вы проецируете ось телефона -z вниз на мировую плоскость xy, а затем вычисляете угол между этим вектором проекции и мировой осью y.

Ответ 2

Это выглядит довольно сложно. Я разрабатываю PNS для Android и сталкиваюсь с какой-то аналогичной проблемой, для которой я все еще нуждаюсь в свете: Как получить поворот между осью акселерометра и вектором движения?

Дело в том, что мне абсолютно невозможно найти, в каком направлении находится пользователь (а не устройство), если он не движется. То есть у человека нет сенсора на его теле, так что, если устройство оставалось в том же положении, но пользователь поворачивался на 90 °? Я не вижу способа найти это.

Что я могу предложить (и это соответствует моей проблеме), так это то, что вы могли (я не знаю, что вы на самом деле делаете в своем коде) используют движение пользователя, чтобы определить его заголовок. Позволь мне объяснить. Скажем, у вас есть первая позиция A. Пользователь переходит к B. Затем вы можете построить вектор AB и получить заголовок пользователя, когда он остановится на B. Затем вам нужно будет ограничить свой код в направлении, с которым он сталкивается, когда прибывающих в пункт назначения.

Я знаю, что это не так хорошо, как Google Maps, но знаете ли вы, что Google использует для этого? Я имею в виду, что они используют только датчики ускорения и магнетика?

Ответ 3

Кажется, что подходящий способ получить опору, когда пользователь держит телефон вертикально, должен использовать что-то вроде этого:

// after calling getRotationMatrix pass the rotationMatix below:
SensorManager.remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);

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

Смотрите документацию API здесь.

Ответ 4

Вы должны взять поле и определить, находится ли пользователь близко к тому, чтобы держать телефон вертикально вверх.

Я выбрал, что после 45-градусного шага вверх или вниз от плоского на столе система координат должна быть переделана.

if (Math.round(Math.toDegrees(-orientation[1])) < 45 && Math.round(Math.toDegrees(-orientation[1])) > -45) {
//do something while the phone is horizontal
}else{
//R below is the original rotation matrix
float remapOut[] = new float[9];
SensorManager.remapCoordinateSystem(R, SensorManager.AXIS_X, SensorManager.AXIS_Z, remapOut);
//get the orientation with remapOut
float remapOrientation[] = new float[3];
SensorManager.getOrientation(remapOut, remapOrientation);

Это получилось неплохо. Дайте мне знать, может ли кто-нибудь предложить улучшить это. Спасибо.