简体   繁体   English

使用磁力计的简单指南针-Android(Java)

[英]simple Compass using Magnetometer - Android (Java)

I am using Sensortag CC2541 (connected to Nexus phone) 我正在使用Sensortag CC2541 (已连接Nexus手机)

I am using the Magnetometer of this sensor to make a simple compass. 我正在使用此传感器的磁力计制作一个简单的指南针。 I am using the following equations to find North, South, West, and East. 我正在使用以下方程式找到北,南,西和东。

Direction (y>0) = 90 - [arcTAN(x/y)]*180/pi

Direction (y<0) = 270 - [arcTAN(x/y)]*180/pi

Direction (y=0, x<0) = 180.0

Direction (y=0, x>0) = 0.0

Here's the code i'm using in my updateMagnetometer method 这是我在updateMagnetometer方法中使用的代码

    @Override
    public void onUpdateMagnetometer(SensorTagManager mgr, Point3D b) {
        super.onUpdateMagnetometer(mgr, b);

        double m=0;

        if(b.x < 0.0 && (b.y < 0.01 && b.y > 0.0))
            m = 180;
        else if(b.x > 0.0 && (b.y < 0.01 && b.y > 0.0))
            m = 0;
        else if(b.y > 0.0)
            m = 90 - (Math.atan(b.x/b.y))*(180/Math.PI);
        else if(b.y < 0.0)
            m = 270 - (Math.atan(b.x/b.y))*(180/Math.PI);

        final float rot = (float) m;

        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                pointer.setRotation(-rot); //pointer is the image of my compass
            }
        });
    }

But when I run it, the compass is always stuck between North and West NO MATTER how much I move, rotate, or change the position of the sensor. 但是,当我运行它时,指南针始终固定在“北和西”之间,无论我移动,旋转或更改传感器的位置有多少。

Any mistakes I might be doing? 我可能会犯任何错误?

And also, I have looked a lot on the internet for making compass help. 而且,我在互联网上寻找了很多指南针帮助。 But I can't find anything. 但是我什么也找不到。 Any coding tips, pseudo code, or hint to make compass using magnetometer, accelerometer, and/or gyroscope??? 使用磁强计,加速度计和/或陀螺仪制作罗盘的任何编码技巧,伪代码或提示???


Extra info 额外信息

So this is in my main class. 这是我的主要课程。 These methods are called every x seconds to update the readings. x秒调用一次这些方法以更新读数。 PS: my sensor has 2 buttons (right and left) PS:我的传感器有2个按钮(左右)

    mStManager.enableSensor(Sensor.MAGNETOMETER,MAGNETOMETER_UPDATE_PERIOD);
    mStManager.enableSensor(Sensor.ACCELEROMETER,MAGNETOMETER_UPDATE_PERIOD);    

    @Override
    public void onUpdateAmbientTemperature(SensorTagManager mgr, double temp) {
        super.onUpdateAmbientTemperature(mgr, temp);
    }

    @Override
    public void onUpdateAccelerometer(SensorTagManager mgr, Point3D acc) {
        super.onUpdateAccelerometer(mgr, acc);

    }

    @Override
    public void onUpdateBarometer(SensorTagManager mgr, double pressure, double height) {
        super.onUpdateBarometer(mgr, pressure, height);
    }

    @Override
    public void onUpdateGyroscope(SensorTagManager mgr, Point3D ang) {
        super.onUpdateGyroscope(mgr, ang);
    }

    @Override
    public void onUpdateHumidity(SensorTagManager mgr, double rh) {
        super.onUpdateHumidity(mgr, rh);
    }

    @Override
    public void onUpdateInfraredTemperature(SensorTagManager mgr, double temp) {
        super.onUpdateInfraredTemperature(mgr, temp);
    }

    @Override
    public void onUpdateKeys(SensorTagManager mgr, boolean left, boolean right) {
        super.onUpdateKeys(mgr, left, right);

        if (right) {
            mgr.calibrateMagnetometer();
        }
    }

    @Override
    public void onUpdateMagnetometer(SensorTagManager mgr, Point3D b) {
        super.onUpdateMagnetometer(mgr, b);
    }

}

So basically in the onUpdateMagnetometer method, the b contains my reading. 因此,基本上在onUpdateMagnetometer方法中, b包含我的读数。 It is of class Point3D which is shown below 它是Point3D类,如下所示

public class Point3D {
public double x, y, z;

public Point3D(double x, double y, double z) {
    this.x = x;
    this.y = y;
    this.z = z;
}

public double norm() {
    return Math.sqrt(x*x + y*y + z*z);
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(x);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(y);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(z);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Point3D other = (Point3D) obj;
    if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
        return false;
    if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
        return false;
    if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
        return false;
    return true;
}

public String toString() {
    return "[" + this.x + ", " + this.y + ", " + this.z + "]";
}

}

One problem you have is that you're handling the 3D magnetometer as a 2D sensor. 您遇到的一个问题是将3D磁力计当作2D传感器使用。 It isn't. 不是。

One way to do this is to use data from both the magnetometer AND the accelerometer: 一种方法是同时使用磁力计和加速度计的数据:

  1. You capture a reading from both sensors, run those values through SensorManager.getRotationMatrix to get an initial rotation matrix. 您捕获了两个传感器的读数,并通过SensorManager.getRotationMatrix运行这些值以获取初始旋转矩阵。 (Note that in the documentation, the description of the matrices has the identity matrix and the rotation matrix switched.) (请注意,在文档中,矩阵的描述具有单位矩阵和旋转矩阵。)
  2. You put the result through SensorManager.getOrientation to get the device's orientation in 3D space 您将结果放入SensorManager.getOrientation中以获取设备在3D空间中的方向
  3. You use the first value in the orientation array as the compass direction 您将方向数组中的第一个值用作指南针方向

That final value represents the device's rotation around a vector pointed towards the center of the earth, which is exactly what you want for a compass. 最终值表示设备围绕指向地球中心的向量的旋转,这正是您想要的指南针。 Using THAT value, you can then calculate the location of west, east, and south. 然后使用THAT值计算西,东和南的位置。

The code would look something like this: (ignoring the setup of the sensors) 代码看起来像这样:(忽略传感器的设置)

// globals
private float[] gravityData = new float[3];
private float[] geomagneticData  = new float[3];
private boolean hasGravityData = false;
private boolean hasGeomagneticData = false;
private double rotationInDegrees;


@Override
public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()){
        case Sensor.TYPE_ACCELEROMETER:
            System.arraycopy(event.values, 0, gravityData, 0, 3);
            hasGravityData = true;
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            System.arraycopy(event.values, 0, geomagneticData, 0, 3);
            hasGeomagneticData = true;
            break;
        default:
            return;
    }

    if (hasGravityData && hasGeomagneticData) {
        float identityMatrix[] = new float[9];
        float rotationMatrix[] = new float[9];
        boolean success = SensorManager.getRotationMatrix(rotationMatrix, identityMatrix,
            gravityData, geomagneticData);

        if (success) {
            float orientationMatrix[] = new float[3];
            SensorManager.getOrientation(rotationMatrix, orientationMatrix);
            float rotationInRadians = orientationMatrix[0];
            rotationInDegrees = Math.toDegrees(rotationInRadians);

            // do something with the rotation in degrees
        }
    }
}

I hope that helps! 希望对您有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM