Getting device orientation (yaw, roll and pitch) in Flutter - android

In Android I can get the device yaw, roll and pitch using a GAME_ROTATION_VECTOR sensor.
I need to do the same thing in Flutter, but I haven't been able to find anything but the sensors package, which only gives access to accelerometer & gyroscope sensors.
What can I do? Do I need to calculate the orientation myself from the accelerometer and gyro?

Don't know if this is still relevant, as the question wasn't closed, but for those seeking the answer... there is now a Flutter Sensors package which does all the magic for you.
You'll have to subscribe to the streams to get the current values of the accelerometers and gyroscopes, of course.
For example:
#override
void initState() {
super.initState();
_streamSubscriptions
.add(accelerometerEvents.listen((AccelerometerEvent event) {
setState(() {
_accelerometerValues = <double>[event.x, event.y, event.z];
});
}));
_streamSubscriptions.add(gyroscopeEvents.listen((GyroscopeEvent event) {
setState(() {
_gyroscopeValues = <double>[event.x, event.y, event.z];
});
}));
_streamSubscriptions
.add(userAccelerometerEvents.listen((UserAccelerometerEvent event) {
setState(() {
_userAccelerometerValues = <double>[event.x, event.y, event.z];
});
}));
}

I would not use the Flutter sensors_plus package to get yaw, roll or pitch. Even if you only wanted yaw (aka. heading or azimuth), as shown in this Github issue: MagnetometerEvent to compass values. It is challenging to convert magnetometer readings (microteslas) into roll, pitch, yaw because of the maths. You only get raw magnetometer readings.
As I wrote in the comment there:
I would highly recommend using
https://pub.dev/packages/flutter_compass, https://pub.dev/packages/motion_sensors or writing your own plugin to
use the OS APIs to get the data you need directly, instead of using
the magnetometer readings (unit: micro Teslas), to allow you to use the
operating-system calculated heading, which could use multiple
sensors (sensor fusion) to give improved accuracy based on the
hardware, as well as compensate for the location (the magnetometer
only allows you to calculate magnetic north, not true north).
motion_sensors provides an OrientationEvent, which contains roll, pitch and yaw, in radians.
Writing your own plugin
If you need roll or pitch (more than yaw/heading/azimuth), the OS APIs provide it:
On Android, in Java from SO answer:
float orientationData[] = new float[3];
SensorManager.getOrientation(R, orientationData);
azimuth = orientationData[0];
pitch = orientationData[1];
roll = orientationData[2];
On iOS, in Swift, from SO answer
CMQuaternion quat = self.motionManager.deviceMotion.attitude.quaternion;
myRoll = radiansToDegrees(atan2(2*(quat.y*quat.w - quat.x*quat.z), 1 - 2*quat.y*quat.y - 2*quat.z*quat.z)) ;
myPitch = radiansToDegrees(atan2(2*(quat.x*quat.w + quat.y*quat.z), 1 - 2*quat.x*quat.x - 2*quat.z*quat.z));
myYaw = radiansToDegrees(asin(2*quat.x*quat.y + 2*quat.w*quat.z));

Related

sensors data with respect to true north android

I am using the SensorEvent API in order to get data for my app from different sensors (more specifically: TYPE_ROTATION_VECTOR, TYPE_GRAVITY, TYPE_GYROSCOPE, TYPE_LINEAR_ACCELERATION). Now, I know that in iOS there is the so called CMAttitudeReferenceFrameXTrueNorthZVertical, which gives all the sensors values with respect to True North, whereas z axis will always be vertical.
I couldn't find anything similar in Android, so I am thinking to manually translate the coordinate system. I am also thinking of using the remapCoordinateSystem method. However, I still don't know how to get the data with respect to True North. Did anyone have to deal with something similar before?
This answer is inspired by the class used here:
Yout should have the onSensorChanged method in the SensorEventListener
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
//get the rotation matrix from the sensor (this will be using the magnetic north)
SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
//change the coordinate system (your new matrix is in the variable mChangedRotationMatrix)
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X, mChangedRotationMatrix);
//get the orientationMatrix of your new coordinate system
SensorManager.getOrientation(mChangedRotationMatrix, mOrientation);
//get the magnetic heading
float magneticHeading = (float) Math.toDegrees(mOrientation[0]);
//adjust accordingly by calculating the true north with either the "computeTrueNorth" method available the above link or the method used in the link below
}
}
To get the true north in degrees you may use this answer

What sensor can be used to detect rotation when upright?

Hi I am creating an application in which the user holds the phone upright and then rotates it around the y axis (similar to taking a panorama).
(source: apple.com)
I need to detect the angle of rotation. In iOS this was fairly simple with the gyroscope sensor, but I am not finding the same luck with Android. If anyone could point me in the right direction that would be great.
Assuming your Y axis points to the center of earth, the value you are looking for is called azimuth.
To monitor its change you will need to register a listener for TYPE_ACCELEROMETER and TYPE_MAGNETIC_FIELD events:
mngr = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
accelerometer = mngr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magneticField = mngr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
int rate = SensorManager.SENSOR_DELAY_GAME; // or other
mngr.registerListener(sensorListener, accelerometer, rate);
mngr.registerListener(sensorListener, magneticField, rate);
And within the listener, call:
float[] values = new float[3];
SensorManager.getOrientation(R, values);
float current_azimuth_val = values[0]; // <----------
Note that the quality. and latency, if the data you will obtain is highly hardware dependent.
There are various sensors available that can be managed through a SensorManager. Of course, since every device decides whether or not to put a particular sensor on the hardware platform for their model you have to check whether one exists. Some have gyro like iOS, some can be done with accelerometer and magnometer sensors in its place.
You can get started here: http://developer.android.com/guide/topics/sensors/sensors_overview.html

how to detect a metal using magnetic sensor in android phone?

I want to detect a metal using magnetic sensor values. i am getting values continuously like x=30.00 ,y=-20.00 ,z=-13.00
now i want to know how to use these values for detecting any metal(mathameticalcalu,formulas)
code is
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
// get compass sensor (ie magnetic field)
myCompassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
float azimuth = Math.round(event.values[0]);
float pitch = Math.round(event.values[1]);
float roll = Math.round(event.values[2]);
To detect metal, you have to check the intensity of the magnetic field, i.e. the magnitude of the magnetic field vector.
float mag = Math.sqrt(x^2 + y^2 + z^2);
Then you need to compare this value to the expected value of the magnetic field at your location on Earth. Luckily, Android provides functions to do so. Look at the GeomagneticField, reference is here https://developer.android.com/reference/android/hardware/GeomagneticField.html
Then if the value you are reading out of the sensors is quite far from the expected value, there's "something" (you guessed it, metal) that is disturbing the Earth magnetic field in the vicinity of your sensor. A test you could implement for instance is the following:
if (mag > 1.4*expectedMag || mag < 0.6*expectedMag) {
//there is a high probability that some metal is close to the sensor
} else {
//everything is normal
}
You should experiment a bit with the 1.4 and 0.6 values so that it fits your application. Note that this is never going to work 100% of the time because the magnetic sensors on a smartphone are quite cheap and nasty.
You can detect magnetic field using android Magnetic field sensor not metals.But Metals which are having magnetic field also will be detected e.g iron,nickel etc.Because ferrous metals behave the same way as a live electric cable .

How to make an accurate compass on android

my android application shows the direction of a particular place in the world and therefore in needs to get the compass degree.
This is the code I've been using to calculate the degrees:
public void getDirection() {
mySensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
List<Sensor> mySensors = mySensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if(mySensors.size() > 0){
mySensorManager.registerListener(mySensorEventListener, mySensors.get(0), SensorManager.SENSOR_DELAY_UI);
}
else{
TextView alert = (TextView)findViewById(R.id.instruct);
alert.setText(getString(R.string.direction_not_found));
myCompassView.setVisibility(myCompassView.INVISIBLE);
}
}
private SensorEventListener mySensorEventListener = new SensorEventListener(){
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
compassBearing = (float)event.values[0];
float bearing;
bearing = compassBearing - templeBearing;
if (bearing < 0)
bearing = 360 + bearing;
myCompassView.updateDirection(bearing);
}
};
This method usually works but sometimes it just gets the wrong north, what do I have to do to get a more accurate location?
I have a couple suggestions for you:
1) Your device may not be calibrated. In order to do it, move it around in a form of 8 (see this). If you don't if your device is calibrated or not make some tests by pointing the device at some known cardinal point direction and compare the values. Typically, if a device is not calibrated, you will see great variations in the azimuth value for small rotations. That is what I would be worried about.
Now, don't forget that the sensor gives you the bearing to Magnetic North, and not True North! This difference is known as declination of the magnetic field and its value changes from place to place and from time to time due to changes in Earth's magnetic field. This app can compute some of the values for you with relative accuracy. I wouldn't be too much worried about this as the declination is typically small, but you might be looking for good precision (where I live the declination is 3º, currently).
2) Stay away from metal objects or stuff that generate a strong magnetic field. For example, don't do tests if you have your phone near the computer or any physical keyboards! This is pure poison for testing compass-geolocation. Some apps can measure the intensity of the magnetic field (if the device supports it). When you get closer to metal stuff you will experience higher values and strong changes in directions. For fun, there are also some "metal detectors": this app recognises changes in the magnetic field and vibrates when you are close "metal object" or stuff that magnetically interfere with the device.
3) Remember to update the bearing when you tilt your device to landscape mode. (article is a must read!) This is because azimuth value is based on the rotation of the perpendicular axis to the plane of the phone. When you rotate the device to landscape, this value is changed by +/-90º! This is not resolved by disabling the application landscape mode! You will have to determine it programmatically by analysing rotations around the other two axis (pitch and roll). This is not trivial, but there are some examples somewhere in the net.
edit: If you are interested in some code, check out Mixare, it is an open source augmented reality framework under the GPL3 for Android. Take a look at their code regarding orientation, compass geolocation and bearing.
PS: I don't have any sort of connection with the creators of the mentioned applications.

Strange behavior with android orientation sensor

Currently, I'm trying to rotate 3D Cube using orientation sensor values, using getRotation() method. Some unexpected behaviors are observed when the android device is rotated above some bounds. For instance, if I make the device 'stand up', the value of the 'roll' just becomes crazy.
Also I'm experiencing the phenomenon similar to so-called gimbal-lock. The only difference is I'm experiencing the very problem even before applying the sensor values to the 3D rotation. When I try to change the 'pitch' value by rotating the device around only 'pitch' axis, the 'yaw' value also changes according to the rotation of the pitch. It seems completely unreasonable to me.
Could somebody help me?? I'm stuck in this problem for a month.
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.]
Using quaternions to compute YPR won't do much to solve any problem. The problem of gimbal lock (which near pitch of +/-90 can drive yaw and roll -- actually yaw-roll at the north pole -- to go crazy under slight changes/noise in the underlying quaternion).
However, if you use Yaw Pitch and Roll values to perform a rotation of a 3D object shouldn't exhibit any odd behavior near the gimbal lock position. It's just that an amibguity in yaw and roll arise and large variations in yaw and roll do not imply the actual orientation is going crazy -- just that the orientation is insensitive to large changes in yaw-roll near pitch of 90.
BUT, also note that phones and browsers for HTML5 do not properly implement yaw, pitch and roll per conventions for Android. Here is a good blog for reference:
http://www.sensorplatforms.com/understanding-orientation-conventions-mobile-platforms/
Here is a basic example, this will return the vector of gravity. Note that you can change the sensor type and the speed of sampling, more details here
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
double total = Math.sqrt(x * x + y * y + z * z);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}, sensor, SensorManager.SENSOR_DELAY_FASTEST);
Well if you running on the phone.
Quaternions are the best, and you should use it
For rotation matrix and euler angle, you can easily came across such term called gimbal lock. It happens frequently with user violent action.
Gimbal lock is the loss of one degree of freedom in a three-dimensional, three-gimbal mechanism that occurs when the axes of two of the three gimbals are driven into a parallel configuration, "locking" the system into rotation in a degenerate two-dimensional space.
Rotation matrix and euler angle are good for slow moving robot action.
For details on quaternions concatnations and convert point to new system,
you can refer to wiki link
https://en.wikipedia.org/wiki/Quaternion

Categories

Resources