I am working on an Android app and I am trying to figure out how can I combine accelerometer and magnetometer to find the walking direction of the user when indoors. I understand that I can only get an approximate direction and not the real value. :)
Any ideas? Would gyroscope also help in this case?
Thanks
The user's acceleration is nearly zero most of the time, therefore, walking will produce a predictable pattern. This general property serves as something like an anchor point, from which other stages of the walk can be analyzed.
With this in mind, i think a position classifier to detect where the phone is located on the body is needed. After that you can measure the angle between the phone position and the magnetic north of the earth.
But be aware, for an accurate measure, it will need an enormous filtering.
don't know what kind of direction you mean but might possible SensorEvent helps you what your are looking. SensorEventListener is being used to build compass app.
https://developer.android.com/reference/android/hardware/SensorEvent.html
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.
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.
I have a question regarding inertial navigation with a mobile device.
I am using an android tablet for development but I think the question is related to
all types (even with better sensors) of hardware.
The most basic question when developing an inertial system is how to determine the
direction of the carrier's movement.
Even if we assume that the magnetometer readings are 100% accurate (which they are obviously not!) There is still the question of the device orientation relative to the user.
Simple example - if the user is walking north, but holds the device with the device's Y axis points north-east, (a link to a picture of the different axis: http://developer.android.com/reference/android/hardware/SensorEvent.html)
Then the magnetometer will point towards north-east.
How can we tell which way the user is actually heading?
(The same will be true if we use both magnetometer and Gyro for determining heading)
A possible solution will be to use the Accelerometer's Y-axis and X-axis readings.
Something in the direction of using arctan(a-Y/a-X)
(for example - if the user holds the device perfectly straight, then the X-Axis will show nothing...)
But since the Accelerometer's readings are not stable, it is not so easy...
Does anyone know of an algorithm that actually works? I am sure this is a well known problem, but I can't seem to find references to solutions...
Thanks in advance!
Ariel
See this answer for an idea: by obtaining the acceleration values in relation to the earth, you can then use the atan2 function to compute the actual direction.
You mention the user holds the tablet, and I assume fairly stable (unlike a case I am working on, where the user moves the phone constantly). Yet, for some reason, the user may change the orientation of the device, and this may influence the readings you obtain.
Thus, in the event of an orientation change, you should call remapCoordinates() accordingly to fix the readings you obtain.
NOTE: You can also use the getOrientation() method accordingly, and the first field represents the heading direction.
The really right answer is to leave the device sitting there for a while, detect the rotation of the earth, and then compute true north from that. Unfortunately, the gyros in a mobile phone aren't accurate enough for that....
I would like to detect when the phone is in motion, but not all kind of motion. For example picking up or waving the phone should not trigger.
I would like ideally to run a code when the phone/person is in > "walking" state. What options I have?
you can use a combination of the accelerometer and the digital compass, in phones that have them, to determine a speed and direction.
If all you need to do is determine if the person is walking, all you need is the accelerometer. Just process its output for foot steps.
There are plenty of tutorials on the web for detecting foot steps with an accelerometer.
You can also get your location from Wifi-Networks and Cell Towers. All location providers in Android are subclasses of android.location.LocationProvider. That's probably a good place to start. I don't know that either of those would be best for you, as their "range" can be several hundreds of feet wide.
Android accelerometer provides us the feature to know the acceleration using SensorEvent class. So use the object of the class, and handle onSensorChanged(), to determine the movement in the device.
x = sensorEvent.values[0];
States the acceleration in x direction.
So what you may be interested in is, find acceleration in x,y and z directions, and try to calculate the Average and standard deviation for the last 10 such samples.
Working on the standard deviation will surely get you to the right point! If it goes to 0, it means device is still. If SD>0.5 for more than 15sec's or so..it means device is continously moving! Lemme know if you need more help!
Cheers,
Nithin
I need to build compass for my application.
From reading the documentation it seems there are two reasonable ways of doing this:
Sensor.TYPE_ORIENTATION method: This is the easy way of doing it. The problem with this is it is not accurate. When I compare my reading with Snaptic Compass it is about 10-15 degress off which for my purposes is unacceptable.
Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_MAGNETIC_FIELD and getRotationMatrix() in conjunction with remapCoordinateSystem() and getOrientation() method: The documentation says this "is usually more accurate". The problem is regardless of the delay I register with listener the compass goes crazy even when the device is stationary on flat surface.
Any suggestions for solving this problem will be greatly appreciated.
Looks like mob1lejunkie has figured this out already, but just for future reference, let me mention that the issue here is magnetic declination (the difference between magnetic north and true north). You can try out an online model from NOAA to get a sense for what the difference is in your part of the world.
Android includes a model called GeomagneticField that can be used to compute the approximate declination given a latitude, longitude, and altitude. So, what I believe you need to do to compute true north is read out the azimuth value from Sensor.TYPE_ORIENTATION and then add to that GeomagneticField.getDeclination().
It's the downside of compact parts made for phones. A $5000 IMU that we're using right now has an error of +/- 2 degrees and in that range it seems to go crazy as well.
Try stepping away from interference, metal surfaces, and see if it improves. If not, I hate to break the bad news, but that's the hardware.
Have you seen these?
http://developer.android.com/reference/android/hardware/SensorManager.html
http://groups.google.com/group/android-developers/browse_thread/thread/bbb0e8b97c673cf9/5c16bc2e49f6e972?lnk=raot