Android 如何获得电话';s方位角与罗盘读数和陀螺仪读数?
我希望通过以下方法获取手机的当前方向:Android 如何获得电话';s方位角与罗盘读数和陀螺仪读数?,android,sensors,android-sensors,gyroscope,magnetometer,Android,Sensors,Android Sensors,Gyroscope,Magnetometer,我希望通过以下方法获取手机的当前方向: 首先通过getRotationMatrix()和getOrientation()获取初始方向(方位角) 将陀螺仪读数随时间的积分添加到其中,以获得当前方向 电话定位: 手机的x-y平面与地平面平行固定。i、 例如,处于“边走边发短信”的状态 “getOrientation()”退货: Android API允许我从getOrientation()轻松获取方向,即方位角、俯仰、滚动 请注意此方法始终返回其范围内的值:[0,-PI]和[o,PI] 我的问题:
getRotationMatrix()
和getOrientation()
获取初始方向(方位角)getOrientation()
”退货:
Android API允许我从getOrientation()
轻松获取方向,即方位角、俯仰、滚动
请注意此方法始终返回其范围内的值:[0,-PI]
和[o,PI]
我的问题:
由于陀螺仪读数的积分(用dR
表示)可能很大,因此当我做CurrentOrientation+=dR
时,CurrentOrientation
可能超过[0,-PI]
[0,-PI]
和[o,PI]
范围内的当前方向?
我在Python中尝试了以下内容,但我高度怀疑其正确性
rotation = scipy.integrate.trapz(gyroSeries, timeSeries) # integration
if (headingDirection - rotation) < -np.pi:
headingDirection += 2 * np.pi
elif (headingDirection - rotation) > np.pi:
headingDirection -= 2 * np.pi
# Complementary Filter
headingDirection = ALPHA * (headingDirection - rotation) + (1 - ALPHA) * np.mean(azimuth[np.array(stepNo.tolist()) == i])
if headingDirection < -np.pi:
headingDirection += 2 * np.pi
elif headingDirection > np.pi:
headingDirection -= 2 * np.pi
rotation=scipy.integrate.trapz(陀螺系列、时间系列)#集成
如果(前进方向-旋转)<-np.pi:
车头方向+=2*np.pi
elif(前进方向-旋转)>np.pi:
车头方向-=2*np.pi
#互补滤波器
航向=α*(航向-旋转)+(1-α)*np.平均值(方位角[np.阵列(stepNo.tolist())==i])
如果车头方向<-np.pi:
车头方向+=2*np.pi
elif头向>np.pi:
车头方向-=2*np.pi
备注 这并不是那么简单,因为它涉及到以下问题制造者:
0
到-PI
,然后直接跳到+PI
,并通过+PI/2
逐渐返回到0
dR
,还是减去dR
方位传感器已经在为您整合陀螺仪读数,这就是您获得方位的方式。我不明白你为什么自己做这件事 您没有正确集成陀螺仪读数,这比
CurrentOrientation+=dR更复杂(这是不正确的)。如果您需要集成陀螺读数(我不明白为什么,SensorManager已经在为您进行集成),请阅读如何正确进行集成(方程式17)
不要尝试与欧拉角积分(又称方位角、俯仰角、横滚角),不会有好结果
请在计算中使用四元数或旋转矩阵,而不是欧拉角。如果使用旋转矩阵,则始终可以将其转换为Euler角度,请参见
(对于四元数也是如此。)有两种方法(在非退根情况下)表示旋转,即,将得到两个欧拉角。选择所需范围内的一个。(如果存在,则有无限多个欧拉角,请参见上面的PDF)。只要保证在将旋转矩阵转换为欧拉角后,在计算中不再开始使用欧拉角
现在还不清楚你在用互补滤波器做什么。你可以在手稿的基础上实现一个非常好的传感器融合,这基本上是一个教程。这不是小事,但我不认为你会找到一个更好的,更容易理解的教程比这篇手稿
当我在这篇手稿的基础上实现传感器融合时,我必须要发现的一件事是,所谓的融合是可能发生的。我通过限定TotalCorrection
(第27页)来处理它。如果你实施这种传感器融合,你就会明白我在说什么
更新:我在这里回答您在接受答案后在评论中发布的问题
我想指南针通过重力和磁场给了我当前的方位,对吗?罗盘中使用陀螺仪吗
是的,如果手机至少静止半秒钟,你只需要使用重力和指南针就可以得到很好的方位估计。以下是如何做到这一点:
不,罗盘中没有使用陀螺仪
你能解释一下为什么我做的整合是错误的吗?我知道如果我的手机的俯仰点向上,欧拉角就会失效。但是我的集成还有什么问题吗
有两件不相关的事情:(i)积分应该以不同的方式进行,(ii)欧拉角是一个麻烦,因为万向锁。我再说一遍,这两个是不相关的
至于
public class Compass implements SensorEventListener
{
public static final float TWENTY_FIVE_DEGREE_IN_RADIAN = 0.436332313f;
public static final float ONE_FIFTY_FIVE_DEGREE_IN_RADIAN = 2.7052603f;
private SensorManager mSensorManager;
private float[] mGravity;
private float[] mMagnetic;
// If the device is flat mOrientation[0] = azimuth, mOrientation[1] = pitch
// and mOrientation[2] = roll, otherwise mOrientation[0] is equal to Float.NAN
private float[] mOrientation = new float[3];
private LinkedList<Float> mCompassHist = new LinkedList<Float>();
private float[] mCompassHistSum = new float[]{0.0f, 0.0f};
private int mHistoryMaxLength;
public Compass(Context context)
{
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
// Adjust the history length to fit your need, the faster the sensor rate
// the larger value is needed for stable result.
mHistoryMaxLength = 20;
}
public void registerListener(int sensorRate)
{
Sensor magneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
if (magneticSensor != null)
{
mSensorManager.registerListener(this, magneticSensor, sensorRate);
}
Sensor gravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
if (gravitySensor != null)
{
mSensorManager.registerListener(this, gravitySensor, sensorRate);
}
}
public void unregisterListener()
{
mSensorManager.unregisterListener(this);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
@Override
public void onSensorChanged(SensorEvent event)
{
if (event.sensor.getType() == Sensor.TYPE_GRAVITY)
{
mGravity = event.values.clone();
}
else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
{
mMagnetic = event.values.clone();
}
if (!(mGravity == null || mMagnetic == null))
{
mOrientation = getOrientation();
}
}
private void getOrientation()
{
float[] rotMatrix = new float[9];
if (SensorManager.getRotationMatrix(rotMatrix, null,
mGravity, mMagnetic))
{
float inclination = (float) Math.acos(rotMatrix[8]);
// device is flat
if (inclination < TWENTY_FIVE_DEGREE_IN_RADIAN
|| inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN)
{
float[] orientation = sensorManager.getOrientation(rotMatrix, mOrientation);
mCompassHist.add(orientation[0]);
mOrientation[0] = averageAngle();
}
else
{
mOrientation[0] = Float.NAN;
clearCompassHist();
}
}
}
private void clearCompassHist()
{
mCompassHistSum[0] = 0;
mCompassHistSum[1] = 0;
mCompassHist.clear();
}
public float averageAngle()
{
int totalTerms = mCompassHist.size();
if (totalTerms > mHistoryMaxLength)
{
float firstTerm = mCompassHist.removeFirst();
mCompassHistSum[0] -= Math.sin(firstTerm);
mCompassHistSum[1] -= Math.cos(firstTerm);
totalTerms -= 1;
}
float lastTerm = mCompassHist.getLast();
mCompassHistSum[0] += Math.sin(lastTerm);
mCompassHistSum[1] += Math.cos(lastTerm);
float angle = (float) Math.atan2(mCompassHistSum[0] / totalTerms, mCompassHistSum[1] / totalTerms);
return angle;
}
}
private Compass mCompass;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mCompass = new Compass(this);
}
@Override
protected void onPause()
{
super.onPause();
mCompass.unregisterListener();
}
@Override
protected void onResume()
{
super.onResume();
mCompass.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
switch (sensorEvent.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
mAccValues[0] = sensorEvent.values[0];
mAccValues[1] = sensorEvent.values[1];
mAccValues[2] = sensorEvent.values[2];
break;
case Sensor.TYPE_MAGNETIC_FIELD:
mMagValues[0] = sensorEvent.values[0];
mMagValues[1] = sensorEvent.values[1];
mMagValues[2] = sensorEvent.values[2];
break;
}
}
private void updateData() {
SensorManager.getRotationMatrix(mR, mI, mAccValues, mMagValues);
/**
* arg 2: what world(according to app) axis , device's x axis aligns with
* arg 3: what world(according to app) axis , device's y axis aligns with
* world x = app's x = app's east
* world y = app's y = app's north
* device x = device's left side = device's east
* device y = device's top side = device's north
*/
switch (mDispRotation) {
case Surface.ROTATION_90:
SensorManager.remapCoordinateSystem(mR, SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, mR2);
break;
case Surface.ROTATION_270:
SensorManager.remapCoordinateSystem(mR, SensorManager.AXIS_MINUS_Y, SensorManager.AXIS_X, mR2);
break;
case Surface.ROTATION_180:
SensorManager.remapCoordinateSystem(mR, SensorManager.AXIS_MINUS_X, SensorManager.AXIS_MINUS_Y, mR2);
break;
case Surface.ROTATION_0:
default:
mR2 = mR;
}
SensorManager.getOrientation(mR2, mO);
//--upside down when abs roll > 90--
if (Math.abs(mO[2]) > PI_BY_TWO) {
//--fix, azimuth always to true north, even when device upside down, realistic --
mO[0] = -mO[0];
//--fix, roll never upside down, even when device upside down, unrealistic --
//mO[2] = mO[2] > 0 ? PI - mO[2] : - (PI - Math.abs(mO[2]));
//--fix, pitch comes from opposite , when device goes upside down, realistic --
mO[1] = -mO[1];
}
CircleUtils.convertRadToDegrees(mO, mOut);
CircleUtils.normalize(mOut);
//--write--
mResults[0] = mOut[0];
mResults[1] = mOut[1];
mResults[2] = mOut[2];
}