I am making a compass in android. I store the starting value of the z-axis.
As I rotate the phone in a circle, after every 10 degrees I want to make a decision.
I am calculating things as follows:
startingAngle=355;
currentAngle=`Real time value of z-axis`;
difference=currentAngle-startingAngle;
if(difference==10){
`enter code here`
}
When I start to rotate the phone in a circle, eventually currentAngle reaches 359, after which it goes to 1.
This raises a problem when currentAngle is 1 and startingAngle is 355 because difference becomes 344 but it should be 6.
How can I get the correct value of difference so I can trigger a function after 10 degrees?
If I understand your question right:
Diff = Abs(Angle1 - Angle2)
if Diff >= 180
Diff = 360 - Diff
Related
So this is apparently quite a simple question but I haven't been able to find a 100% working solution despite my research and googling over the last few months. Basically, I just want to obtain the pitch, yaw and roll from an Android device represented from 0 to 360 degrees.
Most functionality I have seen that accomplishes this seems to boil down to code that looks like the following:
private fun updateOrientation(rotationVector: FloatArray) {
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVector)
val (worldAxisForDeviceAxisX, worldAxisForDeviceAxisY) = when (mWindowManager.defaultDisplay.rotation) {
Surface.ROTATION_0 -> Pair(SensorManager.AXIS_X, SensorManager.AXIS_Z)
Surface.ROTATION_90 -> Pair(SensorManager.AXIS_Z, SensorManager.AXIS_MINUS_X)
Surface.ROTATION_180 -> Pair(SensorManager.AXIS_MINUS_X, SensorManager.AXIS_MINUS_Z)
Surface.ROTATION_270 -> Pair(SensorManager.AXIS_MINUS_Z, SensorManager.AXIS_X)
else -> Pair(SensorManager.AXIS_X, SensorManager.AXIS_Z)
}
val adjustedRotationMatrix = FloatArray(9)
SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX,
worldAxisForDeviceAxisY, adjustedRotationMatrix)
// Transform rotation matrix into azimuth/pitch/roll
val orientation = FloatArray(3)
SensorManager.getOrientation(adjustedRotationMatrix, orientation)
// Convert radians to degrees
val pitch = orientation[1] * -57
val roll = orientation[2] * -57
println("pitch is $pitch")
println("roll is $roll")
mListener?.onOrientationChanged(pitch, roll)
}
However, when I run this on my physical device, and I roll the device from side to side (that is, put the device on approximately 45 degree angle either way), when I roll to the left I get readings like 95 degrees, and when I roll to the right I get readings like -95. If I roll the phone and then leave it, the roll angle (while the phone is flat on the desk) goes between 140 and 160 before finally stabilising at ~144-145 degrees.
Pitch behaves in a more reasonable manner (starts at -90, goes to -74 if I tilt the phone up and down).
I would expect that the roll of the device would start at 0 or 180, as the phone is flat on the desk. If I tilted the device 45 degrees to the left, I would get a reading of 135. If I tilted it 45 degrees to the right, I would get readings like 225.
I'm starting to think that I could critically misunderstand the accelerometer/gyroscope functionality in android and physical devices and what I want to accomplish is physically impossible. I am trying to get this data so I can set up a "safe range" of angles for my app (if the phone is at less than a certain angle, or more than a certain angle for a certain amount of time, it sounds an alert).
How can I get the pitch, yaw, and roll of the device from 0 - 360 degrees on an Android device?
TL;DR
How come the accelerometer values I get from Sensor.TYPE_ACCELEROMETER are slightly offset? I don't mean by gravity, but by some small error that varies from axis to axis and phone to phone.
Can I calibrate the accelerometer? Or is there a standard way of compensating for these errors?
I'm developing an app that has a need for as precise acceleration measurements as possible (mainly vertical acceleration, i.e. same direction as gravity).
I've been doing A LOT of testing, and it turns out that the raw values I get from Sensor.TYPE_ACCELEROMETER are off. If I let the phone rest at a perfectly horizontal surface with the screen up, the accelerometer shows a Z-value of 9.0, where it should be about 9.81. Likewise, if I put the phone in portrait or landscape mode, the X- and Y- accelerometer values show about 9.6. instead of 9.81.
This of course affects my vertical acceleration, as I'm using SensorManager.getRotationMatrixFromVector(), to calculate the vertical acceleration, resulting in a vertical acceleration that is off by a different amount depending on the rotation of the device.
Now, before anyone jumps the gun and mentions that I should try using Sensor.TYPE_LINEAR_ACCELERATION instead, I must point out that I'm actually doing that as well, parallel to the TYPE_ACCELERATION. By using the gravity sensor I then calculate the vertical acceleration (as described in this answer). The funny thing is that I get EXACTLY the same result as the method that uses the raw accelerometer, SensorManager.getRotationMatrixFromVector() and matrix multiplication (and finally subtracting gravity).
The only way I'm able to get almost exactly zero vertical acceleration for a stationary phone in any rotation is to get the raw accelerometer values, add an offset (from earlier observations, i.e. X+0.21, Y+0.21 and Z+0.81) and then performing the rotation matrix stuff to get the world coordinate system accelerations. Note that since it's not just the calculated vertical acceleration that is wrong - it's actually the raw values from Sensor.TYPE_ACCELEROMETER, which I would think excludes other error sources like gyroscope sensor, etc?
I have tested this on two different phones (Samsung Galaxy S5 and Sony Xperia Z3 compact), and both have these accelerometer value deviances - but of course not the same values on both phones.
How come the the values of Sensor.TYPE_ACCELEROMETER are off, and is there a better way of "calibrating" the accelerometer than simply observing how much they deviate from gravity and adding the difference to the values before using them?
You should calibrate gains, offsets, and angle of the 3 accelerometers.
Unfortunately it's not possible to deepen the whole topic here.
I'll write a small introduction, describing the basic concept, and then I'll post a link to the code of a simple Clinometer that implements the calibration.
The calibration routine could be done with 7 misurations (calculate the mean value of a good number of samples) in different ortogonal positions at your choice, in order to have all +-0 and +-g values of your accelerometers. For example:
STEP 1 = Lay flat
STEP 2 = Rotate 180°
STEP 3 = Lay on the left side
STEP 4 = Rotate 180°
STEP 5 = Lay vertical
STEP 6 = Rotate 180° upside-down
STEP 7 = Lay face down
Then you can use the 7 measurements mean[][] to calculate offsets and gains:
calibrationOffset[0] = (mean[0][2] + mean[0][3]) / 2;
calibrationOffset[1] = (mean[1][4] + mean[1][5]) / 2;
calibrationOffset[2] = (mean[2][0] + mean[2][6]) / 2;
calibrationGain[0] = (mean[0][2] - mean[0][3]) / (STANDARD_GRAVITY * 2);
calibrationGain[1] = (mean[1][4] - mean[1][5]) / (STANDARD_GRAVITY * 2);
calibrationGain[2] = (mean[2][0] - mean[2][6]) / (STANDARD_GRAVITY * 2);
using the values of mean[axis][step], where STANDARD_GRAVITY = 9.81.
Then apply the Gain and Offset Corrections to measurements:
for (int i = 0; i < 7; i++) {
mean[0][i] = (mean[0][i] - calibrationOffset[0]) / calibrationGain[0];
mean[1][i] = (mean[1][i] - calibrationOffset[1]) / calibrationGain[1];
mean[2][i] = (mean[2][i] - calibrationOffset[2]) / calibrationGain[2];
}
and finally calculates the correction angles:
for (int i = 0; i < 7; i++) {
angle[0][i] = (float) (Math.toDegrees(Math.asin(mean[0][i]
/ Math.sqrt(mean[0][i] * mean[0][i] + mean[1][i] * mean[1][i] + mean[2][i] * mean[2][i]))));
angle[1][i] = (float) (Math.toDegrees(Math.asin(mean[1][i]
/ Math.sqrt(mean[0][i] * mean[0][i] + mean[1][i] * mean[1][i] + mean[2][i] * mean[2][i]))));
angle[2][i] = (float) (Math.toDegrees(Math.asin(mean[2][i]
/ Math.sqrt(mean[0][i] * mean[0][i] + mean[1][i] * mean[1][i] + mean[2][i] * mean[2][i]))));
}
calibrationAngle[2] = (angle[0][0] + angle[0][1])/2; // angle 0 = X axis
calibrationAngle[1] = -(angle[1][0] + angle[1][1])/2; // angle 1 = Y axis
calibrationAngle[0] = -(angle[1][3] - angle[1][2])/2; // angle 2 = Z axis
You can find a simple but complete implementation of a 3-axis calibration in this opensource Clinometer app: https://github.com/BasicAirData/Clinometer.
There is also the APK and the link of the Google Play Store if you want to try it.
You can find the calibration routine in CalibrationActivity.java;
The calibration parameters are applied in ClinometerActivity.java.
Furthermore, you can find a very good technical article that deepens the 3-axis calibration here: https://www.digikey.it/it/articles/using-an-accelerometer-for-inclination-sensing.
I am making a somewhat-racing game. The car automatically moves forward, but to turn it sideways, I measure the rotation of the phone. Since I have to measure the acceleration on the x axis, I use:
Direction.x = Input.acceleration.x * Time.deltaTime;
Transform.translate (Direction.x * 5f);
When I play the game, the car rotates how I want it to when I tilt the phone on the x-axis. However, the problem is when I place the phone on the table, the car travels left super slowly, which doesn't make sense since it is at a 0 degree angle. To make sure this wasn't because of the table surface, I played it in Unity Simultator and same thing happened. The car travels left super slowly. When I debug.log, it says that Direction.x is about -0.000147..., a super small number. Is there any way to fix this problem, so that when the phone is still, the car's Direction.X will be 0, or is something wrong with my code.
Sometimes in Unity Translate function glitches, because better use of standard operations with vectors. Just try to cut the minimum values of the accelerometer:
float min_value = 0.01f
if(Mathf.Abs(Input.acceleration.x) < min_value)
Direction.x = Input.acceleration.x * Time.deltaTime;
else
Direction.x = 0;
transform.position = transform.position + Direction.x * 5f;
I need find angle of vehicle turn measured in degrees.
Location points update with equal intervals (1 sec). Therefore device makes like 4-5 points during turn. I schematically displayed that on picture.
Is it possible to calculate the angle of turn using Location? If it is possible, how?
What I tried:
Create two geometric vectors from points 3, 4 and 1, 2 respectively and find angle between those vectors. Coordinates of vectors I calculated like Vector1 (lat2 - lat1; lon2 - lon2). Not sure this approach could be applied to Location coordinates.
Use location1.bearingTo(location2). But this doesn't give expected results. Seems like it gives "compass" results. Perhabs I could use it somehow but not sure.
Also tried few trigonometric formulas like here or here or here. They didn't give expected angle.
EDIT: Solution
The accepted answer works great. But to complete the answer I have to show that method of angleDifference. This one works for me:
public int getAngleDifference(int currentAngle){
int r = 0;
angleList.add(currentAngle);
if (angleList.size() == 4) {
int d = Math.abs(angleList.get(0) - angleList.get(3)) % 360;
r = d > 180 ? 360 - d : d;
angleList.clear();
}
return r;
}
I add points to list untill there're 4 of them and then calculate angle difference between 1st and 4th points for better results.
Hope it will help for someone!
vect1 = LatLon2 - LatLon1; // vector subtraction
vect2 = LatLon4 - LatLon3;
By definition of the dot product has the property:
vect1.vect2 = ||vect1||*||vect2||*Cos(theta)
Here's a breakdown of the notation
The term vect1.vect2 is the dot product of vect1 and vect2.
The general form of a dot product can be broken down component wise let v1 = <x1,y1> and v2=<x2,y2> for two arbitrary vectors v1 and v2 the dot product would be:
v1.v2 = x1*x2 + y1*y2
and the magnitude of some arbitrary vector v is:
||v|| = sqrt(v.v); which is a scalar.
The above is equivalent to the Euclidean distance formula with components x and y:
||v|| = sqrt(x^2 + y^2)
Getting the angle
Find a value for theta given the two vectors vect1 and vect2:
theta = Math.ArcCos(vect1.vect2/(||vect1||*||vect2||))
Approach 1 does not work as you described: Lat, Lon are not cartesian coordinates (One degree of longitude expressed in meters is not one degree of latitide, this is only valid at the equator). You would have first to transform to a (local) cartesian system.
An error is in the drawing: The angle marked with "?" is placed at the wrong side. You most probably want angle: 180 - ?
In your example the car ist turning less than 90°, altough your angle shows more than 90°.
To understand better make another drawing where the car turns left for only 10 degrees. In your drawing this would be 170°, which is wrong.
Approach 2) works better, but you need to sum up the angle differences.
You have to write yourself a method
double angleDifference(double angle1, double angle2);
This look easier than it is, although the code is only a few lines long.
Make sure that you have some test cases that tests the behaviour when crossing the 360° limit.
Example
(turn from bearing 10 to bearing 350), should either give 20 or -20, depending if you want that the method give sthe absolut evalue or the relative angle
I am able to rotate camera with this code
camera.zoom = 3//in constructor
if(camera.zoom>1)
{
camera.zoom-=0.01f;
camera.rotate(15);
}
this is done in render, Now zooming effect works properly but when zooming completes my screen is stay rotated with current angle. like below.
I want that my screen stops after zooming at 0 degree.
In your code snippet
**camera.zoom=3;**
and in each iteration you are zooming camera by 0.01 till camera.zoom > 1
so you have total 20 iteration for zooming
Then rotate with 18 degree angle after iteration it will rotate in 360 degree.
I wrote this method to calculate current angle of camera:
public float getCameraCurrentXYAngle(Camera cam)
{
return (float)Math.atan2(cam.up.x, cam.up.y)*MathUtils.radiansToDegrees;
}
Then I call rotate method like this:
camera.rotate(rotationAngle - getCameraCurrentXYAngle(camera));
This code works, but it will rotate immediately in one call. to rotate it by a speed, you need to calculate appropriate 'rotationAngle' for every frame.
Have you tried rotating a multiple of 1.8 degrees with each iteration? Then you're image should have completed a number of full rotations once the 200 iterations have passed.
attention, the computer can't correctly represent most real numbers!
in binary 0.01 is a periodic number, so it will be truncated/rounded.
substrating/adding float numbers a few hundred times will add the rounding error and thus give you horribly wrong results.
(e.g. after 200 substractions, your camera.zoom value will be ~ 1.0000019 - NOT 1.0!)
that's why your loop is repeated 201 times, giving you a zoom value of 0.9900019 and a rotation of 361.7996 ~ 361.8 (when using 1.8 as in alex's answer).
you could use libGDX Interpolation functions:
time += Gdx.graphics.getDeltaTime(); //the rounding error is futile here,
//because it'll increase the animation time by max. 1 frame
camera.zoom = Interpolation.linear.apply(3, 1, Math.min(time, 1));
camera.rotate = Interpolation.linear.apply(0, 360, Math.min(time, 1));
this code would create an one second long animation of zooming from 3 to 1 and rotating from 0 to 360 (simply one whole roation)