Android中的指南针

14

我正在尝试使用安卓手机的加速度计和磁场传感器编写一个指南针程序,现在我想知道如何获得正确的角度。

我读取"accele"和"magne"中加速度计和磁场传感器的值。为了获取角度,我执行以下操作:

float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, accele, magne);
        if(success) {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = orientation[0]; // contains azimuth, pitch, roll
                            ....

后来,我使用旋转矩阵来放置我的针头:

rotation.setRotate(azimuth, compass.getWidth() / 2, compass.getHeight() / 2);
canvas.drawBitmap(needle, rotation, null);

现在,getOrientation的文档说明orientation[0]应该是围绕z轴旋转的角度。而TYPE_ORIENTATION的文档说明了“方位角是磁北方向和y轴之间的角度,在z轴周围旋转(0到359)。0=北,90=东,180=南,270=西”。

然而我的方位角不在0到359之间,而是在-2到2之间。getOrientation返回的方位角到底是什么,我如何将其转换为角度?

3个回答

21

使用以下方法将给定的弧度方位角(-PI,+PI)转换为度数(0, 360)

float azimuthInRadians = orientation[0];
float azimuthInDegress = (float)Math.toDegrees(azimuthInRadians);
if (azimuthInDegress < 0.0f) {
    azimuthInDegress += 360.0f;
}

变量名仅用于方便起见 ;-)


1
Math.toDegrees()函数将把一个在-PI到+PI弧度之间的角度转换为在-180到180之间的角度。这只是将所有东西放在正方向上。 - rgrocha
2
嗨!是的,这应该可以正常工作。谢谢!然而,从Matrix.setRotate()中,-180和180被解释为相同的,所以if语句是不必要的。 - user1809923

8

决定“final float alpha = 0.97f;”的标准是什么?你使用了0.97作为过滤器常数,谷歌使用了0.8。 - DeltaCap019
1
它将影响箭头旋转的速度。因此,选择基于视觉感觉:更加平滑的运动。 - iutinvg

2

我在Google的ApiDemos中发现了这个:

    /*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.graphics;

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Config;
import android.util.Log;
import android.view.View;

public class Compass extends GraphicsActivity {

    private static final String TAG = "Compass";

    private SensorManager mSensorManager;
    private SampleView mView;
    private float[] mValues;

    private final SensorListener mListener = new SensorListener() {

        public void onSensorChanged(int sensor, float[] values) {
            if (Config.LOGD) Log.d(TAG, "sensorChanged (" + values[0] + ", " + values[1] + ", " + values[2] + ")");
            mValues = values;
            if (mView != null) {
                mView.invalidate();
            }
        }

        public void onAccuracyChanged(int sensor, int accuracy) {
            // TODO Auto-generated method stub

        }
    };

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        mView = new SampleView(this);
        setContentView(mView);
    }

    @Override
    protected void onResume()
    {
        if (Config.LOGD) Log.d(TAG, "onResume");
        super.onResume();
        mSensorManager.registerListener(mListener, 
                SensorManager.SENSOR_ORIENTATION,
                SensorManager.SENSOR_DELAY_GAME);
    }

    @Override
    protected void onStop()
    {
        if (Config.LOGD) Log.d(TAG, "onStop");
        mSensorManager.unregisterListener(mListener);
        super.onStop();
    }

    private class SampleView extends View {
        private Paint   mPaint = new Paint();
        private Path    mPath = new Path();
        private boolean mAnimate;
        private long    mNextTime;

        public SampleView(Context context) {
            super(context);

            // Construct a wedge-shaped path
            mPath.moveTo(0, -50);
            mPath.lineTo(-20, 60);
            mPath.lineTo(0, 50);
            mPath.lineTo(20, 60);
            mPath.close();
        }

        @Override protected void onDraw(Canvas canvas) {
            Paint paint = mPaint;

            canvas.drawColor(Color.WHITE);

            paint.setAntiAlias(true);
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.FILL);

            int w = canvas.getWidth();
            int h = canvas.getHeight();
            int cx = w / 2;
            int cy = h / 2;

            canvas.translate(cx, cy);
            if (mValues != null) {            
                canvas.rotate(-mValues[0]);
            }
            canvas.drawPath(mPath, mPaint);
        }

        @Override
        protected void onAttachedToWindow() {
            mAnimate = true;
            super.onAttachedToWindow();
        }

        @Override
        protected void onDetachedFromWindow() {
            mAnimate = false;
            super.onDetachedFromWindow();
        }
    }
}

正如您所看到的,您可以获得0到360度之间的角度


如果你解释一下,那就太好了 :) - user1809923
嗨,那是一个有趣的方法,然而我认为这可能不是非常准确,因为我只是估计了-2到2。 - user1809923
我还有点困惑。首先,我仍然认为我们需要使用orientation[0],因为这样我们就可以绕Z轴旋转,而Y指向北,X大致指向西。关于转换:通常的弧度转角度公式是:degrees = (radians/pi)*180。但是在这里,我认为弧度被假定在0到2pi之间。由于我认为我的弧度范围在-pi到pi之间,如果我相应地转换弧度,我会得到degrees = (radians/pi)*180 + 180,不是吗? - user1809923
是的,这正是Math.toDegrees(orientation[2])+180所做的。在toDegrees的文档中写道:“angrad * 180 / pi。”,所以只需加上180就可以了。这个方程式与我的第一个方程式相同。 - Uriel Frankel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接