任何人都知道如何在Android中获得平滑的垂直方向度?
我已经尝试过OrientationEventListener,如下所示,但它非常嘈杂。已经尝试了所有费率,正常,延迟,游戏和最快,都显示相同的结果。
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int arg0) {
orientaion = arg0;
Log.i("orientaion", "orientaion:" + orientaion);
}
};
答案 0 :(得分:1)
因此,有两件事情会影响您的需求。
传感器延迟。 Android提供四种不同的传感器延迟模式: SENSOR_DELAY_UI , SENSOR_DELAY_NORMAL , SENSOR_DELAY_GAME 和 SENSOR_DELAY_FASTEST ,其中 SENSOR_DELAY_UI 两个数据点之间的间隔时间最长, SENSOR_DELAY_FASTEST 最短。间隔越短,数据采样率越高(每秒采样数)。较高的采样率可为您提供更多“响应”数据,但噪声更大,而较低的采样率可为您提供更多“滞后”数据,但更加流畅。
噪音过滤。考虑到上述情况,您需要确定要采用的路线。您的应用程序需要快速响应吗?如果是,您可能想要选择更高的采样率。您的应用程序需要流畅的数据吗鉴于问题的背景,我想这显然是肯定的,这意味着你需要噪音过滤。对于传感器数据,噪声本质上是高频率的(噪声值随时间振荡非常快)。所以low pass filter (LPF)通常就足够了。
实施LPF的一种简单方法是exponential smoothing。要与您的代码集成:
int orientation = <init value>;
float update_rate = <value between 0 to 1>;
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int arg0) {
orientation = (int)(orientation * (1f - update_rate) + arg0 * update_rate);
Log.i("orientation", "orientation:" + orientation);
}
};
较大的 update_value 表示结果数据不太流畅,应该很直观:如果 update_value == 1f ,则会回退到原始代码。关于 update_value 的另一个注意事项是它取决于更新之间的时间间隔(与传感器延迟模式有关)。您可能可以调整此值以找到适合您的值,但如果您想确切知道它是如何工作的,请检查电子低通滤波器下的alpha value definition - &gt;离散时间的实现。
答案 1 :(得分:0)
我遇到了类似的问题,在我的设备上显示出人为的视野。低通滤波器(LPF)解决了这个问题。
然而,您需要考虑何时使用度数角度并盲目地应用LPF,当设备处于纵向模式并从左转到骑行或相反时,结果是错误的。原因是359和0度之间的转换。因此,我建议将度数转换为弧度,并将LPF应用于方向角的sin和cos值。
此外,我建议对LPF使用动态alpha或更新速率。 alpha的静态值可能在您的设备上完美,但在其他任何设备上都不是。
以下类过滤基于弧度并使用动态alpha,如上所述:
import static java.lang.Math.*;
Filter {
private static final float TIME_CONSTANT = .297f;
private static final float NANOS = 1000000000.0f;
private static final int MAX = 360;
private double alpha;
private float timestamp;
private float timestampOld;
private int count;
private int values[];
Filter() {
timestamp = System.nanoTime();
timestampOld = System.nanoTime();
values = new int[0];
}
int filter(int input) {
//there is no need to filter if we have only one
if(values.length == 0) {
values = new int[] {0, input};
return input;
}
//filter based on last element from array and input
int filtered = filter(values[1], input);
//new array based on previous result and filter
values = new int[] {values[1], filtered};
return filtered;
}
private int filter(int previous, int current) {
calculateAlpha();
//convert to radians
double radPrev = toRadians(previous);
double radCurrent = toRadians(current);
//filter based on sin & cos
double sumSin = filter(sin(radPrev), sin(radCurrent));
double sumCos = filter(cos(radPrev), cos(radCurrent));
//calculate result angle
double radRes = atan2(sumSin, sumCos);
//convert radians to degree, round it and normalize (modulo of 360)
long round = round(toDegrees(radRes));
return (int) ((MAX + round) % MAX);
}
//dynamic alpha
private void calculateAlpha() {
timestamp = System.nanoTime();
float diff = timestamp - timestampOld;
double dt = 1 / (count / (diff / NANOS));
count++;
alpha = dt/(TIME_CONSTANT + dt);
}
private double filter(double previous, double current) {
return (previous + alpha * (current - previous));
}
}
如需进一步阅读,请参阅此discussion。