I am making a 2d game. The phone is held horizontally and a character moves up/down & left/right to avoid obstacles. The character is controlled by the accelerometer on the phone. Everything works fine if the player doesn't mind (0,0) (the point where the character stands still) being when the phone is held perfectly flat. In this scenario it's possible to just read the Y and X values directly and use them to control the character. The accelerometer values are between -10 and 10 (they get multiplied by an acceleration constant to decide the movement speed of the character), libgdx is the framework used.
The problem is that having (0,0) isn't very comfortable, so the idea is to calibrate it so that 0,0 will be set to the phones position at a specific point in time.
Which brings me to my question, how would I do this? I tried just reading the current X and Y values then subtracting it. The problem with that is that when the phone is held at a 90 degree angle then the X offset value is 10 (which is the max value) so it ends up becoming impossible to move because the value will never go over 10 (10-10 = 0). The Z axis has to come into play here somehow, I'm just not sure how.
Thanks for the help, I tried explaining as best as I can, I did try searching for the solution, but I don't even know what the proper term is for what I'm looking for.
An old question, but I am providing the answer here as I couldn't find a good answer for Android or LibGDX anywhere. The code below is based on a solution someone posted for iOS (sorry, I have lost the reference).
You can do this in three parts:
Capture a vector representing the neutral direction:
Vector3 tiltCalibration = new Vector3(
Gdx.input.getAccelerometerX(),
Gdx.input.getAccelerometerY(),
Gdx.input.getAccelerometerZ() );
Transform this vector into a rotation matrix:
public void initTiltControls( Vector3 tiltCalibration ) {
Vector3.tmp.set( 0, 0, 1 );
Vector3.tmp2.set( tiltCalibration ).nor();
Quaternion rotateQuaternion = new Quaternion().setFromCross( Vector3.tmp, Vector3.tmp2 );
Matrix4 m = new Matrix4( Vector3.Zero, rotateQuaternion, new Vector3( 1f, 1f, 1f ) );
this.calibrationMatrix = m.inv();
}
Whenever you need inputs from the accelerometer, first run them through the rotation matrix:
public void handleAccelerometerInputs( float x, float y, float z ) {
Vector3.tmp.set( x, y, z );
Vector3.tmp.mul( this.calibrationMatrix );
x = Vector3.tmp.x;
y = Vector3.tmp.y;
z = Vector3.tmp.z;
[use x, y and z here]
...
}
For a simple solution you can look at the methods:
Gdx.input.getAzimuth(), Gdx.input.getPitch(), Gdx.input.getRoll()
The downside is that those somehow use the internal compass to give your devices rotation compared to North/South/East/West. I did only test that very shortly so I'm not 100% sure about it though. Might be worth a look.
The more complex method involves some trigonometry, basically you have to calculate the angle the phone is held at from Gdx.input.getAccelerometerX/Y/Z(). Must be something like (for rotation along the longer side of the phone):
Math.atan(Gdx.input.getAccelerometerX() / Gdx.input.getAccelerometerZ());
For both approaches you then store the initial angle and subtract it later on again. You have to watch out for the ranges though, I think Math.atan(...) is within -Pi and Pi.
Hopefully that'll get you started somehow. You might search for "Accelerometer to pitch/roll/rotation" and similar, too.
Related
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
A week ago i didn't know anything about Android Motion Sensors. After know the amazing thing called Virtual Reality I started to search about which sensors are used to get those results. Than I had a idea for a APP but I still don't know which sensors I should use for the situation below:
I have to get the phone orientation in reference to it self. I mean, I should be able to isolate each axis in degress. Something like it:
In this case, using gyroscope, I think that this variation is on the Z Axis.
Using ONLY gyroscope I had a good result for this situation, but after some repetions, I got a famous problem for the Gyro Sensor: Drift.
After this tutorial:
http://www.thousand-thoughts.com/articles/#articles
things became more clear in my head, but I still am having problems like latency between the real movement, and the output and wrong outputs when I change the device orientation (I think that the gravity is the guilty for that).
Is there some code example about how to get 0 - 360 degrees for each axis using ONLY the gyroscope and accelerometer sensors?
(I may had commited some english mistakes. Sorry for that)
The following code will give you correct lean angle, but only if your phone Z axis is 0. (like the way you illustrated it).
When starting to change the Z axis as well, it become problematic, i'm still working on that. (* Degrees has minus "-" sign when lean left and "+" sign to the right)
float[] mGravity;
float[] mGeomagnetic;
float[] temp = new float[9];
float[] RR = new float[9];
//Load rotation matrix into R
SensorManager.getRotationMatrix(temp, null,
mGravity, mGeomagnetic);
//Remap to camera's point-of-view
SensorManager.remapCoordinateSystem(temp,
SensorManager.AXIS_X,
SensorManager.AXIS_Z, RR);
//Return the orientation values
float[] values = new float[3];
SensorManager.getOrientation(RR, values);
Double degrees = (values[2] * 180) / Math.PI;
Using Android opengl I need to move an object from point A to point B and rotate it around its local Z axis in the same time. I have been reading tutorials for the past 3 days, everybody gives you bits of informations and hints, but nobody is capable of explaining this from top to bottom for beginners.
I know how to only translate the object from point A to point B.
I also know how to rotate the object in point A around its local axis (translate it to origin, rotate it, translate it back)
I DON'T know how to rotate and translate in the same time.
I've tried to translate to origin, rotate, translate back, then translate to point B. It doesn't work, and I think I know why (the rotation is messing the object axis, so the translation to point B is incorrect)
A(-x1, y1 , -z1)
B(-x1 + deltaX, y1 + deltaY, -z1 + deltaZ)
_gl.glTranslatef(x1, -y1 , z1);
_gl.glRotatef(degrees, x1, -y1 , z1);
_gl.glTranslatef(-x1, y1 , -z1);
_gl.glTranslatef(deltaX, deltaY, deltaZ);
I need to take into consideration the way the rotation is chaning the axes. Some say I can do that with quaterninons, or with rotation matrixes, etc.
But I don't have enough opengl knowledge to use apis to resolve this.
Can someone explain this to me? With somecode also?
Thank you in advance.
If you have the following code:
glTranslate(x, y, z);
glRotatef(angle, 0, 0, 1);
drawObject();
The object will first be rotated around it's local z-axis and then translated with (x, y, z). The transform call that is closest to the draw call is the one that happens first.
From your code it seems like you actually don't want to rotate the object around it's own origin but some other point, in this case you should do the following:
glTranslate(x, y, z); //Transform 4
glTranslate(origin.x, origin.y, origin.z); //Transfrom 3
glRotatef(angle, 0, 0, 1); // Transform 2
glTranslate(-origin.x, -origin.y, -origin.z); // Transform 1
drawObject();
It's past several days since I started using this function and have not yet succeeded in obtaining valid results.
What i want is basically convert acceleration vector from device's coordinates system, to real world coordinates. I' know that is possible because i have acceleration in relative coordinates and i know the orientation of the device in real world system.
Reading Android developers seems that using getRotationMatrix() i get R = rotation matrix.
So if i want A (acceleration vector in world system) from A' (acceleration vector in phone system) i must do simply:
A=R*A'
But i cant'n understand why the vector A has ALWAYS the first and the second component zero (example: +0,00;-0,00;+6,43)
My current code is similar to this:
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch(event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
accelerometervalues = event.values.clone();
break;
case Sensor.TYPE_MAGNETIC_FIELD:
geomagneticmatrix =event.values.clone();
break;
}
if (geomagneticmatrix != null && accelerometervalues != null) {
float[] Rs = new float[16];
float[] I = new float[16];
SensorManager.getRotationMatrix(Rs, I, accelerometervalues, geomagneticmatrix);
float resultVec[] = new float[4];
float relativacc[]=new float [4];
relativacc[0]=accelerationvalues[0];
relativacc[1]=accelerationvalues[1];
relativacc[2]=accelerationvalues[2];
relativacc[3]=0;
Matrix.multiplyMV(resultVec, 0, Rs, 0, relativacc, 0);
//resultVec[] is the vector acceleration relative to world coordinates system..but doesn't WORK!!!!!
}
}
}
This question is very similar to this one Transforming accelerometer's data from device's coordinates to real world coordinates but there i can't find the solution...i had tried all the ways..
Please help me, i need help!!!
UPDATE:
Now my code is below, i had tried to explain matrix product, but nothing change:
float[] Rs = new float[9];
float[] I = new float[9];
SensorManager.getRotationMatrix(Rs, I, accelerationvalues, geomagneticmatrix);
float resultVec[] = new float[4];
resultVec[0]=Rs[0]*accelerationvalues[0]+Rs[1]*accelerationvalues[1]+Rs[2]*accelerationvalues[2];
resultVec[1]=Rs[3]*accelerationvalues[0]+Rs[4]*accelerationvalues[1]+Rs[5]*accelerationvalues[2];
resultVec[2]=Rs[6]*accelerationvalues[0]+Rs[7]*accelerationvalues[1]+Rs[8]*accelerationvalues[2];
Here some example of data read and result:
Rs separated by " " Rs[0] Rs[1]....Rs[8]
Av separated by " " accelerationvalues[0] ...accelerationvalues[2]
rV separated by " " resultVec[0] ...resultVec[2]
As you can notice the component on x and y axes in real world are zero (around) even if you move speddy the phone. Instead the relative acceleration vector detect correctly each movement!!!
SOLUTION
The errors in the numberrs are relative to float vars multiplication that is not the same as a double multyplication.
This sums to the fact that rotation matrix isn't costant if the phone, even if with the same orientation, is accelerating.
So is impossible translate acceleration vector to absolute coordinates during motion...
It's hard but it's the reality.
Finnaly i found the answer:
The errors in the numbers are relative to float vars multiplication that is not the same as a double multyplication. Here there is the solution.
This sums to the fact that rotation matrix isn't costant if the phone, even if with the same orientation, is accelerating. So is impossible translate acceleration vector to absolute coordinates during motion... It's hard but it's the reality.
FYI the orientation vector is made from magnetomer data AND gravity vector. This cause a ciclic problem: convert relative acc needs oirentation needs magnetic field AND gravity, but we know gravity only if the phone is stop by relative acc..so we are return to begin.
This is confirmed in Android Developers where is explained that rotation matrix give true result only when the phone isn't accelerate (e.g. they talk of free fall, infact there shouldn't be gravity mesaurement) or when it isn't in a non regulare magnetic field.
The matrices returned by this function are meaningful only when the
device is not free-falling and it is not close to the magnetic north.
If the device is accelerating, or placed into a strong magnetic field,
the returned matrices may be inaccurate.
In others world, fully un-useful...
You can trust this thing doing simple experiment on the table with Android Senor or something like this..
You must track down this arithmetic error before you worry about rotation, acceleration or anything else.
You have confirmed that
resultVec[0]=Rs[0]*accelerationvalues[0];
gives you
Rs[0]: 0.24105562
accelerationValues[0]: 6.891896
resultVec[0]: 1.1920929E-7
So once again, simpify. Try this:
Rs[0] = 0.2;
resultVec[0] = Rs[0] * 6.8
EDIT:
The last one gave resultVec[0]=1.36, so let's try this:
Rs[0] = 0.2;
accelerationValues[0] = 6.8
resultVec[0] = Rs[0] * accelerationValues[0];
If you do the sums, using the printed values you have appended, I get
`(0.00112, -0.0004, 10)`
which is not as small as what you have. Therefore there is an arithmetic error!
Could the problem be that you are using accelerationvalues[] in the last block, and accelerometervalues[] later?
I have developed several applications that make use of android sensors, so I am answering to one of your questions according to my experience:
But i cant'n understand why the vector A has ALWAYS the first and the
second component zero (example: +0,00;-0,00;+6,43)
I have observed this problem with the acceleration sensor and the magnetic field sensor, too. The readings are zero for some of the axis (two as you point, or just one in other occasions). This problem happens when you have just enabled the sensors (registerListener()) and I assume that it is related to some kind of sensor initialization.
In the case of the acceleration sensor, I have observed that just a small shaking of the device makes it to start giving correct sensor readings.
The correct solution would be the method onAccuracyChanged() giving the correct information about the sensor state. It should be returning a staus of SensorManager.SENSOR_STATUS_UNRELIABLE, but instead of that, it permanently returns SensorManager.SENSOR_STATUS_ACCURACY_HIGH on all physical devices that I have tested so far. With the method onAccuracyChanged() properly implemented, you could ignore bad readings or ask the user to wait while the sensor is being initialized.
I have written a first person camera class for android.
The class is really simple , the camera object has its three axes
X,y and Z
and there are functions to create the ModelView matrix ( i.e. calculateModelViewMatrix() ),
rotate the camera along its X and Y axis
and Translate the camera along its Z-axis.
I think that my ModelViewMatrix calulation is correct and i can also translate the camera along the Z-axis.
Rotation along x-axis seems to work but along Y-axis it gives strange results.
Also another problem with the rotation seems to be that instead of the camera being rotated, my 3d model starts to rotate instead along its axis.
I have written another implementation based on the look at point and using the openGL ES's GLU.gluLookAt( ) function to obtain the ModelView matrix but that too seems to suffer from the exactly the same problems.
EDIT
First of all thanks for your reply.
I have actually made a second implementation of the Camera class, this time using the rotation functions provided in android.opengl.Matrix class as you said.
I have provided the code below, which is much simpler.
To my surprise, the results are "Exactly" the same.
This means that my rotation functions and Android's rotation functions are producing the same results.
I did a simple test and looked at my data.
I just rotated the LookAt point 1-dgree at a time around Y-axis and looked at the coordinates. It seems that my LookAt point is lagging behind the exact rotation angle e.g. at 20-deg it has only roatated 10 to 12 degree.
And after 45-degrees it starts reversing back
There is a class android.opengl.Matrix which is a collection of static methods which do everything you need on a float[16] you pass in. I highly recommend you use those functions instead of rolling your own. You'd probably want either setLookAtM with the lookat point calculated from your camera angles (using sin, cos as you are doing in your code - I assume you know how to do this.)
-- edit in response to new answer --
(you should probably have edited your original question, by the way - your answer as another question confused me for a bit)
Ok, so here's one way of doing it. This is uncompiled and untested. I decided to build the matrix manually instead; perhaps that'll give a bit more information about what's going on...
class TomCamera {
// These are our inputs - eye position, and the orientation of the camera.
public float mEyeX, mEyeY, mEyeZ; // position
public float mYaw, mPitch, mRoll; // euler angles.
// this is the outputted matrix to pass to OpenGL.
public float mCameraMatrix[] = new float [16];
// convert inputs to outputs.
public void createMatrix() {
// create a camera matrix (YXZ order is pretty standard)
// you may want to negate some of these constant 1s to match expectations.
Matrix.setRotateM(mCameraMatrix, 0, mYaw, 0, 1, 0);
Matrix.rotateM(mCameraMatrix, 0, mPitch, 1, 0, 0);
Matrix.rotateM(mCameraMatrix, 0, mRoll, 0, 0, 1);
Matrix.translateM(mCameraMatrix, 0, -mEyeX, -mEyeY, -mEyeZ);
}
}