转换磁场的X,Y,Z值从设备到全球基准帧 [英] Convert magnetic field X, Y, Z values from device into global reference frame

查看:640
本文介绍了转换磁场的X,Y,Z值从设备到全球基准帧的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当您使用TYPE_MAGNETOMETER传感器,你得到的X,Y,相对于设备的方向磁场强度Z值。我希望得到的是将这些值转换成全球参考框架,明确:围绕任何轴用户需要的设备,测量这些值,比旋转设备进行一定程度和获取〜相同的值。 请在下面找到类似的问题: <一href="http://stackoverflow.com/questions/13361010/getting-magnetic-field-values-in-global-coordinates">Getting在全球坐标系磁场值 <一href="http://stackoverflow.com/questions/11772923/how-can-i-get-the-magnetic-field-vector-independent-of-the-device-rotation">How我可以得到磁场矢量,独立于设备的旋转? 在这个答案的样品溶液被描述(这是直线加速,但我认为这并不重要): HTTP://计算器。 COM / A /2152255分之11614404 我用它,我得到了3个值,X总是很小(不认为这是正确的),Y和Z都OK,但他们还是改了一下,当我旋转设备。怎么可能进行调整?而且它可以解决呢?我用简单的卡尔曼滤波器来近似测量值,因为W / O它,我得到即使该设备是不动的安静不同的价值观/旋转的。请在下面找到我的code:

When you use TYPE_MAGNETOMETER sensor, you get X, Y, Z values of magnetic field strength in relation to the device orientation. What I want to get is to convert these values into global reference frame, clarifying: user takes the device, measure these values, than rotate the device for some degrees around any axis and gets ~the same values. Please, find similar questions below: Getting magnetic field values in global coordinates How can I get the magnetic field vector, independent of the device rotation? In this answer sample solution is described (it is for linear acceleration, but I think it doesn't matter): http://stackoverflow.com/a/11614404/2152255 I used it and I got 3 values, X is always very small (don't think that it is correct), Y and Z are OK, but they still changed a bit when I rotate the device. How could it be adjusted? And could it be solved all? I use simple Kalman filter to approximate measurement values, because w/o it I get quiet different values even if the device is not moving/rotating at all. Please, find my code below:

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.opengl.Matrix;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import com.test.statistics.filter.kalman.KalmanState;
import com.example.R;

/**
 * Activity for gathering magnetic field statistics.
 */
public class MagneticFieldStatisticsGatheringActivity extends Activity implements SensorEventListener {

    public static final int KALMAN_STATE_MAX_SIZE = 80;
    public static final double MEASUREMENT_NOISE = 5;

    /** Sensor manager. */
    private SensorManager mSensorManager;
    /** Magnetometer spec. */
    private TextView vendor;
    private TextView resolution;
    private TextView maximumRange;

    /** Magnetic field coordinates measurements. */
    private TextView magneticXTextView;
    private TextView magneticYTextView;
    private TextView magneticZTextView;

    /** Sensors. */
    private Sensor mAccelerometer;
    private Sensor mGeomagnetic;
    private float[] accelerometerValues;
    private float[] geomagneticValues;

    /** Flags. */
    private boolean specDefined = false;
    private boolean kalmanFiletring = false;

    /** Rates. */
    private float nanoTtoGRate = 0.00001f;
    private final int gToCountRate = 1000000;

    /** Kalman vars. */
    private KalmanState previousKalmanStateX;
    private KalmanState previousKalmanStateY;
    private KalmanState previousKalmanStateZ;
    private int previousKalmanStateCounter = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main2);
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mGeomagnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

        vendor = (TextView) findViewById(R.id.vendor);
        resolution = (TextView) findViewById(R.id.resolution);
        maximumRange = (TextView) findViewById(R.id.maximumRange);

        magneticXTextView = (TextView) findViewById(R.id.magneticX);
        magneticYTextView = (TextView) findViewById(R.id.magneticY);
        magneticZTextView = (TextView) findViewById(R.id.magneticZ);

        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
        mSensorManager.registerListener(this, mGeomagnetic, SensorManager.SENSOR_DELAY_FASTEST);
    }

    /**
     * Refresh statistics.
     *
     * @param view - refresh button view.
     */
    public void onClickRefreshMagneticButton(View view) {
        resetKalmanFilter();
    }

    /**
     * Switch Kalman filtering on/off
     *
     * @param view - Klaman filetring switcher (checkbox)
     */
    public void onClickKalmanFilteringCheckBox(View view) {
        CheckBox kalmanFiltering = (CheckBox) view;
        this.kalmanFiletring = kalmanFiltering.isChecked();
    }

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {
        if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
            return;
        }
        synchronized (this) {
            switch(sensorEvent.sensor.getType()){
                case Sensor.TYPE_ACCELEROMETER:
                    accelerometerValues = sensorEvent.values.clone();
                    break;
                case Sensor.TYPE_MAGNETIC_FIELD:
                    if (!specDefined) {
                        vendor.setText("Vendor: " + sensorEvent.sensor.getVendor() + " " + sensorEvent.sensor.getName());
                        float resolutionValue = sensorEvent.sensor.getResolution() * nanoTtoGRate;
                        resolution.setText("Resolution: " + resolutionValue);
                        float maximumRangeValue = sensorEvent.sensor.getMaximumRange() * nanoTtoGRate;
                        maximumRange.setText("Maximum range: " + maximumRangeValue);
                    }
                    geomagneticValues = sensorEvent.values.clone();
                    break;
            }
            if (accelerometerValues != null && geomagneticValues != null) {
                float[] Rs = new float[16];
                float[] I = new float[16];

                if (SensorManager.getRotationMatrix(Rs, I, accelerometerValues, geomagneticValues)) {

                    float[] RsInv = new float[16];
                    Matrix.invertM(RsInv, 0, Rs, 0);

                    float resultVec[] = new float[4];
                    float[] geomagneticValuesAdjusted = new float[4];
                    geomagneticValuesAdjusted[0] = geomagneticValues[0];
                    geomagneticValuesAdjusted[1] = geomagneticValues[1];
                    geomagneticValuesAdjusted[2] = geomagneticValues[2];
                    geomagneticValuesAdjusted[3] = 0;
                    Matrix.multiplyMV(resultVec, 0, RsInv, 0, geomagneticValuesAdjusted, 0);

                    for (int i = 0; i < resultVec.length; i++) {
                        resultVec[i] = resultVec[i] * nanoTtoGRate * gToCountRate;
                    }

                    if (kalmanFiletring) {

                        KalmanState currentKalmanStateX = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[0], (double)resultVec[0], previousKalmanStateX);
                        previousKalmanStateX = currentKalmanStateX;

                        KalmanState currentKalmanStateY = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[1], (double)resultVec[1], previousKalmanStateY);
                        previousKalmanStateY = currentKalmanStateY;

                        KalmanState currentKalmanStateZ = new KalmanState(MEASUREMENT_NOISE, accelerometerValues[2], (double)resultVec[2], previousKalmanStateZ);
                        previousKalmanStateZ = currentKalmanStateZ;

                        if (previousKalmanStateCounter == KALMAN_STATE_MAX_SIZE) {
                            magneticXTextView.setText("x: " + previousKalmanStateX.getX_estimate());
                            magneticYTextView.setText("y: " + previousKalmanStateY.getX_estimate());
                            magneticZTextView.setText("z: " + previousKalmanStateZ.getX_estimate());

                            resetKalmanFilter();
                        } else {
                            previousKalmanStateCounter++;
                        }

                    } else {
                        magneticXTextView.setText("x: " + resultVec[0]);
                        magneticYTextView.setText("y: " + resultVec[1]);
                        magneticZTextView.setText("z: " + resultVec[2]);
                    }
                }
            }
        }
    }

    private void resetKalmanFilter() {
        previousKalmanStateX = null;
        previousKalmanStateY = null;
        previousKalmanStateZ = null;
        previousKalmanStateCounter = 0;
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) {
    }
}

谢谢大家谁读这篇文章,谁张贴有关问题的一些想法提前。

Thanks everybody who read this post and who post some thoughts about the problem in advance.

推荐答案

在我对你上面提供的链接对检查答案的评论,我提到我的答案很简单,在<一个href="http://stackoverflow.com/questions/14963190/calculate-acceleration-in-reference-to-true-north/14988559#14988559">calculate加速参考真北

In my comment on the checked answer on the link you provided above, I referred to my simple answer at calculate acceleration in reference to true north

让我有更多的澄清回答这里。答案是在旋转矩阵的产物,在磁场值。如果进一步在读X总是非常小是正确的值。

Let me answer here again with more clarification. The answer is the product of the rotation matrix and the magnetic field values. If you read further on the "X is always very small" is the correct value.

加速度计和磁场传感器测量设备的加速度和在地球的磁场在装置位置分别。他们在3维空间中的向量,让 M 分别打电话给他们。照片 如果你站着不动,旋转设备,理论上的 M 不改变假设有来自周围物体(实际上是无磁场干扰的 M 应该变化不大,如果由于中移动地球的磁场应该改变小在一个很短的距离)。但 A 不改变,即使它不应该是激烈的,在大多数情况下。

The accelerometer and magnetic field sensors measure the acceleration of the device and the magnetic field of the earth at the device location respectively. They are vectors in 3 dimentional space, let call them a and m respectively.
If you stand still and rotate your device, theoretically m does not change assuming there are no magnetic interference from surrounding objects (actually m should change little, if you move around since the magnetic field of the earth should change little in a short distance). But a does change, even though it should not be drastic in most situation.

现在一个vector v 在3维空间可以再通过一个3元组psented $ P $(V_1,V_2,V_3)对于一些基础( E_1 E_2 e_3 ),即 v = V_1 E_1 + V_2 E_2 + V_3 e_3 。 (V_1,V_2,V_3)被称为 v 相对于基础( E_1 E_2 e_3 )。

Now a vector v in 3 dimensional space can be represented by a 3-tuples (v_1, v_2, v_3) with respect to some basis (e_1, e_2, e_3), i.e v = v_1 e_1 + v_2 e_2 + v_3 e_3. (v_1, v_2, v_3) are called the coordinates of v with respect to the basis (e_1, e_2, e_3).

在Android设备上,依据是( X ž)其中,对于大多数手机, X 是沿着短边,指着右侧,是沿着长边和指向上和以Z 是垂直于屏幕,并指出,
现在此基础上变化的装置的位置变化。我们可以把这些基地作为时间的函数( X (T),(T),ž(T)),在数学来看,它是一个移动的坐标系。

In Android devices, the basis is (x, y, z) where, for most phone, x is along the shorter side and pointing right, y is along the longer side and pointing up and z is perpendicular to the screen and pointing out.
Now this basis changes as the position of the device changes. One can think these bases as a function of time (x(t), y(t), z(t)), in mathematics term it is a moving coordinate system.

因此​​,即使 M 不会改变,但在 event.values​​ 由传感器的回报是不同的,因为基础是不同的(我会谈谈波动后)。由于是,在 event.values​​ 是无用的,因为它给我们的坐标,但我们不知道依据是什么,即相对于一定的基础,我们知道。

Thus even though m does not change, but the event.values returns by the sensors are different because the basis is different (I will talk about fluctuation later). As is, the event.values are useless because it gives us the coordinates but we do not know what the basis is, i.e with respect to some basis we know.

现在的问题是:是否有可能找到 米坐标相对于固定的世界的基础上( W_1 W_2 w_3 )其中 W_1 对东指出, W_2 则指向磁北和 w_3 点向上指向天空?

Now the question is: is it possible to find the coordinates of a and m with respect to the fixed world basis (w_1, w_2, w_3) where w_1 points toward East, w_2 points toward magnetic North and w_3 points up toward the sky?

答案是肯定提供了2个重要假设是满意的。
有了这两个假设它是简单的计算(只有几个交叉的产品)基矩阵的研究从基础的变化( X ž)的基础上( W_1 W_2 w_3 ),这在Android中被称为<强>旋转矩阵
。然后,向量的坐标 v 相对于基础( W_1 W_2 w_3 )由获得乘法的研究与坐标 v 对于( X ž)。中的 M 因此,相对于世界坐标系中的坐标是在旋转矩阵的不仅仅是产品,并在 event.values​​ 返回的TYPE_MAGNETIC_FIELD传感器和类似的

The answer is yes provided 2 important assumptions are satisfied.
With these 2 assumptions it is simple to calculate (just a few cross products) the change of basis matrix R from the basis (x, y, z) to the basis (w_1, w_2, w_3), which in Android is called the Rotation matrix. Then the coordinates of a vector v with respect to the basis (w_1, w_2, w_3) is obtained by multiply R with the coordinates of v with respect to (x, y, z). Thus the coordinates of m with respect to the world coordinates system is just the product of the rotation matrix and the event.values returned by the TYPE_MAGNETIC_FIELD sensor and similarly for a.

在Android上的旋转矩阵是通过调用获得getRotationMatrix(浮动[] R,漂浮[]我,漂浮[]重力,漂浮[]地磁)和我们通常通过为引力参数和地磁的磁场值返回的加速度值。

In android the rotation matrix is obtained by calling getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic) and we normally pass in the returned accelerometer values for the gravity parameter and the magnetic field values for the geomagnetic.

2重要的假设是:
1 - 重力参数再presents矢量躺在 w_3 ,更特别的是矢量的单独受重力影响,负。照片 因此,如果你传递的加速度计值,而无需过滤,在旋转矩阵会稍微偏离。这就是为什么你需要过滤的加速度计,这样的过滤器值大约只是减去重力矢量。由于重力加速度是在加速度向量的主导因素,通常的低通滤波器就足够了。
2 - 地磁参数再presents矢量躺在由 W_2 跨越的平面和 w_3 载体。这就是它位于北天上的飞机。因此,在的( W_1 W_2 w_3 )的基础上,第一个坐标应为0,因此,X总是很期限小为你上述这是正确的值,理想情况下应该是0。现在的磁场值会出现波动相当多。样的这种预期,就像一个普通罗盘针不会坐以待毙,如果你把它放在你的手,你的手抖了一点。此外,您可能会干扰物体围绕着你,在这种情况下,磁场值未predictable。我曾经测试我的指南针的应用程序坐在附近的一个石头的表,我的指南针是关闭的超过90度,只能通过使用,我发现了一个真正的指南针,有什么错我的应用程序和石头表产生真正的强磁场。
随着重力,你可以过滤加速度值,但没有任何其他知识的一个主导因素,你怎么fitler磁场值?你怎么知道是否有是或不是周围物体的干扰?

The 2 important assumptions are:
1- The gravity parameter represents a vector lying in w_3, more particular it is the minus of the vector influenced by gravity alone.
Thus if you pass in the accelerometer values without filtering, the rotation matrix will be slightly off. That is why you need to filter the accelerometer so that the filter values are approximately just the minus gravity vector. Since the gravitational acceleration is the dominant factor in the accelerometer vector, normally low pass filter is sufficient.
2- The geomagnetic parameter represents a vector lying in the plane spanned by the w_2 and the w_3 vectors. That is it lies in the North-Sky plane. Thus in term of the (w_1, w_2, w_3) basis, the first coordinate should be 0. Therefore, the "X is always very small" as you stated it above is the correct value, ideally it should be 0. Now the magnetic field values will fluctuate quite a bit. This is kind of expected, just as a regular compass needle will not stand still if you keep it in your hand and your hand shakes a little. Also, you may get interference from objects surround you and in this case the magnetic field values are unpredictable. I once test my compass app sitting near a "stone" table and my compass was off by more than 90 degrees, only by using a real compass that I found out that there is nothing wrong with my app and the "stone" table produces a real strong magnetic field.
With gravity as a dominant factor you can filter accelerometer values, but without any other knowledge, how do you fitler magnetic values? How do you know if there is or isn't any interference from surrounding objects?

您可以做很多更像设备的空间位置等是旋转矩阵

You can do a lot more like complete knowledge of your device spatial position etc with the understanding of rotation matrix.

这篇关于转换磁场的X,Y,Z值从设备到全球基准帧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆