首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在android中获得平滑的方向数据

如何在android中获得平滑的方向数据
EN

Stack Overflow用户
提问于 2015-01-09 02:00:44
回答 3查看 5.2K关注 0票数 11

我有一个应用程序,它使用方向数据,这是非常好地使用使用Sensor.TYPE_ORIENTAITON的pre API-8方法。平滑这些数据是相对容易的。

我正在尝试更新代码,以避免使用这种过时的方法。新的标准方法是将单个Sensor.TYPE_ORIENTATION替换为Sensor.TYPE_ACCELEROMETERSensor.TYPE_MAGENTIC_FIELD的组合。当接收到该数据时,它被(通过SensorManager.getRotationMatrix())发送到SensorManager.getOrientation()。这(理论上)返回与Sensor.TYPE_ORIENTATION相同的信息(除了不同的单位和轴方向)。

然而,这种方法似乎生成的数据比被弃用的方法(仍然有效)要抖动(即噪声)得多。因此,如果您在同一设备上比较相同的信息,则不推荐使用的方法提供的噪声数据比当前方法少得多。

我如何获得被弃用的方法所提供的实际相同(噪音较小)的数据?

让我的问题更清晰一点:我已经阅读了关于这个问题的各种答案,我已经尝试了所有类型的滤波器:你建议的简单KF / IIR低通;中值滤波器在5到19点之间,但到目前为止,我还没有接近手机通过TYPE_ORIENTATION提供的数据的平滑度。

EN

回答 3

Stack Overflow用户

发布于 2015-01-09 02:44:39

对传感器输出应用低通滤波器。

这是我的低通滤波器方法:

代码语言:javascript
运行
复制
private static final float ALPHA = 0.5f;
//lower alpha should equal smoother movement
...
private float[] applyLowPassFilter(float[] input, float[] output) {
    if ( output == null ) return input;

    for ( int i=0; i<input.length; i++ ) {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}

像这样应用它:

代码语言:javascript
运行
复制
float[] mGravity;
float[] mGeomagnetic;
@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        mGravity = applyLowPassFilter(event.values.clone(), mGravity);
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
        mGeomagnetic = applyLowPassFilter(event.values.clone(), mGeomagnetic);
    if (mGravity != null && mGeomagnetic != null) {
        float R[] = new float[9];
        float I[] = new float[9];

        boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
        if (success) {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = -orientation[0];
            invalidate();
        }
    }
}

这显然是指南针的代码,去掉你不需要的部分。

此外,还可以查看此SE问题How to implement low pass filter using java

票数 8
EN

Stack Overflow用户

发布于 2015-01-11 23:34:20

事实证明,还有另一种没有特别记录的方式来获取定位数据。隐藏在传感器类型列表中的是TYPE_ROTATION_VECTOR。所以,设置一个:

代码语言:javascript
运行
复制
Sensor mRotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
sensorManager.registerListener(this, mRotationVectorSensor, SensorManager.SENSOR_DELAY_GAME);

然后:

代码语言:javascript
运行
复制
@Override
public void onSensorChanged(SensorEvent event) {
    final int eventType = event.sensor.getType();

    if (eventType != Sensor.TYPE_ROTATION_VECTOR) return;

    long timeNow            = System.nanoTime();

    float mOrientationData[] = new float[3];
    calcOrientation(mOrientationData, event.values.clone());

    // Do what you want with mOrientationData
}

关键机制是通过旋转矩阵将输入的旋转数据转换为方向向量。有点令人沮丧的是,方向向量最初来自四元数数据,但我不知道如何直接传递四元数。(如果您想知道四元数与方向和旋转信息的关系以及使用它们的原因,请参见here。)

代码语言:javascript
运行
复制
private void calcOrientation(float[] orientation, float[] incomingValues) {
    // Get the quaternion
    float[] quatF = new float[4];
    SensorManager.getQuaternionFromVector(quatF, incomingValues);

    // Get the rotation matrix
    //
    // This is a variant on the code presented in
    // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/
    // which has been altered for scaling and (I think) a different axis arrangement. It
    // tells you the rotation required to get from the between the phone's axis
    // system and the earth's.
    //
    // Phone axis system:
    // https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords
    //
    // Earth axis system:
    // https://developer.android.com/reference/android/hardware/SensorManager.html#getRotationMatrix(float[], float[], float[], float[])
    //
    // Background information:
    // https://en.wikipedia.org/wiki/Rotation_matrix
    //
    float[][] rotMatF = new float[3][3];
    rotMatF[0][0] = quatF[1]*quatF[1] + quatF[0]*quatF[0] - 0.5f;
    rotMatF[0][1] = quatF[1]*quatF[2] - quatF[3]*quatF[0];
    rotMatF[0][2] = quatF[1]*quatF[3] + quatF[2]*quatF[0];
    rotMatF[1][0] = quatF[1]*quatF[2] + quatF[3]*quatF[0];
    rotMatF[1][1] = quatF[2]*quatF[2] + quatF[0]*quatF[0] - 0.5f;
    rotMatF[1][2] = quatF[2]*quatF[3] - quatF[1]*quatF[0];
    rotMatF[2][0] = quatF[1]*quatF[3] - quatF[2]*quatF[0];
    rotMatF[2][1] = quatF[2]*quatF[3] + quatF[1]*quatF[0];
    rotMatF[2][2] = quatF[3]*quatF[3] + quatF[0]*quatF[0] - 0.5f;

    // Get the orientation of the phone from the rotation matrix
    //
    // There is some discussion of this at
    // http://stackoverflow.com/questions/30279065/how-to-get-the-euler-angles-from-the-rotation-vector-sensor-type-rotation-vecto
    // in particular equation 451.
    //
    final float rad2deg = (float)(180.0 / PI);
    orientation[0] = (float)Math.atan2(-rotMatF[1][0], rotMatF[0][0]) * rad2deg;
    orientation[1] = (float)Math.atan2(-rotMatF[2][1], rotMatF[2][2]) * rad2deg;
    orientation[2] = (float)Math.asin ( rotMatF[2][0])                * rad2deg;
    if (orientation[0] < 0) orientation[0] += 360;
}

这似乎给出的数据在感觉上与旧的TYPE_ORIENTATION数据非常相似(我还没有运行数字测试):它可用于边缘过滤设备的运动控制。

还有一些有用的信息here,以及一个可能的替代解决方案here

票数 1
EN

Stack Overflow用户

发布于 2021-01-03 05:31:44

下面是我使用SensorManager.SENSOR_DELAY_GAME进行快速更新的结果。

代码语言:javascript
运行
复制
@Override
protected void onResume() {
    super.onResume();
    sensor_manager.registerListener(this, sensor_manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
    sensor_manager.registerListener(this, sensor_manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME);
}

移动平均

(效率较低)

代码语言:javascript
运行
复制
private float[] gravity;
private float[] geomagnetic;
private float azimuth;
private float pitch;
private float roll; 

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        gravity = moving_average_gravity(event.values);
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
        geomagnetic = moving_average_geomagnetic(event.values);

    if (gravity != null && geomagnetic != null) {
        float R[] = new float[9];
        float I[] = new float[9];

        boolean success = SensorManager.getRotationMatrix(R, I, gravity, geomagnetic);
        if (success) {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = (float) Math.toDegrees(orientation[0]);
            pitch = (float) Math.toDegrees(orientation[1]);
            roll = (float) Math.toDegrees(orientation[2]);
            //if(roll>-46F && roll<46F)view.setTranslationX((roll/45F)*max_translation); //tilt from -45° to 45° to x-translate a view positioned centrally in a layout, from 0 - max_translation
            Log.i("TAG","azimuth: "+azimuth+" | pitch: "+pitch+" | roll: "+roll);
        }
    }
}




private ArrayList<Float[]> moving_gravity;
private ArrayList<Float[]> moving_geomagnetic;
private static final float moving_average_size=12;//change

private float[] moving_average_gravity(float[] gravity) {
    if(moving_gravity ==null){
        moving_gravity =new ArrayList<>();
        for (int i = 0; i < moving_average_size; i++) {
            moving_gravity.add(new Float[]{0F,0F,0F});
        }return new float[]{0F,0F,0F};
    }

    moving_gravity.remove(0);
    moving_gravity.add(new Float[]{gravity[0],gravity[1],gravity[2]});
    return moving_average(moving_gravity);
}

private float[] moving_average_geomagnetic(float[] geomagnetic) {
    if(moving_geomagnetic ==null){
        this.moving_geomagnetic =new ArrayList<>();
        for (int i = 0; i < moving_average_size; i++) {
            moving_geomagnetic.add(new Float[]{0F,0F,0F});
        }return new float[]{0F,0F,0F};
    }

    moving_geomagnetic.remove(0);
    moving_geomagnetic.add(new Float[]{geomagnetic[0],geomagnetic[1],geomagnetic[2]});
    return moving_average(moving_geomagnetic);
}

private float[] moving_average(ArrayList<Float[]> moving_values){
    float[] moving_average =new float[]{0F,0F,0F};
    for (int i = 0; i < moving_average_size; i++) {
        moving_average[0]+= moving_values.get(i)[0];
        moving_average[1]+= moving_values.get(i)[1];
        moving_average[2]+= moving_values.get(i)[2];
    }
    moving_average[0]= moving_average[0]/moving_average_size;
    moving_average[1]= moving_average[1]/moving_average_size;
    moving_average[2]= moving_average[2]/moving_average_size;
    return moving_average;
}

低通滤波器

(效率更高)

代码语言:javascript
运行
复制
private float[] gravity;
private float[] geomagnetic;
private float azimuth;
private float pitch;
private float roll; 

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        gravity = LPF(event.values.clone(), gravity);
    if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
        geomagnetic = LPF(event.values.clone(), geomagnetic);

    if (gravity != null && geomagnetic != null) {
        float R[] = new float[9];
        float I[] = new float[9];

        boolean success = SensorManager.getRotationMatrix(R, I, gravity, geomagnetic);
        if (success) {
            float orientation[] = new float[3];
            SensorManager.getOrientation(R, orientation);
            azimuth = (float) Math.toDegrees(orientation[0]);
            pitch = (float) Math.toDegrees(orientation[1]);
            roll = (float) Math.toDegrees(orientation[2]);
            //if(roll>-46F && roll<46F)view.setTranslationX((roll/45F)*max_translation); //tilt from -45° to 45° to x-translate a view positioned centrally in a layout, from 0 - max_translation
            Log.i("TAG","azimuth: "+azimuth+" | pitch: "+pitch+" | roll: "+roll);
        }
    }
}




private static final float ALPHA = 1/16F;//adjust sensitivity
private float[] LPF(float[] input, float[] output) {
    if ( output == null ) return input;
    for ( int i=0; i<input.length; i++ ) {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }return output;
}

N.B

12个值的移动平均值,而不是ALPHA = 0.0625的here低通滤波器,而不是here

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27846604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档