将加速度计的数据从设备坐标系转换为真实世界坐标系

14
我很抱歉,如果这是一个非常基本的问题,但我别无选择,只能问一下:如何将设备坐标中的加速度计数据转换为真实世界坐标系中的数据?
我的意思是,假设加速度计给出了类似于(Ax,Ay,Az)-在设备坐标中-的值,那么我应该应用哪些变换来将这些值转换为(Ax',Ay',Az')-在真实世界坐标系中-,以便我可以使用真实世界坐标系中的加速度向量来计算设备是否正在向北、东、南、西等方向加速?
我已经在过去几天里一直在处理这个问题。起初我认为这不应该很难,但在搜索了数十页后,我没有找到任何有用的东西。
顺便说一下,这是一些我迄今为止已经实现的代码:
    private SensorEventListener mSensorEventListener = new SensorEventListener() {

    public void onAccuracyChanged(Sensor sensor, int accuracy){
}

    public void onSensorChanged(SensorEvent event) {
        switch(event.sensor.getType()){
        case Sensor.TYPE_ACCELEROMETER:
            accelerometervalues = event.values.clone();
            AX.setText(accelerometervalues[0]+"");
            AY.setText(accelerometervalues[1]+"");
            AZ.setText(accelerometervalues[2]+"");
            break;
        case Sensor.TYPE_ORIENTATION:
            orientationvalues = event.values.clone();
            azimuth.setText(orientationvalues[0]+"");
            pitch.setText(orientationvalues[1]+"");
            roll.setText(orientationvalues[2]+"");
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            geomagneticmatrix =event.values.clone();
            TAX.setText(geomagneticmatrix[0]+"");
            TAY.setText(geomagneticmatrix[1]+"");
            TAZ.setText(geomagneticmatrix[2]+"");
            break;
        }
        if (geomagneticmatrix != null && accelerometervalues != null) {
            float[] R = new float[16];
            float[] I = new float[16];
            SensorManager.getRotationMatrix(R, I, accelerometervalues, geomagneticmatrix);
            //What should I do here to transform the components of accelerometervalues into real world acceleration components??
        }
   }
};

我有:

accelerometervalues中本地坐标下的加速度矢量。

geomagneticmatrix中的磁场值向量。

orientationvalues中的方位角、俯仰角和滚动角。

旋转矩阵R。倾斜矩阵I

我认为所有必要的信息都在那里,方位角、俯仰角和滚动角应该描述设备坐标系相对于真实世界坐标系的位移。此外,我相信R甚至可用作设备坐标系内的真北矢量。

在我看来,获得在真实世界中的加速度值只需要从这些数据进行数学转换即可。我只是想不出来。

提前感谢。

编辑:

我已尝试直接将accelerometervalues的分量与旋转矩阵R相乘(trueaccel = accel * R),但未成功。

                    trueacceleration[0]= accelerometervalues[0]*R[0]+accelerometervalues[1]*R[1]+accelerometervalues[2]*R[2];
                trueacceleration[1]= accelerometervalues[0]*R[1]+accelerometervalues[1]*R[4]+accelerometervalues[2]*R[7];
                trueacceleration[2]= accelerometervalues[0]*R[2]+accelerometervalues[1]*R[5]+accelerometervalues[2]*R[8];

我也尝试过将accelerometervalues与倾斜矩阵I相乘。同时与R和I都相乘(trueaccel=accel*R*I),但这两种方法都没有奏效。调用remapcoordinates()并以任何一种方式进行乘法运算也不起作用。
有人知道我做错了什么吗?
5个回答

6

好的,我已经自己通过数学计算解决了这个问题,请耐心等待。

如果你想将一个加速度向量accelerationvalues转换为一个以真实世界坐标表示的加速度向量trueacceleration,一旦你在一个orientationvalues向量中存储了方位角、俯仰角和翻滚角,只需要按照以下步骤进行:

                trueacceleration[0] =(float) (accelerometervalues[0]*(Math.cos(orientationvalues[2])*Math.cos(orientationvalues[0])+Math.sin(orientationvalues[2])*Math.sin(orientationvalues[1])*Math.sin(orientationvalues[0])) + accelerometervalues[1]*(Math.cos(orientationvalues[1])*Math.sin(orientationvalues[0])) + accelerometervalues[2]*(-Math.sin(orientationvalues[2])*Math.cos(orientationvalues[0])+Math.cos(orientationvalues[2])*Math.sin(orientationvalues[1])*Math.sin(orientationvalues[0])));
            trueacceleration[1] = (float) (accelerometervalues[0]*(-Math.cos(orientationvalues[2])*Math.sin(orientationvalues[0])+Math.sin(orientationvalues[2])*Math.sin(orientationvalues[1])*Math.cos(orientationvalues[0])) + accelerometervalues[1]*(Math.cos(orientationvalues[1])*Math.cos(orientationvalues[0])) + accelerometervalues[2]*(Math.sin(orientationvalues[2])*Math.sin(orientationvalues[0])+ Math.cos(orientationvalues[2])*Math.sin(orientationvalues[1])*Math.cos(orientationvalues[0])));
            trueacceleration[2] = (float) (accelerometervalues[0]*(Math.sin(orientationvalues[2])*Math.cos(orientationvalues[1])) + accelerometervalues[1]*(-Math.sin(orientationvalues[1])) + accelerometervalues[2]*(Math.cos(orientationvalues[2])*Math.cos(orientationvalues[1])));

1
你能详细说明一下并提供更多细节,关于你是如何得出那个解决方案的吗?我的意思是解释一下这个方程,因为它看起来很复杂。 - pzo
1
我已经测试了你的片段,但是我得到的只是一个相当嘈杂的随机信号。 - pzo
你好。你使用了哪个公式/矩阵将其转换为真实世界坐标? - this-Me

2

试一下这个,对我有效

private float[] gravityValues = null;
    private float[] magneticValues = null;
    private SensorManager mSensorManager = null;  
private void registerSensorListener(Context context) {
        mSensorManager = (SensorManager) context.getSystemService(SENSOR_SERVICE);
        mSensorManager.registerListener(this,
                mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_FASTEST);

        mSensorManager.registerListener(this,
                mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
                SensorManager.SENSOR_DELAY_FASTEST);

        mSensorManager.registerListener(this,
                mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                SensorManager.SENSOR_DELAY_FASTEST);

        mSensorManager.registerListener(this,
                mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY),
                SensorManager.SENSOR_DELAY_FASTEST);
    }

    @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;

            Log.d("Raw Acceleration::","Values: (" + event.values[0] + ", " + event.values[1] + ", " + event.values[2] + ")");

            // 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("Earth 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;
        }
    }

你能展示或指向其他非代码解释的来源吗?我的意思是用数学术语来解释。 - k8C

1

我也遇到了同样的问题。您可以这样做,使用R[]矩阵乘以加速度向量即可解决问题。

float resultVec[] = new float[4];
Matrix.multiplyMV(trueacceleration, 0, R, 0, accelerometervalues, 0);

提示:accelerometervalues 必须是一个四元素向量,只需在最后一个元素添加 0 即可。


1

您需要知道参考坐标系,以及设备在“真实”世界坐标系中的方向。如果没有这些信息,将数据转换为有用的内容似乎是不可能的。

例如,您的设备是否具有某种类型的“定向”传感器,可以帮助理解加速度计数据(例如陀螺仪和指南针)?


它具有磁场传感器。除了加速度信息外,我还可以获取有关方向的信息。我已经进行了测试,可以获得设备的实际方位角、俯仰角和旋转角,问题是一旦我拥有了加速度向量(ax,ay,az)-在设备的坐标系中- 和一个方向向量(方位角、俯仰角、旋转角),我不确定如何将设备的加速度转换为真实世界坐标系中的加速度。我在很多网页上搜索过,但没有找到有用的内容。官方文档在这个主题上似乎有点神秘。谢谢你的回复。 - Tommy

0

这是我用来将加速度计数据从本地(移动)参考系映射到地球参考系的方法,以消除方向依赖性。由于在地球参考系中,Z轴指向天空,必须显示值约为9.81m / sec ^ 2。我无法理解的一个现象是,当我将手机放在旋转椅上并以恒定速度旋转时,XEarth和YEarth值显示出90度相位差的旋转,并像正弦/余弦波一样振荡,我认为这是北轴和东轴。

public void onSensorChanged(SensorEvent event) {

        switch(event.sensor.getType()){

           case Sensor.TYPE_ACCELEROMETER:
                 System.arraycopy(event.values, 0, accel, 0, 3);
                     //To get Quternion representation of Accelrometer data              
                     SensorManager.getQuaternionFromVector(quatA , event.values);
             q1.w = quatA[0]; q1.x = quatA[1]; q1.y = quatA[2]; q1.z = quatA[3];
           break;

           case Sensor.TYPE_ROTATION_VECTOR:
                SensorManager.getRotationMatrixFromVector(rotationMatrix1,event.values);
                System.arraycopy(event.values, 0, rotationVector, 0, 3);
                SensorManager.getQuaternionFromVector(quat , event.values);
                q2.w = quat[0]; q2.x = quat[1]; q2.y = quat[2]; q2.z = quat[3];
                rotationMatrix2 = getRotationMatrixFromQuaternion(q2);
                rotationResult =  matrixMultiplication(accel,rotationMatrix2);
                //You can  use rotationMatrix1 or rotationMatrix2  

             break;
//Accel Data rotated as per earth frame of reference 
//rotationResult[0]; 
//rotationResult[1];
//rotationResult[2];

        }

    private float[] getRotationMatrixFromQuaternion(Quaternion q22) {
        // TODO Auto-generated method stub
        float [] q = new float[4];
        float [] result = new float[9];
        q[0] = q22.w;
        q[1] = q22.x;
        q[2] = q22.y;
        q[3] = q22.z;

        result[0] = q[0]*q[0] + q[1]*q[1] - q[2]*q[2] -q[3]*q[3];
            result[1] = 2 * (q[1]*q[2] - q[0]*q[3]);
            result[2] = 2 * (q[1]*q[3] + q[0]*q[2]);

            result[3] = 2 * (q[1]*q[2] + q[0]*q[3]);
            result[4] = q[0]*q[0] - q[1]*q[1] + q[2]*q[2] - q[3]*q[3];
            result[5] = 2 * (q[2]*q[3] - q[0]*q[1]);

            result[7] = 2 * (q[2]*q[3] + q[0]*q[1]);
            result[6] = 2 * (q[1]*q[3] - q[0]*q[2]);
        result[8] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];

        return result;
    }

 private float[] matrixMultiplication(float[] A, float[] B) {
        float[] result = new float[3];

        result[0] = A[0] * B[0] + A[1] * B[1] + A[2] * B[2];
        result[1] = A[0] * B[3] + A[1] * B[4] + A[2] * B[5];
        result[2] = A[0] * B[6] + A[1] * B[7] + A[2] * B[8];

        return result;
    }

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