[英]Vertical orientation degree - Android
有谁知道如何在Android中获得平滑的垂直方向度?
我已经尝试了如下所示的OrientationEventListener,但是非常嘈杂。 已经尝试过所有速率,“正常”,“延迟”,“游戏”和“最快”,均显示相同的结果。
myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int arg0) {
orientaion = arg0;
Log.i("orientaion", "orientaion:" + orientaion);
}
};
因此,有两件事会影响您的需求。
传感器延迟 。 Android提供四种不同的传感器延迟模式:SENSOR_DELAY_UI,SENSOR_DELAY_NORMAL,SENSOR_DELAY_GAME,和SENSOR_DELAY_FASTEST,其中SENSOR_DELAY_UI具有两个数据点之间的最长间隔和SENSOR_DELAY_FASTEST具有最短。 间隔越短,数据采样率(每秒采样数)越高。 较高的采样率可为您提供更多“响应”数据,但会带来更大的噪音,而较低的采样率可为您提供更多“延迟”数据,但更为平滑。
噪声过滤 。 考虑到以上几点,您需要确定要采取的路线。 您的应用程序需要快速响应吗? 如果是这样,您可能希望选择更高的采样率。 您的应用程序需要平滑的数据吗? 考虑到问题的上下文,我想这显然是肯定的,这意味着您需要进行噪声过滤。 对于传感器数据,噪声本质上多数是高频信号(噪声值会随时间快速振荡)。 因此, 低通滤波器(LPF)通常就足够了。
实现LPF的一种简单方法是指数平滑 。 要与您的代码集成:
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值定义 。
我在装置上显示人造视线时也遇到类似的问题。 低通滤波器(LPF)解决了此问题。
但是,您需要考虑以度为单位使用方向角并将LPF盲目地应用在其上时,当设备处于纵向模式并从左转为乘坐或相反时,结果将是错误的。 原因是在359度和0度之间转换。 因此,我建议将度数转换为弧度,并将LPF应用于方向角的正弦和余弦值。
此外,我建议对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));
}
}
有关进一步的阅读,请参见此讨论 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.