我的传感器监测技术出了什么问题?

时间:2013-09-13 12:46:44

标签: java android android-sensors orientation-changes blockingqueue

(请在最后阅读更新3)我正在开发一款不断与设备传感器配合使用的应用程序,可与AccelerometerMagnetic传感器配合使用以检索设备的方向(目的)提到here)。换句话说,我的应用程序需要知道实时设备的方向(但这是永远不可能的,所以尽可能快,但确实和可能一样快!)。正如Reto Meier的专业Android 4应用程序开发中提到的那样

  

加速度计每秒可以更新数百次......

我不能丢失传感器报告的任何数据,我也想对这些数据进行耗时的操作(检索方向然后进行计算......)。我决定使用LinkedBlockingQueue

来解决我的问题
    public void startSensors() {
            LinkedBlockingQueue<float[][]> array=new LinkedBlockingQueue();
    sensorListenerForOrientation = new SensorEventListener() {

        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
                aValues = (event.values.clone());
            else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
                mValues = (event.values.clone());
            if (aValues != null && mValues != null) {
                try {
                    array.put(new float[][] { aValues, mValues });
                } catch (InterruptedException e) {
                }
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };
    Sensor aSensor = sm.getSensorList(Sensor.TYPE_ACCELEROMETER).get(
            sm.getSensorList(Sensor.TYPE_ACCELEROMETER).size() - 1);
    Sensor mSensor = sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD).get(
            sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD).size() - 1);
    sm.registerListener(sensorListenerForOrientation, aSensor,
            SensorManager.SENSOR_DELAY_FASTEST);
    sm.registerListener(sensorListenerForOrientation, mSensor,
            SensorManager.SENSOR_DELAY_FASTEST);
    executor.execute(new Runnable() {
        @Override
        public void run() {
            doCalculations();
        }
    });
}

    public void doCalculations() {
    for (;;) {
        float[][] result = null;
        try {
            result = array.take();
        } catch (InterruptedException e) {
        }
        float[] aValues, mValues;
        aValues = result[0];
        mValues = result[1];

                    int[] degrees=getOrientation(aValues,mValues);
                    Log.e("",String.valueOf(degrees[0]));

                 //other calculations...
                     }
                                }

现在我拿起​​我的设备并向右旋转大约90度,然后快速将其返回到第一个位置(例如1.5秒)但是当我查看设备中注册的方向时,我看到例如: 0,1,2,3,4,5 ......,40,39,38,37,...,0

我只是想说我在结果中看不到大的度数范围。 基于我所做的和我研究的内容我可以确定我没有丢失任何数据,传感器报告的任何新数据都被记录

任何想法,解决方案?!

问候!

更新1:我用我的设备做了另一个实验并获得了令人震惊的结果!如果我将设备快速旋转90度(小于一秒),我可以看到我的结果中的所有度数:0,1,2,3,....,89,90(例如)但是如果我旋转90度,然后将其旋转回第一个位置,结果将是0,1,2,...,36,37,36,...... 2,1,0(例如)......真的令人困惑!

更新2:我更新了doCalculations()方法,以便更清楚我做了什么

  

更新3:我想也许我们可以用另一种方式解决问题!我对此代码有明确的目的。请看this。一世   已经提到将会发生什么,我需要检测一个特定的   运动手势。所以也许是我选择的整个方式(   上面的技术)不是解决这个问题的好方法。也许   最好通过使用其他传感器或使用。来检测该手势   其他方面的传感器相同。你觉得怎么样?

7 个答案:

答案 0 :(得分:7)

因此,您似乎正在尝试为标准的“生产者 - 消费者”问题找到高吞吐量低延迟解决方案。基本上这个想法很简单:减少数据处理开销,并行处理数据。建议如下:

<强> 1。使用“低延迟”库

  • javolution.org - 是一个实时库,旨在使Java或Java-Like / C ++应用程序更快,更具时间可预测性。它包括Android支持。
  • mentaqueue - 是一个超级快速,无垃圾,无锁,双线程(生产者 - 消费者)队列,基于Disruptor的想法。 Android支持未定义(它看起来应该可以工作)。
  • disruptor - 另一个闪电般快速的图书馆
  • trove - 为Java提供高速常规和原始集合。

任何这些解决方案都可以节省大量的CPU周期。

enter image description here

<强> 2。明智地处理数据

每次提交作业都会产生开销。批处理可能非常有用。

enter image description here

持续处理数据。请注意,executor.execute会消耗很多。几个长寿的消费者可能会有所帮助。

第3。最后,使用微优化技术

例如,摆脱if-else-if,转而使用switch

始终跟踪绩效,以确定好的和坏的解决方案。实验

快乐的编码。

答案 1 :(得分:3)

想一想:请尝试以下方法:

public void startSensors() {
    final Stack<Runnable> mStack = new Stack<Runnable>();
    sensorListenerForOrientation = new SensorEventListener() {

        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
                aValues = (event.values.clone());
            else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
                mValues = (event.values.clone());
            if (aValues != null && mValues != null) {
               mStack.push(new Calculater(new float[][] { aValues, mValues });
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };
    Sensor aSensor = sm.getSensorList(Sensor.TYPE_ACCELEROMETER).get(
            sm.getSensorList(Sensor.TYPE_ACCELEROMETER).size() - 1);
    Sensor mSensor = sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD).get(
            sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD).size() - 1);
    sm.registerListener(sensorListenerForOrientation, aSensor,
            SensorManager.SENSOR_DELAY_FASTEST);
    sm.registerListener(sensorListenerForOrientation, mSensor,
            SensorManager.SENSOR_DELAY_FASTEST);
    new Thread() { 
        public void run() {
            while(true)
            {
                try { 
                    Runnable r = mStack.pop();
                    r.run();
                } catch(Exception ex){}
            }
        }
    }.start();
}
private class Calculater implements Runnable {
    float[][] theValues;
    public Calculater(float[][] values) {
        theValues = values;
    }
    public void run() {
        int[] degrees= getOrientation(theValues[0], theValues[1]);
        Log.e("",String.valueOf(degrees[0]));
    }
}

答案 2 :(得分:2)

您的代码看起来很合理。一个很大的未知数是传感器和传感器融合在您的设备中有多好。快速角度变化读数依赖于角加速度的集成,或者物理陀螺仪与磁性数据的混合,以使结果与地球完全对齐。磁数据受周围环境影响。如果您的设备具有低质量传感器或您的环境中存在磁性干扰,则完全可以看到您所看到的各种错误。大型金属结构和磁性设备(如电机或甚至荧光灯镇流器)可能会使现场空白或引入任意错误。对于正常使用,设备仅需要加速度计来准确确定哪个方向下降,因此屏幕翻转是准确的。这只需要在设备不移动时工作,陀螺仪没有任何作用。如果您的手机或平板电脑带有仅用于此目的的传感器 - 因此没有陀螺仪或不准确的陀螺仪 - 您会看到设备限制。不稳定的值是您的设备质量低劣和/或您处于地球磁场扭曲位置的其他证据。在外面和外面的另一个(最好是昂贵的)设备上尝试该程序,看看你得到了什么。

答案 3 :(得分:2)

在事件块中通常要做的事情就是什么都不做,因为这真的很快。 “几乎”是重要的词。在您的情况下,事件可以只是将事件的数据(从事件参数)添加到某些数据结构(列表,堆栈,循环缓冲区......您的选择)。这样你就会失去更少的事件(如果有的话)。

这意味着您可以(例如定期)读取存储的事件并确定是否制作了手势。这意味着您不必经常进行密集计算。但你不会丢失任何事件。我认为这是可以接受的,因为你的目的是手势识别。我认为它不必那么快(即,每次传感器更新时你都不必计算它。)

注意:这是在Linux世界中处理IT的一种常用方法。

答案 4 :(得分:0)

只是一个想法。当我需要收集几个大样本量和执行计算时,我遇到了类似的问题。我的情况可能与你的情况完全不同,因为我只需要加速。我做的是创建一个数组列表。报告的每条记录的计算加速度:

  @Override
        public void onSensorChanged(SensorEvent event) {
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];

            float acceleration = FloatMath.sqrt((x * x) + (y * y) + (z * z));

然后在相同的onSensorChanged方法中,我等到大小达到某个限制,如300,将该样本克隆到新列表,清除原始,在新列表上执行计算并以此方式继续。我得到秒的结果。我不确定你的应用程序允许多少停机时间,但是当我运行它时,我会在不到5秒内得到我想要的东西。如果您需要更多示例代码,请告诉我,但这就是要点。对不起,如果我没有正确理解您的问题,但我认为您要求一种方法来计算数据而不会损失太多?另外,当我注册监听器时,我在一个单独的处理程序上运行,不干扰主线程,不影响用户体验。

答案 5 :(得分:-1)

  
      
  1. 更改变量声明:
  2.   
List<float[][]> array = Collections.synchronizedList(new ArrayList<float[][]>());
  
      
  1. 在runnable内:
  2.   
Iterator<float[][]> values = array.iterator();
while (values.hasNext()) {
        float[][] result = values.next();
        //calculating.

        //after calculating remove the items.
        values.remove();
}

答案 6 :(得分:-1)

这是你的代码出了什么问题。尽可能快地需要快速编码技术。保存传感器类型而不是两次评估。

@Override
    public void onSensorChanged(SensorEvent event) {
        int i = event.sensor.getType();
        if (i == Sensor.TYPE_ACCELEROMETER)
            aValues = (event.values.clone());
        else if (i == Sensor.TYPE_MAGNETIC_FIELD)
            mValues = (event.values.clone());
    }