转换磁场X,Y,从装置Z的值到全局参考帧(Convert magnetic field X, Y,

2019-07-21 06:52发布

当您使用TYPE_MAGNETOMETER传感器,你得到的X,Y,相对于设备的方向磁场强度Z值。 我想是将这些值转换为全局参考坐标,澄清:用户需要的设备,测量这些值,比旋转设备一定程度上围绕任何轴,并得到〜相同的值。 请在下面找到类似的问题: 获得在全球坐标磁场值 我怎样才能得到磁场矢量,独立于设备的旋转? 在这种应答样品溶液被描述(它是直线加速,但我认为这并不重要): https://stackoverflow.com/a/11614404/2152255我用它和我的3个值,X总是很小(不认为这是正确的),Y和Z都OK,但他们还是改了一下,当我旋转设备。 怎么会调整? 而且可以把它都会解决? 我用简单的卡尔曼滤波器来近似测量值,因为W / O它,我得到即使设备不动安静的不同的值/旋转可言。 请在下面找到我的代码:

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) {
    }
}

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

Answer 1:

在我对你上面提供的链接所检查出的答案评论,我提到我的答案很简单,在计算加速参照真北

让我有更多的澄清再回答这里。 答案是旋转矩阵磁场值的乘积。 如果您在继续阅读“X总是非常小”是正确的值。

加速度计和磁场传感器分别测量该装置的设备位置的加速度和地球的磁场。 他们在3维空间中的向量,让分别打电话给他们am。
如果你停步不前和旋转你的设备,理论上M不改变假设有来自周围物体无磁场干扰(实际上应该变化不大,如果你走动,因为地球的磁场应该在短距离内变化不大)。 但a不改变,即使它不应该在大多数情况下激烈。

现在,在3维空间中的矢量v可以由3元组(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)。

在Android设备的基础是(X,Y,Z),其中,对于大多数电话,x是沿着短边和指向右侧,y为沿长边和指向上和z是垂直于屏幕和指出。
现在此基础上变化的设备的位置发生变化。 人们可以认为这些碱作为时间的函数(X(t),Y(t)Z(t))的,在数学术语它是一个移动的坐标系。

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

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

答案是肯定的提供了2个重要的假设是满意的。
与这些2个假设它是简单的计算(只是几个横产品)基础矩阵R的从基础到基础(W_1,W_2,w_3),其在Android中被称为旋转的变化(X,Y,Z) 矩阵 。 然后相对于该基础(W_1,W_2,w_3)一个矢量v的坐标是通过乘法R 3相对于(X,Y,Z)的坐标而获得。 因此m的相对于世界坐标系统的坐标是旋转矩阵的只是产品和由TYPE_MAGNETIC_FIELD传感器同样地,对于一个所返回的event.values。

在机器人的旋转矩阵是通过调用getRotationMatrix得到(浮动[] R,浮[] I,浮动[]重力,浮[]地磁),我们通常在返回的加速计值通为重力参数和用于磁场值地磁。

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

你可以做更多的事情像你的设备的空间位置等与旋转矩阵的理解的完整的知识。



Answer 2:

按照上面的解释,这样做

private static final int TEST_GRAV = Sensor.TYPE_ACCELEROMETER;
private static final int TEST_MAG = Sensor.TYPE_MAGNETIC_FIELD;
private final float alpha = (float) 0.8;
private float gravity[] = new float[3];
private float magnetic[] = new float[3];

public void onSensorChanged(SensorEvent event) {
    Sensor sensor = event.sensor;
    if (sensor.getType() == TEST_GRAV) {
            // Isolate the force of gravity with the low-pass filter.
              gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
              gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
              gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
    } else if (sensor.getType() == TEST_MAG) {

            magnetic[0] = event.values[0];
            magnetic[1] = event.values[1];
            magnetic[2] = event.values[2];

            float[] R = new float[9];
            float[] I = new float[9];
            SensorManager.getRotationMatrix(R, I, gravity, magnetic);
            float [] A_D = event.values.clone();
            float [] A_W = new float[3];
            A_W[0] = R[0] * A_D[0] + R[1] * A_D[1] + R[2] * A_D[2];
            A_W[1] = R[3] * A_D[0] + R[4] * A_D[1] + R[5] * A_D[2];
            A_W[2] = R[6] * A_D[0] + R[7] * A_D[1] + R[8] * A_D[2];

            Log.d("Field","\nX :"+A_W[0]+"\nY :"+A_W[1]+"\nZ :"+A_W[2]);

        }
    }


文章来源: Convert magnetic field X, Y, Z values from device into global reference frame