不可靠的Android Compass定位(低通滤波器)

Mad*_*sen 15 algorithm android signal-processing orientation compass-geolocation

我正在创建一个应用程序,我需要根据设备的方向定位ImageView.我使用MagneticField和Accelerometer Sensors中的值来计算设备方向

SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);
Run Code Online (Sandbox Code Playgroud)

我的问题是ImageView的定位对方向的变化非常敏感.使imageview不断跳到屏幕上.(因为度数变化)

我读到这可能是因为我的设备接近可能影响磁场读数的东西.但这不是它看起来的唯一原因.

我尝试下载一些应用程序,发现" 3D罗盘 "和" 指南针 "的读数仍然非常稳定(当设置噪声滤波器时),我想在我的应用程序中使用相同的行为.

我读到我可以通过添加" 低通滤波器 " 来调整读数的"噪音" ,但我不知道如何实现这一点(因为我缺乏数学).

我希望有人可以帮助我在我的设备上创建更稳定的读数,其中设备的一点点移动不会影响当前的方向.现在我做了一个小

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }
Run Code Online (Sandbox Code Playgroud)

过滤噪音的升值.但它不能很好地工作:)

小智 30

虽然我没有在Android上使用指南针,但下面显示的基本处理(用JavaScript)可能对你有用.

它基于加速度计上的低通滤波器,由Windows Phone团队推荐,经过修改以适应指南针(每360个循环行为).

我假设罗盘读数是以度为单位,浮点数在0-360之间,输出应该相似.

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

  1. 如果变化很小,为了防止灰尘,逐渐转向那个方向.
  2. 如果变化很大,为防止滞后,立即转向该方向(如果您希望指南针仅以平滑的方式移动,则可以取消).

为此,我们将有2个常量:

  1. 缓动浮动定义了移动的平滑程度(1表示没有平滑,0表示永不更新,默认值为0.5).我们将其称为SmoothFactorCompass.
  2. 距离大到足以立即转动的阈值(0总是跳跃,360永远不会跳跃,我的默认值是30).我们将其称为SmoothThresholdCompass.

我们在调用中保存了一个变量,一个名为oldCompass的浮点数,它是算法的结果.

所以变量防御是:

var SmoothFactorCompass = 0.5;
var SmoothThresholdCompass = 30.0;
var oldCompass = 0.0;
Run Code Online (Sandbox Code Playgroud)

并且函数接收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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我看到问题是在5个月前开放的,可能不再相关,但我确信其他程序员可能会发现它很有用.

Oded Elyada.

  • 当`oldCompass`大约为0而`newCompass`为359时,这给了我很好的结果. (3认同)

Sha*_*ley 20

该低通滤波器适用于弧度角.使用每个罗盘读数的添加功能,然后调用平均值来获得平均值.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用Math.toDegrees()Math.toRadians()转换.

  • 嗨菲利克斯,你需要三角形,因为一组角度的简单数值平均值并不总是代表中间.假设您的角度为[350,10],那么您的平均值将为(350 + 10)/ 2 = 180(其中中间值为0).**感谢您的编辑.** (4认同)
  • 您的实现正是我想要的.即使没有插值动画也能平滑 (3认同)
  • 返回值以弧度为单位(与输入值和trig函数一致).我们使用cos和sin将角度分解为[x,y]向量,因为这些向量*的​​平均值*代表中线.然后我们使用atan2将矢量转换回弧度的角度. (2认同)

小智 5

请记住,例如 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;
Run Code Online (Sandbox Code Playgroud)