通过遵循 android 开发示例中的示例,我设法使用Sensor.TYPE_ACCELEROMETER
和Sensor.TYPE_MAGNETIC_FIELD
创建了一个不平滑的指南针(。
通过一些朴素的平滑技术(使用最后 n 值的平均值(,它会变得更好。但是当旋转设备时,它远不及谷歌地图应用程序可以做的事情。
使用内置的谷歌地图应用程序时,指南针运行得非常好,没有感知延迟,没有抖动和流畅的360度。
这是正确的传感器/API 使用,它只需要更复杂的平滑吗? 如果是,安卓 API 中是否有任何帮助?
对于不太精确的目的,例如根据方位在屏幕上旋转地图,您可以使用基于传感器信号和阈值的指数平滑的方法(跳过微小的变化并摆脱颤抖(,而无需任何其他 API 或库。一般来说,它是这样的:
...
private static final ALPHA = 0.095;
private static final THRESHOLD = 0.75;
...
// get current bearing value
SensorManager.getOrientation(rotationMatrix, mOrientationAngles);
float currentMeasuredBearing = (float) (Math.toDegrees(mOrientationAngles[0]));
// process 0 transition
if (currentMeasuredBearing < 0) {
currentMeasuredBearing += 360;
}
// apply exponential smoothing (select trend of bearing)
currentMeasuredBearing = mLastMeasuredBearing + (float) ALPHA * (currentMeasuredBearing - mLastMeasuredBearing);
// apply threshold (skip values less THRESHOLD)
if (Math.abs(Math.toDegrees(mOrientationAngles[0]) - mLastMeasuredBearing) > THRESHOLD){
// do what you need (e.g. rotate map) here
updateCamera(currentMeasuredBearing);
}
您可以通过指数平滑和THRESHOLD
参数的值来调整灵敏度ALPHA
。
或更详细:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
...
private static final SAMPLING_PERIOD = 250 * 1000;
private static final ALPHA = 0.095;
private static final THRESHOLD = 0.75;
private GoogleMap mGoogleMap;
private SensorManager mSensorManager;
private Sensor mSensorAccelerometer;
private Sensor mSensorMagneticField;
private float[] mAccelerometer;
private float[] mMagnetic;
private float[] mOrientationAngles = new float[3];
private float mLastMeasuredBearing;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
...
}
@Override
protected void onStart() {
super.onStart();
startSensors();
}
@Override
protected void onStop() {
super.onStop();
stopSensors();
}
private void updateCamera(float bearing) {
CameraPosition oldPos = mGoogleMap.getCameraPosition();
CameraPosition pos = CameraPosition.builder(oldPos).bearing(bearing).build();
mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(pos));
}
private void startSensors() {
mLastMeasuredBearing = 0;
if (mSensorAccelerometer != null) {
mSensorManager.registerListener(mSensorEventListener,
mSensorAccelerometer, SAMPLING_PERIOD);
}
if (mSensorMagneticField != null) {
mSensorManager.registerListener(mSensorEventListener,
mSensorMagneticField, SAMPLING_PERIOD);
}
}
private void stopSensors() {
if (mSensorAccelerometer != null && mSensorMagneticField != null) {
mSensorManager.unregisterListener(mSensorEventListener);
}
}
private SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mAccelerometer = event.values.clone();
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
mMagnetic = event.values.clone();
}
if (mAccelerometer != null && mMagnetic != null) {
float[] rotationMatrix = new float[9];
if (SensorManager.getRotationMatrix(rotationMatrix, null, mAccelerometer, mMagnetic)) {
SensorManager.getOrientation(rotationMatrix, mOrientationAngles);
float currentMeasuredBearing = (float) (Math.toDegrees(mOrientationAngles[0]));
// process 0 transition
if (currentMeasuredBearing < 0) {
currentMeasuredBearing += 360;
}
// apply exponential smoothing (select trend of sensors data)
currentMeasuredBearing = mLastMeasuredBearing + (float) ALPHA * (currentMeasuredBearing - mLastMeasuredBearing);
// apply threshold (skip values less THRESHOLD)
if (Math.abs(Math.toDegrees(mOrientationAngles[0]) - mLastMeasuredBearing) > THRESHOLD){
updateCamera(currentMeasuredBearing);
}
mBearingTextView.setText(String.valueOf(Math.round(currentMeasuredBearing)));
mAxisXTextView.setText(String.valueOf(Math.round(Math.toDegrees(mOrientationAngles[0]))));
mAxisYTextView.setText(String.valueOf(Math.round(Math.toDegrees(mOrientationAngles[1]))));
mAxisZTextView.setText(String.valueOf(Math.round(Math.toDegrees(mOrientationAngles[2]))));
mLastMeasuredBearing = currentMeasuredBearing;
}
}
}
};