I'm working on an Android device which does not have a Magnetometer but has a Rotation Sensor (Gyro) and Gravity Sensor and GPS. I would like to determine True North / Azimuth from these sensors but can't figure out how. Without a Magnetometer, how do I determine the orientation of this device? Clearly somehow it knows where North is given the Maps app works just fine.
UPDATE:
You can the Rotation Vector Sensor that seems perfect for your situation.
private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
The system of coordinates you want is as follows:
X is defined as the vector product Y x Z. It is tangential to the ground at the device's current location and points approximately East.
Y is tangential to the ground at the device's current location and points toward the geomagnetic North Pole.
Z points toward the sky and is perpendicular to the ground plane.
Android makes a lot of sensors available
if you specifically wish to use the gyroscope (Sensor.TYPE_GYROSCOPE) you could do it like this (taken from the Google example):
private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
public void onSensorChanged(SensorEvent event) {
//...
}
but you could try and check if you can use a higher level API that will leverage more than one sensor
You should have a look at this simple example by JavaCodeGeeks where they leverage the default Sensor.TYPE_ORIENTATION to build an android compass
Just a little more info on the Sensor.TYPE_ORIENTATION. According to Google you can still use device orientation, but by calling getOrientation() instead of acting directly over the sensor.
Instead of using raw data from the orientation sensor, we recommend
that you use the getRotationMatrix() method in conjunction with the
getOrientation() method to compute orientation values, as shown in the
following code sample. As part of this process, you can use the
remapCoordinateSystem() method to translate the orientation values to
your application's frame of reference.
The bad news is that both getOrientation() and the old Sensor.TYPE_ORIENTATION rely at least partially on the magnetometer
Hope this helps!
Related
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
I want to detect the movement of the hand dynamically and modify sound accordingly. The phone is in the hand in a fixed orientation which does not change. For Example, I am holding the phone in my stretched hand and as it moves to the right or left, my music volume changes dynamically; If I move up and down the speed of playing changes and moving at some intermediate angle changes both speed and volume accordingly. I charted the accelerometer data while doing these motions and there seems to be some pattern but I am not sure how to filter those. I have looked at a lot of posts - High Pass/Low Pass filters, Kalman Filters, Gesture Recognizers but it is difficult to understand what is the appropriate method. Most of the posts don't seem to detect dynamically - but only when a certain gesture is finished. I only need to use accelerometer and not gyroscope and any other sensor. What is the correct approach here? Are there any existing libraries that do this?
if your activity implements
SensorEventListener
and you use the variables
private SensorManager sensorManager;
private Sensor mAccelerometer;
in your OnCreate() you instantiate them and register the listener like this:
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_FASTEST);
and then use this method to get the accelerometer data
public void onSensorChanged(SensorEvent event) {
double x = event.values[0];
double y = event.values[1];
double z = event.values[2];
}
and you can take it from there depending on the values you get from the accelerometer, make the changes to the volume or whatever
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 .
I'm using Samsung Galaxy S3. When I retrieved the available sensors:
I got a result like below:
LSM330DLC 3-axis Accelerometer TYPE_ACCELEROMETER
AK8975C 3-axis Magnetic field sensor TYPE_MAGNETIC_FIELD
iNemoEngine Orientation sensor TYPE_ORIENTATION
CM36651 Light sensor TYPE_LIGHT
CM36651 Proximity sensor TYPE_PROXIMITY
LSM330DLC Gyroscope sensor TYPE_GYROSCOPE
iNemoEngine Gravity sensor TYPE_GRAVITY
iNemoEngine Linear Acceleration sensor-S/W TYPE_LINEAR_ACCELERATION
iNemoEngine Rotation_Vector sensor TYPE_ROTATION_VECTOR
LPS331AP Pressure Sensor TYPE_PRESSURE
Rotation Vector Sensor TYPE_ROTATION_VECTOR
Gravity Sensor - software sensor TYPE_GRAVITY
Linear Acceleration Sensor TYPE_LINEAR_ACCELERATION
Orientation Sensor TYPE_ORIENTATION
Corrected Gyroscope Sensor TYPE_GYROSCOPE
STMicroelectronics seems to be the default one and sensors provided by Google Inc and Samsung Inc may be the secondary sensors.
When I used getVendor() method, it returned STMicroelectronics(being the hardware default sensor) and Samsung. But when I registered both of the sensors, the onSensorChanged() was called that returned the orientation values(pitch, roll and azimuth) with great difference.
Example, I got the following values at the same timestamp(with difference of few seconds).
pitch: roll: azimuth:
Samsung 0.5917465 -4.212 84.583
STMicroelectronics 0.0865345 -3.88854 356.825
Any idea of why there is a difference in both or should we always monitor both the hardware and software sensors?
Accelerometer, Magnetic, Light, Proximity, Gyroscope, Pressure, Gravity, these are hardware-sensors.
'iNemoEngine xxx' should be a kind of 'virtual sensor', implemented by Google in ICS.
linear accelerometer, rotation-vector, orientation, these are software sensor implemented using sensor fusion algorithm.
http://electronicdesign.com/ios/understanding-virtual-sensors-sensor-fusion-context-aware-applications
http://www.sensorplatforms.com/which-sensors-in-android-gets-direct-input-what-are-virtual-sensors/
http://www.thousand-thoughts.com/2012/03/android-sensor-fusion-tutorial/
The sensors object will be a list of all available sensors on the device.
To check for a specific sensor, use one of the other sensor constants such as, TYPE_TEMPERATURE, TYPE_RELATIVE_HUMIDITY or TYPE_PRESSURE.
There's also the getDefaultSensor() method. Passing a specific sensor constant to it will also determine whether a sensor is available on a device.
And if a device has more than one sensor of a given type, one of the sensors will be set as the default sensor. If there is no default sensor set, getDefaultSensor() will return null, thus indicating that the sensor is not present.
For example, the code to check for a gyroscope sensor using the getDefaultSensor() method could look something like this.
if (mSensorMgr.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null)
{
// Yesssss...gyroscope sensor available
}
else
{
// There's no gyroscope on this device :(
}
Hope it helps..
The 2 sensors are provided by the 2 vendors as mentioned by madhus. It can be obtained using the code:
List<Sensor> gyro =mSensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
for(int i=0;i<gyro.size();i++) {
Log.d(TAG,"The vendor is: " +gyro.get(i).getVendor() + " **" +gyro.get(i).getVersion());
}
I got the vendors as STMicroElectronics and google Inc for all the sensors, except orientation(Samsung Inc and STMicroElectronics). However, as the default and hardware sensor is STMicroElectronics, I am using that.
I am developing an AR application on Android and would like to to, regardless of device roll orientation get horizontal and vertical values, much like a spirit level. An example would be a user holds their device in portrait mode and spins their phone, I would like the horizon on the phone to match the natural horizon. I have played with the roll value returned from the sensor manager but it seems to take pitch into account (ie. the device is now in landscape mode, what should be pitch affects roll.)
Also, when reading pitch, I would like the horizon to move up and down, regardless of roll. At the moment, when the device has rolled to 90 degrees, any pitch changes move in the horizontal direction rather than the vertical direction.
Any pointers?
Thanks in advance.
Paul
Yeah, I think your best bet, which I understand has it's flaws is using the accelerometers to determine the direction of the ground.
Use something like this....
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
in your onCreate method, then put this
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
in your onResume
and this to handle the updates
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float xAcceleration = event.values[0];
float yAcceleration = event.values[1];
float zAcceleration = event.values[2];
Then, just use those Acceleration values to determine the direction of the ground. :-)
I find that way is a lot more fluid, I hope that helps. :-)
For more info, check out the following for more info:
http://developer.android.com/reference/android/hardware/SensorEvent.html#values