Преобразование данных акселерометра из координат устройства в координаты реального мира
Мне очень жаль, если это очень простой вопрос, но у меня нет выбора, но я задаю его: как вы переводите данные акселерометра из координат устройства в координаты реального мира?
Я имею в виду, если предположить, что акселерометр дает мне такие значения (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
is / может даже использоваться как истинный северный вектор внутри координат устройства.
Мне кажется, что получение значений ускорения в реальном мире является просто математическим преобразованием этих данных. Я просто не могу понять это.
Заранее спасибо.
Отредактировано:
Я попытался напрямую умножить компоненты 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 ответов
Оки, я сам решил это математически, поэтому, пожалуйста, потерпите меня.
Если вы хотите перевести вектор ускорения 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])));
Попробуйте это, это работает для меня
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;
}
}
Вам необходимо знать эталонную систему координат, которая также дает вам ориентацию вашего устройства в пределах "реальных" координат мира. Без этой информации кажется невозможным преобразовать ваши данные во что-нибудь полезное.
Например, имеет ли ваше устройство тип датчика направления, который поможет понять данные акселерометра (например, гироскоп и компас?)
Я имею дело с той же проблемой. Что вы можете сделать, так как у вас есть матрица R[], умножьте свой вектор ускорения и вуаля.
float resultVec[] = new float[4];
Matrix.multiplyMV(trueacceleration, 0, R, 0, accelerometervalues, 0);
PS: значения акселерометра должны быть вектором из 4 полей, просто добавьте 0 к последнему полю.
Это то, что я использовал для отображения данных акселерометра с локальной (мобильной) системы координат на систему координат Земли, чтобы избавиться от ориентации в зависимости. Так как в земной системе 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;
}