Strange behavior with android orientation sensor
This is a common problem with yaw, pitch and roll. You cannot get rid of it as long as you are using yaw, pitch and roll (Euler angles). This video explains why.
I use rotation matrices instead of Euler angles in my motion sensing application. For an introduction to rotation matrices I recommend:
Direction Cosine Matrix IMU: Theory
Rotation matrices work like a charm.
Quaternions are also very popular and said to be the most stable.
[This answer was copied from here.]
Android - SensorManager strange behaviour of getOrientation
The orientation estimate obtained by the device depends in part upon the local magnetic field direction. Within buildings, near iron structures and near electrical appliances the magnetic field sensed may vary significantly over distances measured in feet. This effect can be easily seen with a hand held magnetometer or Gauss-meter.
I suspect that these distortions are responsible for your applications strange behavior.
Weird behaviour of the magnetic field measurements while rotating the device
Make sure that you're monitoring the accuracy of the sensors via the onAccuracyChanged(Sensor sensor, int accuracy)
method. I find that with my Nexus 10 tablet, the accuracy regularly changes, and when it's low or unreliable, I get results similar to yours.
Device orientation data transformed to css behaves strange when using a real device
Found out that using Quaternion is the way to go when encountering that problem (Gimbel lock). I've found an excellent npm library to handle the math like so:
var rad = Math.PI / 180;
window.addEventListener("deviceorientation", function(ev) {
// Update the rotation object
var q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, 'ZXY');
// Set the CSS style to the element you want to rotate
elm.style.transform = "matrix3d(" + q.conjugate().toMatrix4() + ")";
}, true);
Android Pitch and Roll Issue
I found what I was looking for, Rotational Matrices.
I was using Euler angles (roll, pitch, yaw) for the pitch and roll. When the phone is on end 90 degrees, the x and z plain are the same and the phone goes crazy, a fundamental flaw with Euler angles.
I need to get the pitch and roll degrees using Rotational Matrices via getRotationMatrix
Here it is for all ;)
XML:
<?xml version="1.0" encoding="utf-8"?>
<!-- This file is res/layout/main.xml -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button android:id="@+id/update" android:text="Update Values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doUpdate" />
<Button android:id="@+id/show" android:text="Show Me!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doShow" android:layout_toRightOf="@id/update" />
<TextView android:id="@+id/preferred" android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/update" />
<TextView android:id="@+id/orientation" android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/preferred" />
</RelativeLayout>
Code:
package YOURPACKAGE;
import android.app.Activity;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class YOURCLASS extends Activity implements SensorEventListener {
private static final String TAG = "VirtualJax";
private SensorManager mgr;
private Sensor accel;
private Sensor compass;
private Sensor orient;
private TextView preferred;
private TextView orientation;
private boolean ready = false;
private float[] accelValues = new float[3];
private float[] compassValues = new float[3];
private float[] inR = new float[9];
private float[] inclineMatrix = new float[9];
private float[] orientationValues = new float[3];
private float[] prefValues = new float[3];
private float mAzimuth;
private double mInclination;
private int counter;
private int mRotation;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
preferred = (TextView)findViewById(R.id.preferred);
orientation = (TextView)findViewById(R.id.orientation);
mgr = (SensorManager) this.getSystemService(SENSOR_SERVICE);
accel = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
compass = mgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
orient = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION);
WindowManager window = (WindowManager) this.getSystemService(WINDOW_SERVICE);
int apiLevel = Integer.parseInt(Build.VERSION.SDK);
if(apiLevel <8) {
mRotation = window.getDefaultDisplay().getOrientation();
}
else {
mRotation = window.getDefaultDisplay().getRotation();
}
}
@Override
protected void onResume() {
mgr.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME);
mgr.registerListener(this, compass, SensorManager.SENSOR_DELAY_GAME);
mgr.registerListener(this, orient, SensorManager.SENSOR_DELAY_GAME);
super.onResume();
}
@Override
protected void onPause() {
mgr.unregisterListener(this, accel);
mgr.unregisterListener(this, compass);
mgr.unregisterListener(this, orient);
super.onPause();
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
public void onSensorChanged(SensorEvent event) {
// Need to get both accelerometer and compass
// before we can determine our orientationValues
switch(event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
for(int i=0; i<3; i++) {
accelValues[i] = event.values[i];
}
if(compassValues[0] != 0)
ready = true;
break;
case Sensor.TYPE_MAGNETIC_FIELD:
for(int i=0; i<3; i++) {
compassValues[i] = event.values[i];
}
if(accelValues[2] != 0)
ready = true;
break;
case Sensor.TYPE_ORIENTATION:
for(int i=0; i<3; i++) {
orientationValues[i] = event.values[i];
}
break;
}
if(!ready)
return;
if(SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)) {
// got a good rotation matrix
SensorManager.getOrientation(inR, prefValues);
mInclination = SensorManager.getInclination(inclineMatrix);
// Display every 10th value
if(counter++ % 10 == 0) {
doUpdate(null);
counter = 1;
}
}
}
public void doUpdate(View view) {
if(!ready)
return;
mAzimuth = (float) Math.toDegrees(prefValues[0]);
if(mAzimuth < 0) {
mAzimuth += 360.0f;
}
String msg = String.format(
"Preferred:\nazimuth (Z): %7.3f \npitch (X): %7.3f\nroll (Y): %7.3f",
mAzimuth, Math.toDegrees(prefValues[1]),
Math.toDegrees(prefValues[2]));
preferred.setText(msg);
msg = String.format(
"Orientation Sensor:\nazimuth (Z): %7.3f\npitch (X): %7.3f\nroll (Y): %7.3f",
orientationValues[0],
orientationValues[1],
orientationValues[2]);
orientation.setText(msg);
preferred.invalidate();
orientation.invalidate();
}
public void doShow(View view) {
// google.streetview:cbll=30.32454,-81.6584&cbp=1,yaw,,pitch,1.0
// yaw = degrees clockwise from North
// For yaw we can use either mAzimuth or orientationValues[0].
//
// pitch = degrees up or down. -90 is looking straight up,
// +90 is looking straight down
// except that pitch doesn't work properly
Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse(
"google.streetview:cbll=30.32454,-81.6584&cbp=1," +
Math.round(orientationValues[0]) + ",,0,1.0"
));
startActivity(intent);
return;
}
Android Tilt issue with Accelerometer and Magnetometer
In case some one is stuck with this issue:
Strange behavior with android orientation sensor
In summary, the trouble is with the use of Eulers angle - so there is little that can be done to sort the above issue at extreme angles of the device ... apart from using Quaternions (which is suitable for OpenGL or 3D projections).
I am using it for 2D drawing on my app (to create a parallax effect), so I just ended up using the Accelerometer values (range is obviously -9.8 to 9.8) ... It works perfectly and an annoyingly easy solution for my crude needs. This technique is not acceptable for 3d projection... if needing precise measurements, see https://bitbucket.org/apacha/sensor-fusion-demo.
Related Topics
Error:Execution Failed for Task ':Android:Transformclassesandresourceswithproguardforrelease'
Where Is the Layout Preview in Android Studio
How to Create a Circular Progressbar in Android Which Rotates on It
Viewpager as a Circular Queue/Wrapping
Spinner: Get State or Get Notified When Opens
Android Singletask or Singleinstance Launch Mode
Could Not Find Com.Android.Tools.Build:Gradle:3.0.0-Alpha1 in Circle Ci
How to Add a Custom Button State
Get the Current Language in Device
Play Audio File from the Assets Directory
Change Project Name on Android Studio
How to Execute Async Task Repeatedly After Fixed Time Intervals
Android:Loading an Image from the Web with Asynctask
How to Import Existing Android Project into Eclipse
How to Bring an Activity to Foreground (Top of Stack)
How to Send Parameters from a Notification-Click to an Activity