Sensor values accelerometer to decimal degrees

Asked

Viewed 136 times

1

I need to take the slope and declination value of the cell phone in landscape mode, but the values I need are in decimal degrees, from -90 to 90 degrees. I tried to use the accelerometer, but the values of this sensor are 0 a 10. I also tried to use the magnetometer, but some phones do not have this sensor. How can I get these values using only the accelerometer?

Code using the accelerometer:

package br.eng.itech.smarthipsometer_01;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity implements SensorEventListener {

    SensorManager sensorManager;
    Sensor accelerometer;

    TextView textViewX;
    TextView textViewY;
    TextView textViewZ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textViewX = (TextView) findViewById(R.id.textViewX);
        textViewY = (TextView) findViewById(R.id.textViewY);
        textViewZ = (TextView) findViewById(R.id.textViewZ);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

    }

    @Override
    protected void onResume() {
        super.onResume();
        if (accelerometer == null){
            Toast.makeText(this, "Sensor acelerômetro não encontrado no dispositivo", Toast.LENGTH_SHORT).show();
            finish();
        }else {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
        }
    }

    @Override
    protected void onPause() {
        sensorManager.unregisterListener(this);
        super.onPause();
    }

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {

        textViewX.setText(String.format("X: %.2f", sensorEvent.values[0]));
        textViewY.setText(String.format("Y: %.2f", sensorEvent.values[1]));
        textViewZ.setText(String.format("Z: %.2f", sensorEvent.values[2]));

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) {

    }
}

1 answer

1


Note: I’m not an Android programmer, so I’m answering this only with the concepts of physics and mathematics acquired over the years. Perhaps it would be appropriate to look at official documentation on the positioning of the device, but it passes through a 3x3 rotation matrix to then find the angles; it also makes use of the accelerometer and magnetometer readings

The sensor of the type TYPE_ACCELEROMETER will return the acceleration force on each axis (see documentation). In this type of sensor, the return is a vector with 3 positions:

  • SensorEvent.values[0], acceleration force on axis X
  • SensorEvent.values[1], acceleration force on Y-axis
  • SensorEvent.values[2], acceleration force on Z axis

These axes are considered according to the image below:

eixos de um dispositivo Android

Note also that the acceleration is in m/s², according to the base unit of the SI (the unit could be in N (Newtons) as well, but this implies knowing the mass of the device and of anything else attached to it, but in the end that mass can be despised and the only thing that really matters is the rate of acceleration).

When the body is fully upright without undergoing changes in its positions, you will have the effect of the force of gravity (on the Y axis, pointing downwards) and the normal force in perfect balance with gravity. This sensor doesn’t care about normal force, so ultimately you’re just measuring the force of gravity. As we are on Earth, gravity will have the effect of accelerating the 9,8 m/s². So then you get the impression that the results are between 0 and 10, whatever the position.

For you to detect that the cell phone is more "lying down" than "standing up", the component of gravity on the X axis must be significant than the component of gravity on the Y axis. But still this will not be able to tell you how "landscape" or how "portrait" the cell phone actually is. To answer this question, it is also necessary to understand how to calculate Φ and Θ of this question here from Mathematics. I will reproduce the image in this question below to be better illustrated:

vetor no espaço 3D

Note that, in this case, the axis that this image considers "up" is Z, and the axis that "leaves the reading screen" is X. This is the most classic representation of 3D space; but we can reenter these axes according to the Android orientation, and that’s what we’ll do. We will consider the "up" axis as the Y, and the "out of it" axis as the Z. Take this reinterpretation into consideration when reading my explanations below

In our case, we need to consider the value of Θ to know how inclined the cell phone is. When the value of Θ is 0 degrees, the cell phone will be perfectly aligned vertically. When it is Θ = 180 degrees, it will be upside down. One measure to check this quickly is to take the cosine, for cos(0) = 1 and cos(180) = -1. The angle, therefore, can be considered as an intermediate value that we can discard. Or, if we need it too much, we can use the function cosine bow (acos): given the value of the cosine, which angle generates this cosine?

Note that acos returns in radians; to convert to degrees, read this answer.

To calculate the cosine value of Θ we need the magnitude of the vector (hypotenuse) and its component on the Y axis (adjacent cathode). That answer shows how to find the magnitude of a 3D vector. The component on the Y axis is SensorEvent.values[1]. Then, to find the cosine of the slope Θ, the following calculation would be necessary:

float xPart = SensorEvent.values[0];
float yPart = SensorEvent.values[1];
float zPart = SensorEvent.values[2];

float magnitude = Math.sqrt(xPart*xPart + yPart*yPart + zPart*zPart);

float cosTheta = ypart/magnitude; // <== variável de nosso interesse

On top of this information, you can make some decision about whether you are landscape or portrait or whatever. Note that I am not considering here if the cell phone is "looking at the ground" (rotation on the X axis), only its inclination (rotation on the Z axis). The rotation on the Y-axis is negligible for its calculation and I believe that it is purely not detectable only by gravity (perhaps it is detectable at the moment when the Y-axis is rotated together with other rotations, but it is of all sorts negligible).

If you really want the angle in degrees, just do the following:

float thetaGraus = Math.acos(cosTheta) * 180.0/Math.PI;
  • thank you so much for the help! Answer well summarized but very well explained. I managed to reach my goal through your explanation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.