问题
I need the device's azimuth and roll values for an augmented reality app I'm working on. I'm getting the rotation matrix - from getRotationMatrix
- using the accelerometer and the magnetic field. Since this is an AR app, I need to render objects that wouldn't change their positions in the real world (when seen through the camera) when the device is moved.
The problem is, when physically rolling the device (in attempts to observe the variations in the pitch values), I can observe changes in the azimuth orientation values, even though that should remain stable. Moreover, the azimuth strays off only when the pitch is changing, and returns back to its original value when the rolling is stopped. So to clarify, as I start the roll, the azimuth starts changing as well, and when the roll stops, the azimuth slowly returns back to the correct value. I can't figure out why the azimuth is behaving so.
Here is the code that gets the sensor data and calculates the rotation matrix.
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
switch (sensorEvent.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
accel = lowPass(sensorEvent.values.clone(), accel);
break;
case Sensor.TYPE_MAGNETIC_FIELD:
magnet = lowPass(sensorEvent.values.clone(), magnet);
break;
}
if (accel == null || magnet == null)
return;
if (!SensorManager.getRotationMatrix(rotationMatrix, null, accel, magnet))
return;
SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_Z,
SensorManager.AXIS_MINUS_X, outRotationMatrix); // Remapping because the device is held in landscape.
SensorManager.getOrientation(outRotationMatrix, orientationValues);
currentOrientation = (float) (Math.toDegrees(orientationValues[0]) + this.getDeclination()); //Azimuth; (Degrees);
eyeLevelInclination = (float) Math.toDegrees(orientationValues[1]); //Pitch; (Degrees); down is 90 , up is -90.
deviceOrientation = (float) Math.toDegrees(orientationValues[2]);
sendSensorBroadcast();
}
So in the code above, currentOrientation
(responsible for horizontally stabilizing the rendered AR objects) changes during device roll, when only eyeLevelInclination
should be changing (responsible for vertically stabilizing the rendered AR object).
I'm also using a low pass filter to stabilize the sensor data. Although I dont believe this is responsible for the azimuth variations, I'm putting its code here regardless-
static final float ALPHA = 0.15f;
/**
* @see Adapted from http://blog.thomnichols.org/2011/08/smoothing-sensor-data-with-a-low-pass-filter
*/
protected float[] lowPass( float[] input, float[] output ) {
if ( output == null ) return input;
for ( int i=0; i<input.length; i++ ) {
output[i] = output[i] + ALPHA * (input[i] - output[i]);
}
return output;
}
Apologies if the question isn't very clear. It's a bit hard to explain the problem.
Edit: I've tried using raw values, instead of going through a low pass filter and the azimuth still changes. I notice that the rendered object goes along a diagonal when changing the pitch, when it should only be going vertically up or down. It moves diagonally as the azimuth and the pitch both are changing when I physically change the device's pitch, causing the diagonal movement. Maybe there's something wrong with my axis?
回答1:
Doesn't matter what orientation the device is held (Portrait or Landscape and their variations), if you are interested in the back camera direction then you have to call
SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X,
SensorManager.AXIS_Z, outRotationMatrix);
The above call together with getOrientation
gives the direction of the back camera with respect to magnetic north independent of the orientation of the device (Portrait, Landscape or anything in between).
回答2:
Seeing how I was able to find no solution to this issue, I started to look at other possible ways of handling the sensors.
I found out a much better way of handling the sensor data, that eliminated not only the azimuth swaying, but also the need to have a filter to smooth the sensor values:
Instead of using Sensor.TYPE_ACCELEROMETER
and Sensor.TYPE_MAGNETIC_FIELD
values to calculate the rotation matrix, I used the Sensor.TYPE_ROTATION_VECTOR
sensor. Then, got the rotation matrix from this, and as done earlier, got the orientation array from it.
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
switch (sensorEvent.sensor.getType()) {
case Sensor.TYPE_ROTATION_VECTOR:
rotationMatrix=new float[16];
SensorManager.getRotationMatrixFromVector(rotationMatrix, sensorEvent.values);
}
SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X,
SensorManager.AXIS_Z, outRotationMatrix); // Remap coordinate System to compensate for the landscape position of device
SensorManager.getOrientation(outRotationMatrix, orientationValues);
currentOrientation = (float) (Math.toDegrees(orientationValues[0]) + this.getDeclination()); //Azimuth; (Degrees);
eyeLevelInclination = (float) Math.toDegrees(orientationValues[1]); //Pitch; (Degrees); down is 90 , up is -90.
deviceOrientation = (float) Math.toDegrees(orientationValues[2]); // Roll;
sendSensorBroadcast(); // Let other classes know of update to sensor data.
As you can see in my question above, I was using a low pass filter to smooth the azimuth and pitch values, but this wasn't needed anymore, as the values were pretty smooth without any filtering. Plus, the azimuth swaying is almost gone now. I say almost because you can maybe still see a bit of it happening, but its barely noticeable, maybe about half a degree.
I was stuck with this problem for quite some time, hopefully this will help someone else too.
来源:https://stackoverflow.com/questions/22794011/azimuth-values-changing-when-changing-pitch