So, right now I'm grabbing the accelerometer data and converting them to a decently rough estimate of the angle at which the phone is being held. For right now I'm just focused on the yaw axis.
My area of interest is between 0 and 45 degrees on the yaw axis, so I made a limited queue of the past 5 to 10 readings and compared the numbers to determine if it's going up or down, which kind of works, but it is slow and not really as precise or reliable as I'd want it to be.
Is there a way you can kind of just determine which direction your phone is rotating with just the accelerometer and the magnetic field sensor I guess, without keeping a history of past readings, or something like that? I'm really new to sensor manipulation and Android in general. Any help understanding would be great.
It's not clear exactly what you're looking for here, position or velocity. Generally speaking, you don't want to get a position measurement by using integration on the accelerometer data. There's a lot of error associated with that calculation.
If you literally want the "direction your phone is rotating," rather than angular position, you can actually get that directly from the gyroscope sensor, which provides rotational velocities. That would let you get the direction it's rotating from the velocity without storing data. You should be aware that not every phone has a gyroscope sensor, but it does seem like the newer ones do.
If you want the absolute orientation of the phone (position), you can use the Rotation Vector sensor. This is a combined sensor that automatically integrates data from several of the sensors in one go, and provides additional accuracy. From this, you can get roll-pitch-yaw with a single measurement. Basically, you first want to get your data from the Rotation_vector sensor. Then you use the sensor data with getRotationMatrixFromVector. You can use the output from that in getOrientation (see the same page as the previous link), which will spit out roll-pitch-yaw measurements for you. You might need to rotate the axes around a bit to get the angles measured positive in the direction you want.
Related
All of the guides to getting compass orientation in Android I've found have a bug: when you hold the phone in portrait mode and "look" above the horizon, the compass arrow turns 180 degrees from the correct direction.
Google Maps orientation indicator doesn't have this problem.
Another nice thing that Google Maps have is that they somehow estimate compass accuracy. Any idea how they do this?
Error you ask is caused because of Euler angles or called Gimbal Lock.
To solve very high angle difference, you should check if your device is flat or not checking inclination after getting inclination using gravity sensor or getting gravity from accelerometer with lowpass filter. If it's not flat(AR application or inclination>25 || inclination < 155) remap coordinate system from x,y to x,z. That will solve the issue.
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values);
SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Z, remappedRM);
SensorManager.getOrientation(remappedRM, orientation);
This solves the Gimbal Lock, however, 3D compasses(normal compasses does not give correct results due to they are only mapped in x,y and occurs Gimbal Lock) i've seen on Play Store and every code here has a diffence when you keep your device laying flat or screen pointing at you. That difference sometimes gets up to 10 degrees. I haven't able to solve it. Mostly difference is between 1-5 degrees but i sometimes see it rise up to 10 degrees which is not acceptable.
Google measures azimuth from your location. There is a code to find bearing.
currentLocation.getBearing();
Accuracy of lat long(accuracy of your current location) is what determines accuracy of bearing.
List of ways doing a compass with the most accurate to least is in order
Use GPS/Wife(Google's fused location) location and get bearing from location
Use Rotation Vector(requires magnetic field sensor, you should check if it's available), it's a fused sensor(magnetic field+gyroscope+accelerometer and software parts) and using Kalman filter to smooth values but check inclination to remap orientation
Gravity/Accelerometer + Magnetic Field Sensor. This will have a terrible noise attached to it, to smooth it you should use moving average or low-pass filter(this not for isolating gravity, it's for using a threshold frequency to prevent high jumps)
bug when looking above the horizon.
There is some tricky parts when you work with compass.
The compass orientation depends on magnetic fields and phone orientation, so, in order to correct deviation you need to do some matrix operations.
Check this article, it provides a simple example -> https://www.journal.deviantdev.com/android-compass-azimuth-calculating/
Paraphrasing the article: "So if the device is not holding flat (∓45° deviation) you have to use remapCoordinateSystem() in a useful way to get correct results."
accuracy.
First of all, accuracy rely on physical components present in the device. Fragmentation is a common problem here. Samsung magnetometers are different from LG's, and Android System is not going to abstract you from this.
On the other hand, devices could rely on different type of sensors: harward or sensor fusion. So you're going to experiment differences between one device and other.
So this is a mess. But it exists some techniques you could apply in order to get accuracy and uniformed data from sensors (not only for compass, probably GPS too).
Some people discard the first few seconds of sensor data. Some time is needed in order to calibrate the signal, so the first data retrieved use to have some deviation. The time you need to discard depends on manufacturer again, buy I'd try with 5 seconds or so.
Use Interpolators and Extrapolators. Many sensors in android provide you a way to retrieve data each certain milliseconds. But this is the abstraction provided by Android. Harward sensors have its own timing and they update the signal when they they think it is necessary. The rest of the time, when Android ask the sensor for signal data the sensor returns the last value or maybe some kind of operation of the last signal data provided by manufacturer (again).
So, it is interesting to have some abstraction layer (interpolator / extrapolator) which receive the data from Android system each 20, 50, 1000... This layer make some operations in order to have some uniformity, and then communicates the data to your app.
The operation here could be some kind of average between current and last value, accumulated average or maybe another kind of normalization.
OK, i figured it out myself. first you need to calculate the bearing from the compass. then the Maps api-2 camera can be rotated.
public void updateCamera(float bearing) {
CameraPosition currentPlace = new CameraPosition.Builder()
.target(new LatLng(centerLatitude, centerLongitude))
.bearing(bearing).tilt(65.5f).zoom(18f).build();
googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(currentPlace));
}
set SensorListener in your code and call this method in onSensorChanged event. i have added a tilt value so the map will rotate in 3D.
[Reference:] [Android Maps v2 rotate mapView with compass1
if you can know about more details http://www.techotopia.com/index.php/Working_with_the_Google_Maps_Android_API_in_Android_Studio
Compasses will give you what you want, but it's so noisy. It is noisy for 2 reasons, one of reason is, it is picking up, real noise, real signal. So, we live in an environment that's magnetically very noisy. So, this compasses, picking up everything that's magnetic. The other reason is that, it's not integrated, so it doesn't have benefit of dropping the frequency component. So, try to combine your compass with gyroscope data. This video will help you so much for using these sensors.
Some more details, you can combine accelerometers also. So in summary, Gyroscopes provide orientation, Accelerometers provide a correction due to gravity, and compasses provide a correction due to magnetic North.
I'm developing a tool which receives motion sensor data and sends it to a machine learning algorithm, which ultimately will deduce different types of movement.
I read the Motion sensor guide and it seems like there is some redundancy in the data you can get from the sensors. For example: the accelrometer data contains gravity data and the linear acceleration data shows acceleration without acceleration due to gravity.
So my question is: do i really need all the sensors to get all forms of motion or can I give up some of them?
EDIT: (clarifying the question)
I want to collect the minimal data that will allow me to deduce the same things. What I'm looking for is user behavior: the angle which the user holds his phone, the way the user moves while using his phone, etc..
The answer I'm looking for should include the sets of sensors that have high correlation within them, such that only some of the sensors in this set are required to deduce the same type of motion\movement\rotation\acceleration\etc..
The term "Motion" in the question have no precise meaning. So I answer more generally.
"The way one holds his phone" is nothing but the orientation of the phone.There are three sensors which individually tells the orientation of the phone.
Accelerometer sensor
Orientation sensor
Rotation Vector sensor
Among them only the accelerometer is physical sensor and other two are virtual sensors (they don't have special piece of hardware, they use accelerometer data and report the orientation in different formats).
The orientation sensor is deprecated so you can't use it.
Rotation vector sensor tells the orientation encoded in a quaternion. If your code is based on quaternions then normalize the sensor output using SensorManager.getQuaternionFromVector() and continue. If your code is based on rotation matrix then obtain rotation matrix by calling SensorManager.getRotationMatrixFromVector() passing sensor output and continue. If you want the orientation alone get it by calling SensorManager.getOrientation() passing rotation matrix obtained previously.
Using accelerometer sensor we can find the orientation, but the recommended approach is to combine it with magnetic field sensor output. Call SensorManager.getRotationMatrix() by passing the output of accelerometer output and magnetic field sensor output and get the rotation matrix. If your code is based on rotation matrix, just continue. If you want the orientation alone get it by calling SensorManager.getOrientation() passing rotation matrix obtained in previously. If your code is based on quaternion call SensorManager.getQuaternionFromVector() by passing rotation vector (orientation) obtained previously.
"The way one moves his phone" - Here I consider four motions.
Change of position (Simple translation) and rate change of position (velocity) - No sensor to detect them.
Rate of change of velocity (Simple acceleration) - Accelerometer detects it. But it also contains the gravity component. Normally we need acceleration without gravity component. This could be calculated simply as explained here. However there is another virtual sensor called Linear Acceleration which does the job for us.
Change of orientation (Rotation) - Whenever the orientation changes the accelerometer, orientation and rotation vector sensors report us (gyroscope also reports, but is explained in next point). How to use this sensor to get the current orientation is explained in first part of the answer.
Rate of change of orientation (Angular velocity) - Whenever the orientation changes the gyroscope sensor reports. The output is three numbers representing angular acceleration along x, y and z axes. The unit is radians per second.
Output of the gyroscope sensors is not accurate in long term and the output of accelerometer is not accurate in short term, so combine them to get steady output. For details see this question.
Now it is clear that the gyroscope and accelerometer is required in minimum. However using wide range of sensors minimizes our work.
You can't decide what you get - each sensor's data is already defined, and you get all or nothing. If you see closely, there isn't a place in public API which would let you ask for specific things.
To back this up here's quote from Google's document explaining sensor types:
An accelerometer sensor reports the acceleration of the device along the 3 sensor axes. The measured acceleration includes both the physical acceleration (change of velocity) and the gravity. The measurement is reported in the x, y and z fields of sensors_event_t.acceleration.
If you see into android source, the structs here are strictly defined, and struct for acceleration contains specific fields. So even if you would get 0 in fields you don't like, you won't gain anything.
But what you're referring to are two things - base sensors, which are roughly equivalent to physical sensors on the device, and composite sensors, which combine readings from various physical sensors to get more useful data.
So while you can't decide what you get for a particular sensor (like "only gravity" or "only acceleration in Y axis"), composite sensors do give you data that you can compute by yourself using only base sensors. So linear acceleration is composition of data from accelerometer and gyroscope (or magnetic sensor), after some calculations. Similarly step detector "sensor" uses only accelerometer, but interpretes the data automatically to just give you an event that "yes, someone has made a step" with single value 1.
If you're feeding raw motion data to some algorithms, I would guess base sensors are what you're looking for. That said, I believe you can still safely register for all sensors (both base and composite ones) that combined give you all data that you need (and maybe more), without meaningful battery impact.
For more detailed information on each of the sensors refer to Sensor types on Android website, and if you're curious, you can read up short summary on sensors stack as well.
No, you don't need every sensor. Some of the sensors exist as a convenience to the user. Your example of the linear acceleration sensor is one- it tells you the results of the accelerometer with gravity taken out. You could do this yourself from the raw accelerometer data, but that takes a bit of math (you need to subtract the vector gravity over all 3 axes) and a bit of knowhow (did you remember to calibrate the sensor? It may not read 9.8 at rest. For that matter, 9.8 may not be your gravity if you're not at sea level). That's a lot of work that would need to be repeated by each app, so they created a software "sensor" that sits on top of the accelerometer and provides the computed data. It would be unusual for an app to use raw and linear accelerometers in the same app, generally its one or the other. The step counter is another example of this, it guesses at what a step is based on the accelerometer data. You also wouldn't want calibrated and uncalibrated gyroscope data.
As for what you do need- no clue, you don't say enough about what you're trying to do. One warning though- you said you're trying to detect motion. YOu can't do that. You can detect accelerations and rotation. You cannot detect motion at a constant speed. If you're developing any type of app using these it pays to use the correct terminology and think in terms of physics and how the physical accelerometer and gyroscope work, otherwise you're going to cause yourself bugs.
I am trying to use the phone as a gun aiming with the gyroscope. I calibrate with the phone or tablet in a certain orientation. This will shoot straight. Then depending on the direction the phone is turned (left/right/up/down.), the gun shoots in that direction.
I am using the gyroscope. And all this works. Except after shooting for about 30 secs, the gyroscope slowly starts drifting towards left or right. So when I go back to the orientation I calibrated with, it doesn't shoot straight anymore. Does anyone have any experience writing a Complementary or Kalman Filter to fuse gyro and accelerometer data to give better results in Unity 3D?
I've found this online - http://www.x-io.co.uk/open-source-ahrs-with-x-imu/. It seems to do exactly what I want. But I am using it wrong. I sometime get better and sometimes get worse results with. Anybody have any experience with it ?
First place, Gyro/accelerometer fusion will stabilize your pitch/roll angles, since gravity indicates in which direction ground is. However, you cannot correct "left/right" drift because actual heading is unknown. Getting proper heading stabilization cannot be achieved with gyro/accelerometer alone: it requires additional information.
The example you provide (Madgwick’s MARG/IMU filter) is a filter that can integrate magnetometers ("north" reference), but it has two requirements for getting good results:
The magnetometer has been properly calibrated.
There are no magnetic field disturbances. This is generally not true if you are indoors, or if you are moving close to power lines or metallic structures.
An alternative is using a video signal to get optical flow information, or detecting if the phone is resting in a fixed position to compensate gyro biases from time to time.
I am trying to find the angle of rotation of a car while it makes a turn using Gyroscope from an Android device. So imagine a car is travelling in a bearing of angle 168 and makes a right turn on a road. Now I need to calculate the new heading or bearing angle just using Gyroscope. But the values I receive are in radians/sec. I tried integrating these values over the time period dT. But these values are not even close to the actual angles. I thought the rotation are in reference to the device, and I tried to convert the values to the real World coordinates. But I didnt get a good algorithm for that.
Can someone help me or point to the right resources to solve this issue?
EDIT:
I forgot to mention in the question, I am trying to do this without GPS, (in the scenario when GPS fails.) And I am trying to avoid Sensor fusion as I am planning to use only Gyroscope as I am looking for a solution that could run even out of Android platform. I am even talking to the OBD to get the actual speed of the vehicle. So I am just trying to collect Gyroscope data from any client and process it at the back end and determine just the turning of a vehicle
You need rotation vector see description here (API level 9).
This thing is using something called sensor fusion to get good quality information about phone orientation relative to earth and magnetic north.
You can also calculate derivate of GPS position to estimate car turn direction.
I'm using the Android SDK (2.2) with a Droid and I have access to the 3-axis accelerometer and gyrometer.
I apologize in advance if this reveals my ignorance of physics. It has been awhile.
What I don't understand is why the accelerometer is giving different x,y,z values when I tilt the phone. It's standing still, at least, with negligible acceleration and even mostly constant velocity, it's just tilted. I thought that this was the gyrometer's job?
I don't want gravity in the equation...I just want the other forces affecting the phone. Everything had better be 0,0,0 unless I'm moving it faster. I want to know how hard I hit the phone on the table, and the direction and magnitude of the x,y,z forces when I do so. So far, I am confused beyond belief about how to access this with the accelerometer and gyrometer data. I'm trying to take the gyrometer as the direction and the accelerometer as the magnitude. Unfortunately, the data for those aren't really in sync (I have to setup two events to get each pair of data, and they may come at different times), but it might be good enough to just buffer them and assume some sort of synchronization.
Desperate,
Andrew
I'm afraid that's just how accelerometers work, here on earth there will always be 9.8 Gs pointing at the ground.
If you can assume that you have a working compass, just take a vector of length STANDARD_GRAVITY, rotate with the current orientation taken from the magnetometer and subtract this from your accelerometer's vector.
Keep in mind that you should also dampen the values you get back from the accelerometer, use a running average of the last few values.