Преобразование данных акселерометра из координат устройства в координаты реального мира - PullRequest
10 голосов
/ 29 марта 2011

Мне очень жаль, если это очень простой вопрос, но у меня нет выбора, но я задаю его: как вы переводите данные акселерометра из координат устройства в координаты реального мира?

Я имею в виду, если предположить, что акселерометр дает мне такие значения (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, и на меня (trueaccel = accel * R * I), и это тоже не сработало. Так же как и вызов remapcoordinates(), а затем умножение в любой из предыдущих форм.

Кто-нибудь имеет представление о том, что я делаю неправильно?

Ответы [ 5 ]

6 голосов
/ 03 апреля 2011

Оки, я сам разработал это математически, поэтому, пожалуйста, потерпите меня.

Если вы хотите перевести вектор ускорения 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 голос
/ 23 июня 2017

Попробуйте, это работает для меня

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;
        }
    }
1 голос
/ 16 октября 2011

Я имею дело с той же проблемой.Что вы можете сделать, так как у вас есть матрица R [], умножьте свой вектор ускорения и вуаля.

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

PS: значения акселерометра должны быть вектором из 4 полей, просто добавьте 0 к последнему полю.

1 голос
/ 29 марта 2011

Вам необходимо знать эталонную систему координат, которая также дает вам ориентацию вашего устройства в пределах «реальных» координат мира. Без этой информации невозможно преобразовать ваши данные во что-то полезное.

Например, имеет ли ваше устройство тип датчика направления, который поможет разобраться с данными акселерометра (например, гироскоп и компас?)

0 голосов
/ 08 апреля 2014

Это то, что я использовал для отображения данных акселерометра с локальной (мобильной) системы координат на систему координат Земли, чтобы избавиться от ориентации в зависимости. Так как в земной системе Z-ось направлена ​​к небу и должна показывать значение ~ = 9,81 м / с ^ 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;
    }
...