从设备坐标系加速到绝对坐标系

30

我可以从我的Android设备上读取一个线性加速度值数组(在设备的坐标系中)和一个绝对方向值数组(在地球的坐标系中)。 我需要的是在后者的坐标系中获得线性加速度值。

我该如何转换它们呢?

编辑 在Ali的评论回复后:

好的,如果我理解正确,当我测量线性加速度时,手机的位置完全不重要,因为读数是在地球的坐标系中给出的。 对吗?

但我刚刚进行了一项测试,将手机放在不同的位置,并获得了不同轴的加速度。 这里有3对图片 - 第一张显示我放置设备的方式(抱歉我的“画家技能”),第二张显示直线加速度传感器提供的数据读数:

  1. 将设备放在左侧

first position first readings

  1. 设备仰卧

second position second readings

  1. 设备直立

enter image description here enter image description here

现在 - 为什么在第三种情况下,加速度沿Z轴发生(而不是Y),因为设备位置无关紧要?


你在测量时手机是静止的吗?垂直轴上是什么?例如,-14代表什么意思? - Ali
在测量过程中,我沿着西-东轴(就像那些画的图一样)非常快地移动手机,以创建加速度读数(-14是以m/s*s为单位的加速度)。并且如此所示[链接](https://dev59.com/_G855IYBdhLWcg3wvW_d),测量坐标系是手机自身的坐标系。 - alex
好的,现在我明白你的实验了。我会修正我的答案。 - Ali
好的,这是我的错误,非常抱歉。 我已经修正了我的答案。 另外,我已经为你的问题点了赞。 - Ali
1
@MuhammadBabar 编写了将读数保存到设备文件上,并手动导入Excel的程序,没有任何花哨的功能。 - alex
显示剩余5条评论
4个回答

46

我终于成功解决了!所以要在地球坐标系中获得加速度向量,需要:

  1. SensorManager.getRotationMatrix()获取旋转矩阵(float[16]类型,以便后续使用android.opengl.Matrix类),使用SENSOR.TYPE_GRAVITYSENSOR.TYPE_MAGNETIC_FIELD传感器值作为参数。
  2. 对旋转矩阵使用android.opengl.Matrix.invertM()进行反转(不是转置!)
  3. 使用Sensor.TYPE_LINEAR_ACCELERATION传感器来获取线性加速度向量(在设备坐标系中)
  4. 使用android.opengl.Matrix.multiplyMV()将旋转矩阵乘以线性加速度向量。

就这样啦!希望能为其他人节省宝贵的时间。

感谢Edward Falk和Ali的提示!!


2
在第四步中,您不能直接使用传感器数据,因为其长度为3,而您需要长度为4。您是在末尾还是开头添加0或1? - Nino van Hooff
2
@NinovanHooff 在第四个位置(数组[3])应该有0值。 - alex
2
反转可能是因为旋转矩阵是根据世界坐标系计算的,而不是手机坐标系,正如这里所提到的(http://gentlenav.googlecode.com/files/DCMDraft2.pdf)。这篇科学论文在第III.A节中描述了相同的方法 (http://www.ece.nus.edu.sg/stfpage/elesohws/PerCom_Pedestrian_Tracking.pdf)。 - ravemir
2
@alex,你能发一下代码吗?你是如何将线性加速度修正为世界坐标系的?我的传感器监听器正在使用Sensor.TYPE_ROTATION_VECTOR创建旋转矩阵,效果很好,但我似乎无法按照你描述的方式修正线性加速度..如果有代码示例就太好了。 - erik
2
你为什么在 SensorManager.getRotationMatrix() 中使用了 SENSOR.TYPE_GRAVITY?文档(http://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[]))中说要使用 TYPE_ACCELEROMETER 的值。 - orak
显示剩余13条评论

20

根据 @alex 的回答,这是代码片段:

private float[] gravityValues = null;
private float[] magneticValues = null;

 @Override
    public void onSensorChanged(SensorEvent event) {
        if ((gravityValues != null) && (magneticValues != null) 
            && (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)) {

            float[] deviceRelativeAcceleration = new float[4];
            deviceRelativeAcceleration[0] = event.values[0];
            deviceRelativeAcceleration[1] = event.values[1];
            deviceRelativeAcceleration[2] = event.values[2];
            deviceRelativeAcceleration[3] = 0;

            // Change the device relative acceleration values to earth relative values
            // X axis -> East
            // Y axis -> North Pole
            // Z axis -> Sky

            float[] R = new float[16], I = new float[16], earthAcc = new float[16];

            SensorManager.getRotationMatrix(R, I, gravityValues, magneticValues);

            float[] inv = new float[16];

            android.opengl.Matrix.invertM(inv, 0, R, 0);
            android.opengl.Matrix.multiplyMV(earthAcc, 0, inv, 0, deviceRelativeAcceleration, 0);
           Log.d("Acceleration", "Values: (" + earthAcc[0] + ", " + earthAcc[1] + ", " + earthAcc[2] + ")");                

        } else if (event.sensor.getType() == Sensor.TYPE_GRAVITY) {
            gravityValues = event.values;
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magneticValues = event.values;
        } 
   }

2
成功了!感谢 @orak!我没有重力传感器类型,所以我只使用了低通滤波器来获取它。 - Kanat
抱歉 @orak,但是“started”变量是关于什么的?控制流程吗?我不太确定。 - Saeger
1
@Saeger 你说得对,这不是必需的,我可能写它是为了流程控制,但后来我并不需要它。 - orak
@orak,能否请您解释一下,如果我想让X轴指向真北,而Z垂直,我应该怎么做? - pixie
@pixie 只需要交换 x 和 y 的值吗? - orak

5

根据文档,您可以在手机坐标系中获取线性加速度。

通过将向量乘以旋转矩阵,您可以将任何向量从手机坐标系转换到地球坐标系。您可以从getRotationMatrix()获取旋转矩阵。

(也许已经有一个函数为您执行这个乘法,但我不从事Android编程,也不熟悉它的API。)

关于旋转矩阵的一个很好的教程是方向余弦矩阵IMU:理论手稿。祝好运!


谢谢你的回答!看起来应该可以正常工作,但我不知道为什么它没有..请看一下我对Edward Falk答案的评论,我在那里解释了仍然存在的问题。 - alex

1
首先,如果你想在Android上进行惯性导航,那么你会面临很大的挑战。智能手机中使用的廉价传感器精度不够高。尽管在短距离范围内进行了一些有趣的惯性导航工作,例如建筑物内部。你可能会找到有关这个主题的论文。搜索“Motion Interface Developers Conference”,你可能会找到一些有用的东西——这是Invensense几个月前举办的会议。
其次,线性加速度是在设备坐标系中,而不是世界坐标系中。你需要自行转换,这意味着需要知道设备的3D方向。
你需要使用支持虚拟传感器TYPE_GRAVITY和TYPE_LINEAR_ACCELERATION的Android版本。你需要一个陀螺仪装置来获得相对准确和精密的读数。
在内部,系统将陀螺仪、加速度计和磁力计结合起来,以便得出设备方向的真实值。这有效地将加速度计设备分为重力和加速度两个部分。
所以你想做的是为TYPE_GRAVITY、TYPE_LINEAR_ACCELERATION和TYPE_MAGNETOMETER设置传感器监听器。使用重力和磁力计数据作为SensorManager.getRotationMatrix()的输入,以获取将世界坐标转换为设备坐标或反之亦然的旋转矩阵。在这种情况下,你需要"反过来"。也就是说,通过将它们乘以方向矩阵的转置来将线性加速度输入转换为世界坐标。

我按照您的建议进行了操作 - 通过将旋转矩阵R(使用重力和磁场数据提供的getRotationMatrix()函数)与设备坐标系中的线性加速度向量V(来自lin.acc.传感器)相乘,我获得了地球坐标系中的加速度向量A,如下所示:A=RV。但是我没有得到好的结果。我将手机放在立起的位置(Y轴朝上),并上下移动它。Lin.acc.传感器应该指示Y轴上的运动,而变换后的向量应该指示Z轴上的运动。但我得到了向量V向量A。那么...? - alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接