(请在最后阅读更新3)我正在开发一款不断与设备传感器配合使用的应用程序,可与Accelerometer
和Magnetic
传感器配合使用以检索设备的方向(目的)提到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。一世 已经提到将会发生什么,我需要检测一个特定的 运动手势。所以也许是我选择的整个方式( 上面的技术)不是解决这个问题的好方法。也许 最好通过使用其他传感器或使用。来检测该手势 其他方面的传感器相同。你觉得怎么样?
答案 0 :(得分:7)
因此,您似乎正在尝试为标准的“生产者 - 消费者”问题找到高吞吐量低延迟解决方案。基本上这个想法很简单:减少数据处理开销,并行处理数据。建议如下:
<强> 1。使用“低延迟”库
任何这些解决方案都可以节省大量的CPU周期。
<强> 2。明智地处理数据
每次提交作业都会产生开销。批处理可能非常有用。
持续处理数据。请注意,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)
- 更改变量声明:
醇>
List<float[][]> array = Collections.synchronizedList(new ArrayList<float[][]>());
- 在runnable内:
醇>
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());
}