【问题标题】:simple Compass using Magnetometer - Android (Java)使用磁力计的简单指南针 - Android (Java)
【发布时间】:2014-11-09 18:32:57
【问题描述】:

我正在使用 Sensortag CC2541(已连接到 Nexus 手机)

我正在使用这个传感器的磁力计来制作一个简单的指南针。我正在使用以下等式来查找北、南、西和东。

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

这是我在 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
            }
        });
    }

但是当我运行它时,无论我移动、旋转多少或改变传感器的位置,指南针总是卡在北方和西方之间。

我可能会犯什么错误?

而且,我在互联网上查看了很多关于指南针帮助的信息。但我什么也找不到。任何编码技巧、伪代码或使用磁力计、加速度计和/或陀螺仪制作指南针的提示???


额外信息

所以这是我的主要课程。这些方法每x 秒调用一次以更新读数。 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);
    }

}

所以基本上在onUpdateMagnetometer 方法中,b 包含我的阅读。它属于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 + "]";
}

}

【问题讨论】:

  • 我很乐意帮助你,但我真的不明白你的代码想要做什么。首先,Direction (y=0, x&gt;0) = 0.0 不是一个等式,我什至不知道这意味着什么。其次,使用单字母变量是让人们忽略您的问题的好方法,因为它们很难阅读。 b 是什么?它是否包含某种来自磁力计的读数?
  • 我从一个网站得到Direction (y=0, x&gt;0) = 0.0。这基本上意味着如果磁力计的y等于0并且它的x大于0,那么指南针的指针必须在0.0(北)。其余的与他的相似。此外,b 是磁力计的读数。在参数中,bPoint3D 类的一个,其中 x, y, z 是磁力计的变量。对象b 还包含诸如norm()toString()...等方法...但唯一且最重要的字段是x,y,z
  • 对,我可以看到 b 是一个 Point3D,它在你的代码中。我的问题是:里面有什么数据? x,y,z 是否描述了单位圆上的一个点、某种四元数、每个轴上的磁场强度等?你没有给我们足够的代码来知道b的值来自哪里。
  • 我想我找到了你从...获得这些“方程式”的网站aerospace.honeywell.com/~/media/Images/… 他们只有两个象限中的arctan,这真是太奇怪了。好像那样不对。
  • 好的,然后在下面看到我的答案。

标签: java android sensors compass magnetometer


【解决方案1】:

您遇到的一个问题是将 3D 磁力计作为 2D 传感器处理。不是。

一种方法是使用来自磁力计和加速度计的数据:

  1. 您从两个传感器获取读数,通过SensorManager.getRotationMatrix 运行这些值以获得初始旋转矩阵。 (请注意,在文档中,矩阵的描述已切换了单位矩阵和旋转矩阵。)
  2. 您将结果通过SensorManager.getOrientation 获取设备在 3D 空间中的方向
  3. 您使用方向数组中的第一个值作为罗盘方向

该最终值表示设备围绕指向地球中心的矢量的旋转,这正是您想要的指南针。使用该值,您可以计算出西、东和南的位置。

代码如下所示:(忽略传感器的设置)

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

希望对你有帮助!

【讨论】:

  • 非常感谢您抽出宝贵时间。但是,我相信您的解释和代码使用了设备的磁力计和加速度计。我正在使用单独的设备(Sensortag cc2541)。我必须从该设备获取磁场和加速。该设备具有以下功能。在磁力计和加速度计中,我可以得到xyz。您的代码具有我的传感器库可能没有的功能和方法。 我已经更新了问题并添加了磁力计和加速度计的方法和功能
  • 另外,这种方法不需要校准吗?我的意思是从你提供的代码中,它会自动检测到北方?。
  • 查看您更新的问题,我看不出您无法使用 Android 传感器库的任何原因。唯一的区别是我的示例使用具有三个数据点的数组(这是 Android 原生提供传感器数据的方式)而不是您的 Point3D。作为起点,您可以做的是使用 Nexus 设备的加速度计/磁力计来实现这一点,因为此示例确实有效。有了这个工作,然后替换来自外部设备的数据。
  • 要校准 Nexus 手机的内置磁力计,您只需将手机按图 8 移动,让 Android 校准它。不幸的是,我不知道如何使用您的外部设备来完成。
猜你喜欢
  • 2012-05-02
  • 1970-01-01
  • 2015-01-05
  • 2011-12-29
  • 2013-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多