Проблема с наклоном и вращением Android

Я работаю над наклонным приложением для Android. У меня проблема с портретным и альбомным режимами. При тангаже = 90 градусов (телефон торцом) и даже чуть раньше значение крена сходит с ума, когда физического изменения крена не произошло. Я не смог найти решение этой проблемы. Если кто-нибудь может указать мне в правильном направлении, это будет оценено.

Вот краткий дамп кода, чтобы вы знали, что это не ошибка акселерометра.

final SensorEventListener mEventListener = new SensorEventListener(){
     public void onAccuracyChanged(Sensor sensor, int accuracy) {}  
 public void onSensorChanged(SensorEvent event) {
     setListners(sensorManager, mEventListener);

      SensorManager.getRotationMatrix(mRotationMatrix, null, mValuesAccel, mValuesMagnet);
     SensorManager.getOrientation(mRotationMatrix, mValuesOrientation);


        synchronized (this) {

            switch (event.sensor.getType()){
                case Sensor.TYPE_ACCELEROMETER:

                    System.arraycopy(event.values, 0, mValuesAccel, 0, 3);

                    long actualTime = System.currentTimeMillis();

                    //Sensitivity delay
                    if (actualTime - lastUpdate < 250) {
                        return;
                        }
                    else {
                        sysAzimuth = (int)Math.toDegrees(mValuesOrientation[0]);
                        sysPitch = (int)Math.toDegrees(mValuesOrientation[1]);
                        sysRoll = (int)Math.toDegrees(mValuesOrientation[2]);

                        //invert direction with -1
                      pitch = (sysPitch - pitchCal)*-1;
                      roll = (sysRoll - rollCal);
                      azimuth = sysAzimuth;

                    lastUpdate = actualTime;
                    }

person user1234051    schedule 26.02.2012    source источник


Ответы (4)


Я нашел то, что искал, Вращательные матрицы.

Я использовал углы Эйлера (крен, тангаж, рыскание) для тангажа и крена. Когда телефон поворачивается на 90 градусов, плоскости x и z совпадают, и телефон сходит с ума, что является фундаментальным недостатком углов Эйлера.

Мне нужно получить градусы тангажа и крена, используя матрицы вращения через getRotationMatrix

Вот это для всех ;)

XML:

<?xml version="1.0" encoding="utf-8"?>
<!-- This file is res/layout/main.xml -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button android:id="@+id/update" android:text="Update Values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doUpdate" />
<Button android:id="@+id/show" android:text="Show Me!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doShow" android:layout_toRightOf="@id/update" />
<TextView android:id="@+id/preferred" android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/update" />
<TextView android:id="@+id/orientation" android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/preferred" />
</RelativeLayout>

Код:

package YOURPACKAGE;



import android.app.Activity;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;


public class YOURCLASS extends Activity implements SensorEventListener {
private static final String TAG = "VirtualJax";
private SensorManager mgr;
private Sensor accel;
private Sensor compass;
private Sensor orient;
private TextView preferred;
private TextView orientation;
private boolean ready = false;
private float[] accelValues = new float[3];
private float[] compassValues = new float[3];
private float[] inR = new float[9];
private float[] inclineMatrix = new float[9];
private float[] orientationValues = new float[3];
private float[] prefValues = new float[3];
private float mAzimuth;
private double mInclination;
private int counter;
private int mRotation;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    preferred = (TextView)findViewById(R.id.preferred);
    orientation = (TextView)findViewById(R.id.orientation);
    mgr = (SensorManager) this.getSystemService(SENSOR_SERVICE);
    accel = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    compass = mgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
    orient = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION);
    WindowManager window = (WindowManager) this.getSystemService(WINDOW_SERVICE);
    int apiLevel = Integer.parseInt(Build.VERSION.SDK);
    if(apiLevel <8) {
        mRotation = window.getDefaultDisplay().getOrientation();
    }
    else {
        mRotation = window.getDefaultDisplay().getRotation();
    }
}

@Override
protected void onResume() {
    mgr.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME);
    mgr.registerListener(this, compass, SensorManager.SENSOR_DELAY_GAME);
    mgr.registerListener(this, orient, SensorManager.SENSOR_DELAY_GAME);
    super.onResume();
}

@Override
protected void onPause() {
    mgr.unregisterListener(this, accel);
    mgr.unregisterListener(this, compass);
    mgr.unregisterListener(this, orient);
    super.onPause();
}

public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // ignore
}

public void onSensorChanged(SensorEvent event) {
    // Need to get both accelerometer and compass
    // before we can determine our orientationValues
    switch(event.sensor.getType()) {
        case Sensor.TYPE_ACCELEROMETER:
            for(int i=0; i<3; i++) {
                accelValues[i] = event.values[i];
            }
            if(compassValues[0] != 0)
                ready = true;
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            for(int i=0; i<3; i++) {
                compassValues[i] = event.values[i];
            }
            if(accelValues[2] != 0)
                ready = true;
            break;
        case Sensor.TYPE_ORIENTATION:
            for(int i=0; i<3; i++) {
                orientationValues[i] = event.values[i];
            }
            break;
    }

    if(!ready)
        return;
    if(SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)) {
        // got a good rotation matrix
        SensorManager.getOrientation(inR, prefValues);
        mInclination = SensorManager.getInclination(inclineMatrix);
        // Display every 10th value
        if(counter++ % 10 == 0) {
            doUpdate(null);
            counter = 1;
        }

    }
}

public void doUpdate(View view) {
    if(!ready)
        return;
    mAzimuth = (float) Math.toDegrees(prefValues[0]);
    if(mAzimuth < 0) {
        mAzimuth += 360.0f;
    }
    String msg = String.format(
            "Preferred:\nazimuth (Z): %7.3f \npitch (X): %7.3f\nroll (Y): %7.3f",
            mAzimuth, Math.toDegrees(prefValues[1]),
            Math.toDegrees(prefValues[2]));
    preferred.setText(msg);
    msg = String.format(
            "Orientation Sensor:\nazimuth (Z): %7.3f\npitch (X): %7.3f\nroll (Y): %7.3f",
            orientationValues[0],
            orientationValues[1],
            orientationValues[2]);
    orientation.setText(msg);
    preferred.invalidate();
    orientation.invalidate();
}

public void doShow(View view) {
    // google.streetview:cbll=30.32454,-81.6584&cbp=1,yaw,,pitch,1.0
    // yaw = degrees clockwise from North
    // For yaw we can use either mAzimuth or orientationValues[0].
    //
    // pitch = degrees up or down. -90 is looking straight up,
    // +90 is looking straight down
    // except that pitch doesn't work properly
    Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse(
            "google.streetview:cbll=30.32454,-81.6584&cbp=1," +
                    Math.round(orientationValues[0]) + ",,0,1.0"
    ));
    startActivity(intent);
    return;
}
person user1234051    schedule 27.02.2012
comment
У меня та же проблема, хотя я тоже использую вращательные матрицы. Я использую getRotationMatrixFromVector() вместо getRotationMatrix(), потому что я использую TYPE_ROTATION_VECTOR. Есть ли различия между этими двумя методами? Второй вопрос: для чего это нужно в вашем коде if(mAzimuth ‹ 0) { mAzimuth += 360.0f; }? - person Nazerke; 14.03.2013
comment
Не могу прокомментировать вектор, но mAzimuth - это направление компаса, оно не предоставляет его как чистое значение 0-360, вам нужно преобразовать отрицательные значения обратно в 360, а также множество других настроек, если вы хотите чтобы правильно рассчитать. Вы могли бы подумать, что можете просто запросить у устройства тангаж, крен и направление по компасу и получить его обратно, никоим образом, вы сами это поймете, главный PITA для того, что должно было быть просто. - person user1234051; 14.03.2013
comment
Что это за другие настройки? Что вы в итоге делаете с mInclination? Это часть исправления? Я также использую getRotationMatrixFromVector() и вижу, что азимут меняется в зависимости от высоты тона, чего я не хочу, а карты Google не работают в режиме компаса. - person Flyview; 16.03.2016
comment
Это решило мою проблему, но стоит отметить, что в этом решении используются вещи, которые могут быть не общими для всех, например массивы, передаваемые по ссылке (т. е. значение устанавливается для параметра, передаваемого методам). Некоторых это может сбить с толку, но эти методы реализованы именно так, поэтому мы ничего не можем с этим поделать. Кроме того, некоторые переменные неправильно названы, например, inR, которую можно было бы назвать rotateMatrix. - person LeonardoSibela; 16.04.2020

Я бы не стал использовать углы Эйлера (крен, тангаж, рыскание). Как вы уже заметили, это в значительной степени портит стабильность вашего приложения.

Узнайте здесь, почему и что делать вместо этого: Странное поведение с датчиком ориентации Android.

person Ali    schedule 26.02.2012
comment
Спасибо, я прочитал пост, я ясно понимаю, почему ангелы Эйлера — плохой выбор, но я не знаком с матрицами вращения и не могу найти никакой хорошей информации об их реализации в Android. Любые другие ссылки? - person user1234051; 27.02.2012
comment
Вам не нужно его реализовывать, это реализуется SensorManager. Я бы начал с SensorManager.getRotationMatrix. - person Ali; 27.02.2012
comment
Спасибо, я это посмотрел. К сожалению, я плохо учусь на теоретических примерах Google. Я выясняю ситуацию, глядя на рабочий код, новичок в концепции матриц вращения, а также в Android. Чертовски крутой подъем прямо сейчас. - person user1234051; 27.02.2012
comment
К сожалению, я не занимаюсь программированием для Android, поэтому не могу вам помочь. Ну не сдавайся! - person Ali; 27.02.2012

Путем экспериментов я обнаружил, что при переключении из режима «Портрет» в режим «Пейзаж» ваша матрица вращения не меняется, но вам нужно изменить ее вручную, чтобы правильно использовать с OpenGL.

copyMat(mRotationMatrixP, mRotationMatrix);

// permute and negate columns 0, 1
mRotationMatrixP[0] = -mRotationMatrix[1];
mRotationMatrixP[4] = -mRotationMatrix[5];
mRotationMatrixP[8] = -mRotationMatrix[9];

// permute 1, 0
mRotationMatrixP[1] = mRotationMatrix[0];
mRotationMatrixP[5] = mRotationMatrix[4];
mRotationMatrixP[9] = mRotationMatrix[8];

Также я надеюсь, что вы правильно усвоили матрицу вращения:

public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
        SensorManager.getRotationMatrixFromVector(
                mRotationMatrix , event.values);
        SensorManager.getOrientation (mRotationMatrix, values);
person Vlad    schedule 07.08.2012

То, что вы описываете, называется карданным замком. При тангаже +/-90, рыскание -(+) крен совершенно не определен. Вблизи шага +/-90 небольшой шум/ошибка в положении могут вызвать большие колебания рыскания и крена по отдельности, даже если в фактической ориентации нет больших изменений. Вот отличная статья о рыскании, тангаже (и о том, как они плохо реализованы на многих платформах):

http://www.sensorplatforms.com/understanding-orientation-conventions-mobile-platforms/

person nak    schedule 28.04.2013