Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/182.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android罗盘方向不可靠(低通滤波器)_Android_Algorithm_Orientation_Signal Processing_Compass Geolocation - Fatal编程技术网

Android罗盘方向不可靠(低通滤波器)

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]

我正在创建一个应用程序,需要根据设备的方向定位ImageView。 我使用来自磁场和加速计传感器的值来计算设备的方向

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之间,输出应该类似

您希望在过滤器中完成两件事:

  • 如果变化很小,为了防止gitter,逐渐转向该方向
  • 如果变化很大,为了防止滞后,请立即转向该方向(如果您希望指南针以平稳的方式移动,可以取消该方向)
  • 为此,我们将有2个常数:

  • 定义运动平滑程度的缓和浮动(1表示不平滑,0表示从不更新,我的默认值为0.5)。我们将其称为SmoothFactorCompass
  • 距离大到可以立即转弯的阈值(0表示始终跳跃,360表示从不跳跃,我的默认值为30)。我们将其称为SmoothThresholdCompass
  • 我们在调用中保存了一个变量,一个名为oldCompass的浮点,它是算法的结果

    因此,变量defention是:

    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]向量,因为