Android 指南针方向不可靠(低通滤波器)

发布于 2024-10-12 04:05:36 字数 985 浏览 5 评论 0原文

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

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

我的问题是 ImageView 的定位对方向的变化非常敏感。使图像视图不断地在屏幕上跳跃。 (因为度数发生变化)

我读到这可能是因为我的设备靠近可能影响磁场读数的物体。但这似乎并不是唯一的原因。

我尝试下载一些应用程序,发现“3D 指南针< /a>”和“Compass”仍然非常稳定在其读数中(设置噪声滤波器时),我希望在我的应用程序中具有相同的行为。

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

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

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }

过滤噪音的工作。但它的效果不是很好:)

Im creating an application where i need to position a ImageView depending on the Orientation of the device.
I use the values from a MagneticField and Accelerometer Sensors to calculate the device orientation with

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

My problem is that the positioning of the ImageView is very sensitive to changes in the orientation. Making the imageview constantly jumping around the screen. (because the degrees change)

I read that this can be because my device is close to things that can affect the magneticfield readings. But this is not the only reason it seems.

I tried downloading some applications and found that the "3D compass" and "Compass" remains extremely steady in its readings (when setting the noise filter up), i would like the same behavior in my application.

I read that i can tweak the "noise" of my readings by adding a "Low pass filter", but i have no idea how to implement this (because of my lack of Math).

Im hoping someone can help me creating a more steady reading on my device, Where a little movement to the device wont affect the current orientation.
Right now i do a small

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }

To filter abit of the noise. But its not working very well :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

三岁铭 2024-10-19 04:05:36

虽然我没有在 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;

函数接收 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 个月前提出的,可能不再相关,但我确信其他程序员可能会发现它有用。

奥德·埃利亚达.

Though I havn't used the compass on Android, the basic processing shown below (in JavaScript) will probably work for you.

It's based on the low pass filter on the accelerometer that's recommended by the Windows Phone team with modifications to suit a compass (the cyclic behavior every 360").

I assume the compass reading is in degrees, a float between 0-360, and the output should be similar.

You want to accomplish 2 things in the filter:

  1. If the change is small, to prevent gitter, gradually turn to that direction.
  2. If the change is big, to prevent lag, turn to that direction immediatly (and it can be canceled if you want the compass to move only in a smooth way).

For that we will have 2 constants:

  1. The easing float that defines how smooth the movement will be (1 is no smoothing and 0 is never updating, my default is 0.5). We will call it SmoothFactorCompass.
  2. The threshold in which the distance is big enough to turn immediatly (0 is jump always, 360 is never jumping, my default is 30). We will call it SmoothThresholdCompass.

We have one variable saved across the calls, a float called oldCompass and it is the result of the algorithm.

So the variable defenition is:

var SmoothFactorCompass = 0.5;
var SmoothThresholdCompass = 30.0;
var oldCompass = 0.0;

and the function recieves newCompass, and returns oldCompass as the result.

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;
        }
    }
}

I see that the issue was opened 5 months ago and probably isn't relevant anymore, but I'm sure other programmers might find it useful.

Oded Elyada.

迷你仙 2024-10-19 04:05:36

该低通滤波器适用于以弧度为单位的角度。对每个罗盘读数使用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);
    }
}

使用 Math.toDegrees()Math.toRadians() 进行转换。

This lowpass filter works for angles in radians. Use the add function for each compass reading, then call average to get the 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);
    }
}

Use Math.toDegrees() or Math.toRadians() to convert.

对不⑦ 2024-10-19 04:05:36

请记住,例如 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;

Keep in mind that, for example the average of 350 and 10 is not 180. My solution:

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;
冬天旳寂寞 2024-10-19 04:05:36

低通滤波器 (LPF) 阻止快速变化的信号并
只允许信号缓慢变化。这意味着任何小的
突然的变化将被忽略。

在软件中实现这一点的标准方法是获取运行平均值
最后 N 个样本并报告该值。从 N 小至 3 开始,
继续增加 N,直到您在应用程序中找到足够的平滑响应。

请记住,N 越高,系统的响应就越慢。

A low pass filter (LPF) blocks fast changing signals and
allows only slow changes in the signals. This means any small
sudden changes will be ignored.

The standard way to implement this in software is to take a running average
of the last N samples and report that value. Start with N as small as 3 and
keep increasing N until you find sufficient smoothed out response in your app.

Do keep in mind that the higher you make N, slower the response of the system.

你怎么这么可爱啊 2024-10-19 04:05:36

请参阅我对此相关问题的回答:平滑来自传感器的数据

软件低通滤波器基本上是其修改版本。事实上,在那个答案中,我什至提供了另一个相关问题的链接:低通滤波器软件?

See my answer to this related question: Smoothing data from a sensor

A software low pass filter is basically a modified version of that. Indeed, in that answer I even provided this link to another related question: Low pass filter software?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文