Android罗盘方向不可靠(低通滤波器)
我正在创建一个应用程序,需要根据设备的方向定位ImageView。 我使用来自磁场和加速计传感器的值来计算设备的方向Android罗盘方向不可靠(低通滤波器),android,algorithm,orientation,signal-processing,compass-geolocation,Android,Algorithm,Orientation,Signal Processing,Compass Geolocation,我正在创建一个应用程序,需要根据设备的方向定位ImageView。 我使用来自磁场和加速计传感器的值来计算设备的方向 SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues) SensorManager.getOrientation(rotationMatrix, values); double degrees = Math.toDegrees(values[0]
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);
我的问题是ImageView的定位对方向的变化非常敏感。使imageview不断在屏幕上跳跃。(因为度数变化)
我读到这可能是因为我的设备接近会影响磁场读数的东西。但这似乎不是唯一的原因
我尝试下载了一些应用程序,发现“”和“”的读数非常稳定(在设置噪声过滤器时),我希望在我的应用程序中也有相同的行为
我读到我可以通过添加一个“低通滤波器””来调整读数的“噪音”,但我不知道如何实现这一点(因为我缺乏数学知识)
我希望有人能帮助我在我的设备上创建一个更稳定的读数,在那里设备的一点移动不会影响当前的方向。
现在我做一个小的
if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }
过滤一点噪音。但效果不太好:)参见我对这个相关问题的回答: 软件低通滤波器基本上就是它的一个改进版本。事实上,在这个答案中,我甚至提供了另一个相关问题的链接:低通滤波器(LPF)阻止快速变化的信号和
仅允许信号缓慢变化。这意味着任何小的
突然的变化将被忽略 在软件中实现此功能的标准方法是采用运行平均值
并报告该值。从小到3的N开始,然后
继续增加N,直到在应用程序中找到足够的平滑响应
请记住,您输入的N越高,系统的响应速度越慢。虽然我没有在Android上使用指南针,但下面显示的基本处理(JavaScript)可能对您有效 它基于加速度计上的低通滤波器,该滤波器由制造商推荐,并进行了修改,以适应罗盘(每360度循环一次) 我假设指南针读数以度为单位,浮动范围在0-360之间,输出应该类似 您希望在过滤器中完成两件事:
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;
}
}
}
if(Math.abs(newCompass-oldcompans)<180){
if(Math.abs(newCompass-oldCompass)>SmoothThresholdCompass){
旧指南针=新通道;
}
否则{
旧指南针=旧指南针+平滑因子Compass*(新通道-旧指南针);
}
}
否则{
if(360.0-Math.abs(纽科姆通行证-旧指南针)>平滑阈值指南针){
旧指南针=新通道;
}
否则{
如果(旧指南针>新通道){
旧指南针=(旧指南针+平滑因子compass*((360+新通道-旧指南针)%360)+360)%360;
}
否则{
oldCompass=(oldCompass-SmoothFactorCompass*((360-newCompass+oldCompass)%360)+360)%360;
}
}
}
我知道这个问题在5个月前就开始了,可能不再相关了,但我相信其他程序员可能会发现它很有用
Oded Elyada.请记住,例如,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;
int差=0;
对于(int i=1;i此低通滤波器适用于弧度角度。对每个罗盘读数使用add功能,然后调用average获取平均值
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);
}
}
公共类AngleLowpassFilter{
专用最终整数长度=10;
私人浮动保险公司;
private ArrayDeque queue=new ArrayDeque();
公共空间添加(浮动弧度){
sumSin+=(浮点)数学sin(弧度);
sumCos+=(浮点)数学cos(弧度);
添加(弧度);
if(queue.size()>长度){
float old=queue.poll();
sumSin-=Math.sin(旧);
sumCos-=Math.cos(旧版);
}
}
公众浮动平均值(){
int size=queue.size();
返回(浮动)数学atan2(sumSin/size,sumCos/size);
}
}
使用数学.toDegrees()
或数学.toRadians()
转换。当旧指南针
约为0,而新通道
为359时,这会给我带来非常好的结果。你能解释一下为什么这里需要所有这些三角函数吗?嗨,费利克斯,你需要三角,因为一组角度的简单数字平均值并不总是代表中间。让我们假设你的angles的度数是[350,10],那么你的平均值是(350+10)/2=180(中间实际上是0)。也谢谢你的编辑。好的,是的,这很有意义。而trigos以某种方式解决了这个问题。:)我说的对吗average()
返回角度的度数?返回值是弧度(与输入值和触发函数一致)。我们使用cos和sin将角度分解为[x,y]向量,因为