跳转至

Android 获取手机当前角度

更新日期 2022-6-30
  • 2022-6-30 修改格式

有一些app可以实时显示出手机当前的朝向。实现一些很有趣的功能。例如指南针。

在一些控制类app中,倾斜手机可以控制设备(玩具车,或无人机之类)的方向和速度。倾斜角度越大,加速度越大。

为获取手机当前姿态的角度,我们需要调用加速度传感器和地磁传感器,获取两者的数据后计算出角度。

测试环境:Android4.4.2 Android5.1
数值范围经过手机(荣耀,魅族)实测

姿态角度信息

通过加速度传感器数据和地磁传感器数据计算得到的角度信息存储在一个float数组中, 按顺序分别是AzimuthPitchRoll

类型 说明
Azimuth 围绕z轴的偏转角度,[-π,π],当面向南方时,值为0。
Pitch 围绕x轴的偏转角度,[-π/2,π/2],手机水平放置时为0。
Roll 围绕y轴的偏转角度,[-π,π],手机水平放置时为0。

获取姿态角度信息的方法

需要SensorManagerSensor

计算用到的方法有

SensorManager.getRotationMatrix(mRMatrix, null, mAccValues, mMagValues);
SensorManager.getOrientation(mRMatrix, mPhoneAngleValues);
mRMatrix是包含9个元素的一维数组。SensorManager.getRotationMatrix计算后得到的旋转矩阵存在其中。

SensorEventListener用于监听传感器的事件。

获取旋转矩阵

调用方法

SensorManager.getRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic)

  • R即旋转矩阵的计算结果,例子中为mRMatrix
  • I是一个转换矩阵,将磁场数据转换进实际的重力坐标中,一般情况下可设为null
  • gravity 加速度传感器获得的数据,通过SensorEventListener.onSensorChanged获取
  • geomagnetic 地磁传感器获得的数据,通过SensorEventListener.onSensorChanged获取

计算角度数据

最终的角度数据,需调用SensorManager.getOrientation(float[] R, float values[])

  • R即旋转矩阵,在这里用于计算
  • 最终的手机姿态信息(弧度值)

主要代码

GyroActivity
/**
 * 用于监视陀螺仪的数据
 */
public class GyroActivity extends Activity implements SensorEventListener {

    private static final String TAG = "rustApp";

    // UI ......

    private SensorManager mSensorManager;
    private Sensor mGyroSensor;
    private Sensor mAccSensor;
    private Sensor mMagSensor;

    // 加速度传感器数据
    float mAccValues[] = new float[3];
    // 地磁传感器数据
    float mMagValues[] = new float[3];
    // 旋转矩阵,用来保存磁场和加速度的数据
    float mRMatrix[] = new float[9];
    // 存储方向传感器的数据(原始数据为弧度)
    float mPhoneAngleValues[] = new float[3];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_gyro);
        initUtils();
        initPhoneSensors();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
        mSensorManager.unregisterListener(this, mGyroSensor);
        mSensorManager.unregisterListener(this, mAccSensor);
        mSensorManager.unregisterListener(this, mMagSensor);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        switch (event.sensor.getType()) {
            case Sensor.TYPE_ACCELEROMETER:
                mAccXTv.setText(String.format(Locale.CHINA, "acc x : %f", event.values[0]));
                mAccYTv.setText(String.format(Locale.CHINA, "acc y : %f", event.values[1]));
                mAccZTv.setText(String.format(Locale.CHINA, "acc z : %f", event.values[2]));
                System.arraycopy(event.values, 0, mAccValues, 0, mAccValues.length);// 获取数据
                break;
            case Sensor.TYPE_GYROSCOPE:
                mPhoneGyroXTv.setText(String.format(Locale.CHINA, "PhoneGyro x : %f", event.values[0]));
                mPhoneGyroYTv.setText(String.format(Locale.CHINA, "PhoneGyro y : %f", event.values[1]));
                mPhoneGyroZTv.setText(String.format(Locale.CHINA, "PhoneGyro z : %f", event.values[2]));
                break;
            case Sensor.TYPE_MAGNETIC_FIELD:
                System.arraycopy(event.values, 0, mMagValues, 0, mMagValues.length);// 获取数据
                break;
        }
        SensorManager.getRotationMatrix(mRMatrix, null, mAccValues, mMagValues);
        SensorManager.getOrientation(mRMatrix, mPhoneAngleValues);// 此时获取到了手机的角度信息
        mPhoneAzTv.setText(String.format(Locale.CHINA, "Azimuth(地平经度): %f", Math.toDegrees(mPhoneAngleValues[0])));
        mPhonePitchTv.setText(String.format(Locale.CHINA, "Pitch: %f", Math.toDegrees(mPhoneAngleValues[1])));
        mPhoneRollTv.setText(String.format(Locale.CHINA, "Roll: %f", Math.toDegrees(mPhoneAngleValues[2])));
    }

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

    }

    private void initUtils() {
        ButterKnife.bind(this);
    }

    private void initPhoneSensors() {
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
        for (Sensor sensor : sensorList) {
            Log.d(TAG, String.format(Locale.CHINA, "[Sensor] name: %s \tvendor:%s",
                    sensor.getName(), sensor.getVendor()));
        }
        // 获取传感器
        mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        mAccSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mMagSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        mSensorManager.registerListener(this, mGyroSensor, SensorManager.SENSOR_DELAY_UI);
        mSensorManager.registerListener(this, mAccSensor, SensorManager.SENSOR_DELAY_UI);
        mSensorManager.registerListener(this, mMagSensor, SensorManager.SENSOR_DELAY_UI);
    }
}
一开始要注册监听器mSensorManager.registerListener。 页面结束时取消监听器mSensorManager.unregisterListener

本站说明

一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~

📖AndroidTutorial 📚AndroidTutorial 🙋反馈问题 🔥最近更新 🍪投喂作者

Ads