通过遵循android dev samples中的示例,我设法使用Sensor.TYPE_ACCELEROMETER
和Sensor.TYPE_MAGNETIC_FIELD
创建了一个不平滑的罗盘)。
使用一些幼稚的平滑技术(使用最后n个值的平均值),效果会更好。但是,旋转设备时,它与Google Maps应用程序所能完成的工作相差无几。
[使用内置的Google Maps应用程序时,指南针工作得很好,没有可察觉的延迟,无抖动和平滑的360度。
这是要使用的正确传感器/ API,它仅需要更复杂的平滑处理?如果是,android API是否对此有任何帮助?
出于不太精确的目的,例如根据方位在屏幕上旋转地图,您可以使用基于exponential smoothing传感器信号和阈值的方法(跳过小的变化并摆脱抖动),而无需任何其他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);
}
您可以通过指数平滑的ALPHA
参数和THRESHOLD
的值来调节灵敏度。
或更详细:
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;
}
}
}
};