How can i get device tilt?

2019-06-02 21:48发布

问题:

I am trying to get device tilt (device rotation along y-axis but unfortunately i am unable to achieve my goal. I have tried a lot of stuff using TYPE_ACCELEROMETER and TYPE_MAGNETIC_FIELD as a combined sensor (Sensor fusion). i have also followed Motion Sensors

What i want?

i want to get inclination of device (cell phone ) attached in a vehicle. Let say, i attached a device in a car and car is stationary. So inclination is 0 degrees.Lator on, when vehicle pass through under passes or flyovers, inclination should be accordingly. I have tried to calculate this, here is my code:

...
...
    private static float ALPHA = 0.005f
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tv = (TextView) findViewById(R.id.tv);
        edtAlpha = (EditText) findViewById(R.id.alpha);
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        magnetometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (accelerometer == null) {
            Toast.makeText(this, "Oh  not found", Toast.LENGTH_SHORT).show();
        }
        if (magnetometer == null) {
            Toast.makeText(this, "Oh magnetometer not found",
                    Toast.LENGTH_SHORT).show();
        }

    }

    protected float[] lowPass(float[] input, float[] output) {
        if (output == null)
            return input;
        String s = edtAlpha.getText().toString();
        if (s != null && s.length() > 0) {
            try {
                ALPHA = Float.valueOf(s);
            } catch (NumberFormatException e) {
                ALPHA = 0.005f;
            }
        } else {
            ALPHA = 0.005f;
        }
        for (int i = 0; i < input.length; i++) {
            output[i] = output[i] + ALPHA * (input[i] - output[i]);
        }
        return output;
    }

    public int getRotation(final Activity activity) {
        int result = 1;
        Method mDefaultDisplay_getRotation;
        try {
            mDefaultDisplay_getRotation = Display.class.getMethod(
                    "getRotation", new Class[] {});
            Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay();

            Object retObj = mDefaultDisplay_getRotation.invoke(display);
            if (retObj != null) {
                result = (Integer) retObj;
            }
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return result;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        Log.d(tag, "onSensorChanged");

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            gravSensorVals = lowPass(event.values.clone(), gravSensorVals);

        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magSensorVals = lowPass(event.values.clone(), magSensorVals);
        }

        if (gravSensorVals != null && magSensorVals != null) {
            SensorManager.getRotationMatrix(RTmp, I, gravSensorVals,
                    magSensorVals);

            int rotation = getRotation(this);

            if (rotation == 1) {
                SensorManager.remapCoordinateSystem(RTmp, SensorManager.AXIS_X,
                        SensorManager.AXIS_MINUS_Z, Rot);
            } else {
                SensorManager.remapCoordinateSystem(RTmp, SensorManager.AXIS_Y,
                        SensorManager.AXIS_MINUS_Z, Rot);
            }

            SensorManager.getOrientation(Rot, results);

            float azimuth = (float) (((results[0] * 180) / Math.PI) + 180);
            float pitch = (float) (((results[1] * 180 / Math.PI)) + 90);
            float roll = (float) (((results[2] * 180 / Math.PI)));

            tv.setText("Azimuth : " + df.format(azimuth) + "\nPitch : "
                    + df.format(pitch) + "\nRoll : " + df.format(roll));
        }

        Log.d(tag, "Sensor type : " + event.sensor.getType());

    }

    @Override
    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_UI);
        mSensorManager.registerListener(this, magnetometer,
                SensorManager.SENSOR_DELAY_UI);
    }
...
...

What the issue is with my code?

When i rapidly accelerate /deaccelerate my car, angle goes rapidly increase /decrease but it shouldn't. In other words, when i accelerate/deaccelerate vehicle, there should not be any effect on angle. i have also tried to follow these tutorials: Link 1 Link 2 etc.

回答1:

First of all, you should avoid using Accelerometer or Magnetometer data directly unless you REALLY know what the implications are and what data you receive from those sensors.

I personally recommend that you use the predefined ROTATION_VECTOR sensor which fuses Accelerometer, Magnetometer and Gyroscope in a Kalman-Filter. (see this how to obtain data from the ROTATION_VECTOR-sensor and this how to consume the data).

When you start from this project you can just access the quaternion and play around with it or with the rotation-matrix and apply the corresponding conversion to get the euler angles of your device (the names are not always consistent but you are quite likely interested in the bank or attitude). One final remark: Note that euler-angles suffer from gimbal lock (aka. "jumping" values).



回答2:

As long as you use the accelerometer and the magnetometer but not the gyroscopes, your method will fail when the device goes through rapid acceleration / deceleration. You can try to be overly clever and do some ad-hoc hack that may seem to rule out this particular failure mode you have discovered but I wouldn't even try it.

The true solution is to use gyroscopes as well. In my experience, that works pretty well.

What's wrong with the TYPE_ROTATION_VECTOR?