11

(请阅读最后的更新 3)我正在开发一个应用程序,该应用程序可以持续与设备的传感器一起工作,Accelerometer与传感器一起工作以检索设备的方向(此处Magnetic提到了目的)。换句话说,我的应用程序需要实时知道设备的方向(但是这是不可能的,所以要尽可能快,但要尽可能快)。正如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:我想也许我们可以用另一种方式解决问题!我对这段代码有明确的目的。请看看这个。我已经提到会发生什么,我需要检测一个特定的运动手势。所以也许我选择的整个方式(上面的技术)不是解决这个问题的好方法。也许最好通过使用其他传感器或以其他方式使用相同的传感器来检测该手势。你怎么看?

4

7 回答 7

7

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

1.使用“低延迟”库

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

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

在此处输入图像描述

2. 明智地处理数据

每次提交作业时都会产生开销。批处理真的很有帮助。

在此处输入图像描述

连续处理数据。注意,executor.execute会消耗不少。一些长寿的消费者可能会有所帮助。

3. 最后,使用微优化技术

例如,摆脱有if-else-if利于switch.

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

快乐编码。

于 2013-09-18T00:36:28.783 回答
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]));
    }
}
于 2013-09-18T15:00:27.257 回答
2

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

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

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

于 2013-09-22T02:39:36.200 回答
2

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

于 2013-09-22T01:01:33.070 回答
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 秒的时间内得到我想要的东西。如果您需要更多示例代码,请告诉我,但这就是要点。抱歉,如果我没有正确理解您的问题,但我认为您是在寻求一种在不损失太多数据的情况下计算数据的方法?另外,当我注册侦听器时,我在单独的处理程序上运行它,不干扰主线程,不影响用户体验。

于 2016-05-24T10:44:24.280 回答
-1
  1. 更改变量声明:
List<float[][]> array = Collections.synchronizedList(new ArrayList<float[][]>());
  1. 在可运行文件内部:
Iterator<float[][]> values = array.iterator();
while (values.hasNext()) {
        float[][] result = values.next();
        //calculating.

        //after calculating remove the items.
        values.remove();
}
于 2013-09-15T17:30:00.533 回答
-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());
    }
于 2013-09-20T19:41:58.353 回答