问题
I need to get device orientation. As I know usually used TYPE_ACCELEROMETER
and TYPE_MAGNETIC_FIELD
sensors. My problem is that SensorManager.getDefaultSensor
returns me null
for geomagnetic sensor. It returns null
for TYPE_ORIENTATION
sensor too.
manager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensorAcc = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), //normal object
sensorMagn = manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); //null
orientationListener = new OrientationSensorListener();
manager.registerListener(orientationListener, sensorAcc, 10);
manager.registerListener(orientationListener, sensorMagn, 10);
I need another ways to get device orientation.
回答1:
Orientation can be decomposed in three Euler angle : Pitch, Roll and Azimuth.
With only accelerometer datas, you cannot compute your Azimuth, neither the sign of your pitch.
You can try something as this to know something about your pitch and roll :
private final float[] mMagnet = new float[3]; // magnetic field vector
private final float[] mAcceleration = new float[3]; // accelerometer vector
private final float[] mAccMagOrientation = new float[3]; // orientation angles from mAcceleration and mMagnet
private float[] mRotationMatrix = new float[9]; // accelerometer and magnetometer based rotation matrix
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
System.arraycopy(event.values, 0, mAcceleration, 0, 3); // save datas
calculateAccMagOrientation(); // then calculate new orientation
break;
case Sensor.TYPE_MAGNETIC_FIELD:
System.arraycopy(event.values, 0, mMagnet, 0, 3); // save datas
break;
default: break;
}
}
public void calculateAccMagOrientation() {
if (SensorManager.getRotationMatrix(mRotationMatrix, null, mAcceleration, mMagnet))
SensorManager.getOrientation(mRotationMatrix, mAccMagOrientation);
else { // Most chances are that there are no magnet datas
double gx, gy, gz;
gx = mAcceleration[0] / 9.81f;
gy = mAcceleration[1] / 9.81f;
gz = mAcceleration[2] / 9.81f;
// http://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/
float pitch = (float) -Math.atan(gy / Math.sqrt(gx * gx + gz * gz));
float roll = (float) -Math.atan(gx / Math.sqrt(gy * gy + gz * gz));
float azimuth = 0; // Impossible to guess
mAccMagOrientation[0] = azimuth;
mAccMagOrientation[1] = pitch;
mAccMagOrientation[2] = roll;
mRotationMatrix = getRotationMatrixFromOrientation(mAccMagOrientation);
}
}
public static float[] getRotationMatrixFromOrientation(float[] o) {
float[] xM = new float[9];
float[] yM = new float[9];
float[] zM = new float[9];
float sinX = (float) Math.sin(o[1]);
float cosX = (float) Math.cos(o[1]);
float sinY = (float) Math.sin(o[2]);
float cosY = (float) Math.cos(o[2]);
float sinZ = (float) Math.sin(o[0]);
float cosZ = (float) Math.cos(o[0]);
// rotation about x-axis (pitch)
xM[0] = 1.0f;xM[1] = 0.0f;xM[2] = 0.0f;
xM[3] = 0.0f;xM[4] = cosX;xM[5] = sinX;
xM[6] = 0.0f;xM[7] =-sinX;xM[8] = cosX;
// rotation about y-axis (roll)
yM[0] = cosY;yM[1] = 0.0f;yM[2] = sinY;
yM[3] = 0.0f;yM[4] = 1.0f;yM[5] = 0.0f;
yM[6] =-sinY;yM[7] = 0.0f;yM[8] = cosY;
// rotation about z-axis (azimuth)
zM[0] = cosZ;zM[1] = sinZ;zM[2] = 0.0f;
zM[3] =-sinZ;zM[4] = cosZ;zM[5] = 0.0f;
zM[6] = 0.0f;zM[7] = 0.0f;zM[8] = 1.0f;
// rotation order is y, x, z (roll, pitch, azimuth)
float[] resultMatrix = matrixMultiplication(xM, yM);
resultMatrix = matrixMultiplication(zM, resultMatrix);
return resultMatrix;
}
public static float[] matrixMultiplication(float[] A, float[] B) {
float[] result = new float[9];
result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];
result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];
result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];
return result;
}
回答2:
To get the angle of rotation when the device is not flat, implements OrientationEventListener
. onOrientationChanged
will give the device orientation. See https://developer.android.com/reference/android/view/OrientationEventListener.html for detail.
回答3:
i did something like :
public class MainActivity extends AppCompatActivity
{
SensorManager sensorManager;
Sensor sensor;
ImageView imageViewProtractorPointer;
/////////////////////////////////////
///////////// onResume //////////////
/////////////////////////////////////
@Override
protected void onResume()
{
super.onResume();
// register sensor listener again if return to application
if(sensor !=null) sensorManager.registerListener(sensorListener,sensor,SensorManager.SENSOR_DELAY_NORMAL);
}
/////////////////////////////////////
///////////// onCreate //////////////
/////////////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageViewProtractorPointer = (ImageView)findViewById(R.id.imageView2);
// get the SensorManager
sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
// get the Sensor ACCELEROMETER
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
/////////////////////////////////////
///////////// onPause ///////////////
/////////////////////////////////////
@Override
protected void onPause()
{
// Unregister the sensor listener to prevent battery drain if not in use
super.onPause();
if(sensor !=null) sensorManager.unregisterListener(sensorListener);
}
/////////////////////////////////////////////
/////////// SensorEventListener /////////////
/////////////////////////////////////////////
SensorEventListener sensorListener = new SensorEventListener()
{
@Override
public void onSensorChanged(SensorEvent sensorEvent)
{
// i will use values from 0 to 9 without decimal
int x = (int)sensorEvent.values[0];
int y = (int)sensorEvent.values[1];
int angle = 0;
if(y>=0 && x<=0) angle = x*10;
if(x<=0 && y<=0) angle = (y*10)-90;
if(x>=0 && y<=0) angle = (-x*10)-180;
if(x>=0 && y>=0) angle = (-y*10)-270;
imageViewProtractorPointer.setRotation((float)angle);
}
@Override
public void onAccuracyChanged(Sensor sensor, int i){}
};
}
if you want to understand about my if statements see this image
for my use i lock the screen in a portrait mode , and a use 2 images to show the angle on screen , this is my screenshot
i'm still have to make it little better , just not enough time for it.
i hope this help , if you need a full code let me know.
回答4:
You have to add some permissions to the manifest. The docs states:
the default sensor matching the requested type and wakeUp properties if one exists and the application has the necessary permissions, or null otherwise
I know it sounds counter intuitive, but apparently the permissions you need are:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
(or a sub-set of those).
See here: manifest.xml when using sensors
回答5:
https://developer.android.com/guide/topics/sensors/sensors_position.html
https://developer.android.com/guide/topics/sensors/sensors_motion.html
https://developer.android.com/guide/topics/sensors/sensors_overview.html
Which sensor for rotating android phone?
回答6:
You can try the GEOMAGNETIC_ROTATION_VECTOR like this:
private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)
And compute the sensor info with this:
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
final float[] rotationMatrix = new float[9];
mSensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);
// Express the updated rotation matrix as three orientation angles.
final float[] orientationAngles = new float[3];
mSensorManager.getOrientation(rotationMatrix, orientationAngles);
Extracted from android docs: https://developer.android.com/guide/topics/sensors/sensors_position.html
add the proper permissions in the manifest.
Hope this helps.
回答7:
For anyone who is still confused by this problem, say: you want to get the orientation of your phone (the azimuth, pitch and roll), but sometimes the magnetic field is unstable, so that the orientation you get is also unstable. The answers above may help you get the pitch and roll angle, but you still cannot get the azimuth angle. They said it is impossible. Then you become desperate. So, what should you do to solve this problem?
If you only care about the orientation and you don't care about where north is, here is my suggestion, try this sensor, it works awesome in my case:
TYPE_GAME_ROTATION_VECTOR
来源:https://stackoverflow.com/questions/38711705/android-device-orientation-without-geomagnetic