我正在创建一个应用程序,我需要根据设备的方向定位ImageView。 我使用MagneticField和Accelerometer传感器中的值来计算设备方向
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);
我的问题是ImageView的定位对方向的变化非常敏感。使imageview不断跳到屏幕上。 (因为学位变化)
我读到这可能是因为我的设备接近可能影响磁场读数的东西。但这不是它看起来的唯一原因。
我尝试下载一些应用程序,发现“3D compass”和“Compass”的读数仍然非常稳定(当设置噪音滤镜时),我希望我的行为相同应用
我读到我可以通过添加“低通过滤器”来调整读数的“噪音”,但我不知道如何实现这一点(因为我缺乏数学)。
我希望有人可以帮助我在我的设备上创建一个更稳定的阅读,其中设备的一点点移动不会影响当前的方向。 现在我做了一个小的
if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }
过滤掉噪音。但它不能很好地工作:)
答案 0 :(得分:30)
虽然我没有在Android上使用指南针,但下面显示的基本处理(在JavaScript中)可能对您有用。
它基于加速度计上的低通滤波器,Windows Phone team建议修改以适应指南针(每360度循环行为)。
我假设指南针读数是度数,浮点数在0-360之间,输出应该相似。
您希望在过滤器中完成两件事:
- 如果变化很小,为防止灰尘,请逐渐转向该方向。
- 如果变化很大,为防止滞后,立即转向该方向(如果您希望指南针只能以平滑的方式移动,则可以取消)。
醇>
为此我们将有2个常数:
- 缓动浮动,定义移动的平滑程度(1表示没有平滑,0表示永不更新,默认值为0.5)。我们将其称为SmoothFactorCompass。
- 距离大到足以立即转动的阈值(0表示始终跳跃,360表示从不跳跃,默认为30)。我们将其称为SmoothThresholdCompass。
醇>
我们在调用中保存了一个变量,一个名为oldCompass的浮点数,它是算法的结果。
所以变量defenition是:
var SmoothFactorCompass = 0.5;
var SmoothThresholdCompass = 30.0;
var oldCompass = 0.0;
并且函数接收newCompass,并返回oldCompass作为结果。
if (Math.abs(newCompass - oldCompass) < 180) {
if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
oldCompass = newCompass;
}
else {
oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass);
}
}
else {
if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
oldCompass = newCompass;
}
else {
if (oldCompass > newCompass) {
oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360;
}
else {
oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360;
}
}
}
我发现该问题已于5个月前开放,可能不再相关,但我确信其他程序员可能会觉得它很有用。
Oded Elyada。
答案 1 :(得分:19)
此低通滤波器适用于弧度角。对每个罗盘读数使用添加功能,然后调用平均值来获得平均值。
public class AngleLowpassFilter {
private final int LENGTH = 10;
private float sumSin, sumCos;
private ArrayDeque<Float> queue = new ArrayDeque<Float>();
public void add(float radians){
sumSin += (float) Math.sin(radians);
sumCos += (float) Math.cos(radians);
queue.add(radians);
if(queue.size() > LENGTH){
float old = queue.poll();
sumSin -= Math.sin(old);
sumCos -= Math.cos(old);
}
}
public float average(){
int size = queue.size();
return (float) Math.atan2(sumSin / size, sumCos / size);
}
}
使用 Math.toDegrees()
或 Math.toRadians()
进行转换。
答案 2 :(得分:4)
请记住,例如350和10的平均值不是180.我的解决方案:
int difference = 0;
for(int i= 1;i <numberOfAngles;i++){
difference += ( (angles[i]- angles[0] + 180 + 360 ) % 360 ) - 180;
}
averageAngle = (360 + angles[0] + ( difference / numberOfAngles ) ) % 360;
答案 3 :(得分:3)
低通滤波器(LPF)阻止快速变化的信号和
只允许信号的缓慢变化。这意味着任何小的
突然的变化将被忽略。
在软件中实现这一点的标准方法是采用运行平均值
最后N个样本并报告该值。从N开始,小到3和
继续增加N,直到在您的应用中找到足够平滑的响应。
请记住,制作N越高,系统响应越慢。
答案 4 :(得分:2)
请参阅我对此相关问题的回答:Smoothing data from a sensor
软件低通滤波器基本上是其修改版本。实际上,在那个答案中,我甚至提供了另一个相关问题的链接:Low pass filter software?