java – 静止时使用TYPE_ROTATION_VECTOR实现当前的基数方向方法?

前端之家收集整理的这篇文章主要介绍了java – 静止时使用TYPE_ROTATION_VECTOR实现当前的基数方向方法?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

似乎有许多旧的例子表明在Android设备上获得当前的主要方向,但Google提供的官方解决方案似乎并未出现在他们的文档中.

不推荐使用的最旧参考Sensor.TYPE_ORIENTATION,最新的参考文献提到了Sensor.TYPE_ACCELEROMETER和Sensor.TYPE_MAGNETIC_FIELD(我试过但收效甚微 – 准确性根据设备方向快速变化).我一直在尝试使用像this.这样的两个实现,我甚至见过一些TYPE.GRAVITY.

most recent seem to suggest TYPE_ROTATION_VECTOR显然是一个融合传感器(reference),但示例实现似乎并不容易获得.

我需要使用这些位置/运动传感器,而不是GPS,因为在需要进行此测量时,用户不会移动.无论手机是平的还是垂直的(如果你正在拍照),也需要测量稳定

在我们以某种方式拉出度数测量之后,转换到基本方向似乎很容易.(https://stackoverflow.com/a/25349774/1238737)

以前的方案

> How to get Direction in Android (Such as North,West)
> https://stackoverflow.com/a/11068878/1238737

最佳答案
我之前正在研究开源地图项目,如OsmAnd,MapsWithMe和MapBox.我认为这些项目是地图和导航领域中最好的Android开源.我检查了他们的代码,发现当手机垂直然后围绕垂直轴(y)旋转时,显示罗盘的MapBox方法是稳定的.如果旋转矢量传感器可用,它使用TYPE_ROTATION_VECTOR.否则,它使用TYPE_ORIENTATION传感器或TYPE_ACCELEROMETER和TYPE_MAGNETIC_FIELD的组合.在使用TYPE_ACCELEROMETER和TYPE_MAGNETIC_FIELD的情况下,可以通过低通滤波器减少结果的振荡,以实现更平滑的值.

enter image description here

这是MapBox的指南针引擎及其用法.
.

LocationComponentCompassEngine.java:

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.Surface;
import android.view.WindowManager;
import timber.log.Timber;

import java.util.ArrayList;
import java.util.List;

/**
 * This manager class handles compass events such as starting the tracking of device bearing,or
 * when a new compass update occurs.
 */
public class LocationComponentCompassEngine implements SensorEventListener {

    // The rate sensor events will be delivered at. As the Android documentation states,this is only
    // a hint to the system and the events might actually be received faster or slower then this
    // specified rate. Since the minimum Android API levels about 9,we are able to set this value
    // ourselves rather than using one of the provided constants which deliver updates too quickly for
    // our use case. The default is set to 100ms
    private static final int SENSOR_DELAY_MICROS = 100 * 1000;
    // Filtering coefficient 0 < ALPHA < 1
    private static final float ALPHA = 0.45f;

    // Controls the compass update rate in milliseconds
    private static final int COMPASS_UPDATE_RATE_MS = 500;

    private final WindowManager windowManager;
    private final SensorManager sensorManager;
    private final ListIoUsNameCombination")
    private void updateOrientation() {
        if (rotationVectorValue != null) {
            SensorManager.getRotationMatrixFromVector(rotationMatrix,rotationVectorValue);
        } else {
            // Get rotation matrix given the gravity and geomagnetic matrices
            SensorManager.getRotationMatrix(rotationMatrix,null,gravityValues,magneticValues);
        }

        final int worldAxisForDeviceAxisX;
        final int worldAxisForDeviceAxisY;

        // Remap the axes as if the device screen was the instrument panel,// and adjust the rotation matrix for the device orientation.
        switch (windowManager.getDefaultDisplay().getRotation()) {
            case Surface.ROTATION_90:
                worldAxisForDeviceAxisX = SensorManager.AXIS_Z;
                worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X;
                break;
            case Surface.ROTATION_180:
                worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X;
                worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z;
                break;
            case Surface.ROTATION_270:
                worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z;
                worldAxisForDeviceAxisY = SensorManager.AXIS_X;
                break;
            case Surface.ROTATION_0:
            default:
                worldAxisForDeviceAxisX = SensorManager.AXIS_X;
                worldAxisForDeviceAxisY = SensorManager.AXIS_Z;
                break;
        }

        float[] adjustedRotationMatrix = new float[9];
        SensorManager.remapCoordinateSystem(rotationMatrix,worldAxisForDeviceAxisX,worldAxisForDeviceAxisY,adjustedRotationMatrix);

        // Transform rotation matrix into azimuth/pitch/roll
        float[] orientation = new float[3];
        SensorManager.getOrientation(adjustedRotationMatrix,orientation);

        // The x-axis is all we care about here.
        notifyCompassChangeListeners((float) Math.toDegrees(orientation[0]));
    }

    private void notifyCompassChangeListeners(float heading) {
        for (CompassListener compassListener : compassListeners) {
            compassListener.onCompassChanged(heading);
        }
        lastHeading = heading;
    }

    private void registerSensorListeners() {
        if (isCompassSensorAvailable()) {
            // Does nothing if the sensors already registered.
            sensorManager.registerListener(this,compassSensor,SENSOR_DELAY_MICROS);
        } else {
            sensorManager.registerListener(this,gravitySensor,SENSOR_DELAY_MICROS);
            sensorManager.registerListener(this,magneticFieldSensor,SENSOR_DELAY_MICROS);
        }
    }

    private void unregisterSensorListeners() {
        if (isCompassSensorAvailable()) {
            sensorManager.unregisterListener(this,compassSensor);
        } else {
            sensorManager.unregisterListener(this,gravitySensor);
            sensorManager.unregisterListener(this,magneticFieldSensor);
        }
    }

    private boolean isCompassSensorAvailable() {
        return compassSensor != null;
    }

    /**
     * Helper function,that filters newValues,considering prevIoUs values
     *
     * @param newValues      array of float,that contains new data
     * @param smoothedValues array of float,that contains prevIoUs state
     * @return float filtered array of float
     */
    private float[] lowPassFilter(float[] newValues,float[] smoothedValues) {
        if (smoothedValues == null) {
            return newValues;
        }
        for (int i = 0; i < newValues.length; i++) {
            smoothedValues[i] = smoothedValues[i] + ALPHA * (newValues[i] - smoothedValues[i]);
        }
        return smoothedValues;
    }

    /**
     * Pulls out the rotation vector from a SensorEvent,with a maximum length
     * vector of four elements to avoid potential compatibility issues.
     *
     * @param event the sensor event
     * @return the events rotation vector,potentially truncated
     */
    @NonNull
    private float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) {
        if (event.values.length > 4) {
            // On some Samsung devices SensorManager.getRotationMatrixFromVector
            // appears to throw an exception if rotation vector has length > 4.
            // For the purposes of this class the first 4 values of the
            // rotation vector are sufficient (see crbug.com/335298 for details).
            // Only affects Android 4.3
            System.arraycopy(event.values,truncatedRotationVectorValue,4);
            return truncatedRotationVectorValue;
        } else {
            return event.values;
        }
    }

    public static float shortestRotation(float heading,float prevIoUsHeading) {
        double diff = prevIoUsHeading - heading;
        if (diff > 180.0f) {
            heading += 360.0f;
        } else if (diff < -180.0f) {
            heading -= 360.f;
        }
        return heading;
    }

}

CompassListener.java:

/**
 * Callbacks related to the compass
 */
public interface CompassListener {

    /**
     * Callback's invoked when a new compass update occurs. You can listen into the compass updates
     * using {@link LocationComponent#addCompassListener(CompassListener)} and implementing these
     * callbacks. Note that this interface is also used internally to to update the UI chevron/arrow.
     *
     * @param userHeading the new compass heading
     */
    void onCompassChanged(float userHeading);

    /**
     * This gets invoked when the compass accuracy status changes from one value to another. It
     * provides an integer value which is identical to the {@code SensorManager} class constants:
     * 

MainActivity.java:

import android.content.Context;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import android.widget.TextView;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private LocationComponentCompassEngine compassEngine;
    private float prevIoUsCompassBearing = -1f;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView textView = findViewById(R.id.textView);

        CompassListener compassListener = new CompassListener() {

            @Override
            public void onCompassChanged(float targetCompassBearing) {
                if (prevIoUsCompassBearing < 0) {
                    prevIoUsCompassBearing = targetCompassBearing;
                }
                float normalizedBearing =
                        LocationComponentCompassEngine.shortestRotation(targetCompassBearing,prevIoUsCompassBearing);
                prevIoUsCompassBearing = targetCompassBearing;

                String status = "NO_CONTACT";
                switch (compassEngine.getLastAccuracySensorStatus()) {
                    case SensorManager.SENSOR_STATUS_NO_CONTACT:
                        status = "NO_CONTACT";
                        break;
                    case SensorManager.SENSOR_STATUS_UNRELIABLE:
                        status = "UNRELIABLE";
                        break;
                    case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
                        status = "ACCURACY_LOW";
                        break;
                    case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM:
                        status = "ACCURACY_MEDIUM";
                        break;
                    case SensorManager.SENSOR_STATUS_ACCURACY_HIGH:
                        status = "ACCURACY_HIGH";
                        break;
                }

                textView.setText(String.format(Locale.getDefault(),"CompassBearing: %f\nAccuracySensorStatus: %s",normalizedBearing,status));
            }

            @Override
            public void onCompassAccuracyChange(int compassStatus) {
            }

        };

        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        compassEngine = new LocationComponentCompassEngine(windowManager,sensorManager);
        compassEngine.addCompassListener(compassListener);

        compassEngine.onStart();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        compassEngine.onStop();
    }

}

猜你在找的Android相关文章