3

我正在检测设备的方向以检查它是否倒置在表面上,然后在其上调用方法。我遇到的问题是,用户可能会无意中将设备的方向转向该位置一瞬间并返回一个面朝上的位置以纠正他们的错误,但是在他们第一次这样做时仍会调用该方法。

我正在尝试以 800 毫秒的延迟向方向添加“双重检查”,以便用户有时间在触发该方法之前更正方向。不幸的是,我无法控制onSensorChanged调用频率以添加延迟为 800 毫秒的线程,这将仔细检查方向以防止无意的方向更改。

我的代码如下,

public SensorEventListener accelerometerListener = new SensorEventListener(){

    @Override
    public void onSensorChanged(SensorEvent event) {

            float z_value = event.values[2];

                if (z_value < 0) {
                    //the device is now upside-down

                       try {

                           Thread.sleep(800);

                           //this if statement is never called correctly because
                           // onSensorChanged is not called again.
                                if (z_value < 0) {
                                    toggleMethod();
                                }

                        } catch (InterruptedException e) {
                               e.printStackTrace();
                        }
                 }
          }

我的问题:有没有一种方法可以调用onSensorChangedonSensorChanged 方法本身的延迟以进行双重检查?

4

3 回答 3

2

使用 onSensorCheck 时最好的做法是使用过滤器:您始终根据传感器给出的最后 n 个值的平均值做出决定,这样您的应用程序将运行平稳并且传感器的突然变化不会影响它。
这是您的操作方法:

z_value = 0.2*event.values[2]+0.8*z_value; // 0.2+0.8 = 1 

这样你只取了新值的 20%,其余的是最后四个值的平均值。

尝试使用 coefs 直到您对结果感到满意。

对我来说,这工作得很好:

 z_value = 0.02*event.values[2]+0.98*z_value;

---------------------------------------------------------------

编辑

下面是如何初始化 z_value : z_value 应该是一个字段,您必须添加另一个仅用于初始化的布尔字段,即用于第一个 onSensorChanged 调用。所以你有两个领域:

double z_value =0;
boolean firstTime = true;

这是 onSensorChanged 方法:

@Override
public void onSensorChanged(SensorEvent event) {
        if(firstTime){
            z_value = event.values[2]; // you will enter here only the first time
            firstTime = false
        }
        else{
            z_value = 0.02*event.values[2] + 0.98*z_value;
        }

        if (z_value < 0) {
                //the device is now upside-down

               // do what you gotta do
        }
 }
于 2013-09-20T21:31:57.607 回答
2

一个非常简单的方法是让传感器有机会在接下来的 n 毫秒内(正如所要求的那样)给出一个新值,如下所示:

1-决定你想给用户多少毫秒以撤消他的操作(你使用了 800):

private static final long TIME_MARGIN_FOR_USER_TO_UNDO = 800;

2-创建一个处理程序和一个可运行对象来执行您的操作(让它们的范围比您的 onSensorChanged 更大 - 例如您的活动):

Handler sensorHandler = new Handler();
Runnable toggleRunnable = new Runnable() {
   public void run() {
      toggleMethod();
   }
}

3-只要您的 if 语句评估为真,就发布此可运行文件;但是,在 n 毫秒后发布它。

@Override
public void onSensorChanged(SensorEvent event) {
   float z_value = event.values[2];
   if (z_value < 0) {
      sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
   }
}

4-由于传感器值更改时将调用 onSensorChanged,因此如果用户修复了它,您可以停止运行可运行对象。因此,您将需要一个删除可运行文件的 else 语句。

@Override
public void onSensorChanged(SensorEvent event) {
   float z_value = event.values[2];
   if (z_value < 0) {
      sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
   }
   else {
      sensorHandler.removeCallbacks(toggleRunnable);
   }
}
于 2013-09-23T22:46:03.343 回答
0

另一种方法是随着时间的推移平滑输入,因此取 800 毫秒输入的平均读数。

(YAT 的回应有正确的想法,但执行实际上并没有考虑随着时间的推移交付的样本量,这将是未知的,从一部手机到另一部手机。它也永远不会忘记任何测量,10 秒后的行为可能是1分钟后明显不同。)

//the idea here is to store all the inputs over the last 800ms and average them
LinkedList<Entry<long, double>> inputs = new LinkedList<Entry<long,double>>
double totalZ = 0.0f;   //keep a running total so we don't have to iterate through the list to calculate the average

@Override
public void onSensorChanged(SensorEvent event) {
    //clear out values older than 800ms
    while(inputs.size() > 0 && inputs.getLast().getKey() < System.currentTimeInMillis() - 800){
        Entry<long, double> deletedEntry = inputs.removeLast();
        totalZ -= deletedEntry.getValue(); //update the running total
    }

    //add the new measurement to the head
    inputs.addFirst(new Entry<long, double>(System.currentTimeInMillis(),event.values[2]));
    totalZ += event.values[2]; //update the running total with the new measurement

    //this is the Z averaged over 800ms
    float averagedZ = totalZ / inputs.size();
}

或者更简单的方法,更符合所提出的问题,在检测到初始变化时。

节省时间,保存新的势态

然后当你得到另一个 onSensorChanged()

该状态与潜在状态相同吗?如果不是,则清除时间并清除电位状态

如果 800ms 已经过去并且潜在状态仍然是当前状态,则启动该更改。

在大多数精度模式下,onSensorChanged 每秒调用多次,因此您应该接近 800 毫秒,只是等待正常的 onSensorChanged 调用发生。

下面粗略的伪代码

long lastChange = 0;
int potentialState = 0;

@Override
public void onSensorChanged(SensorEvent event) {
    int currentState = determineCurrentState(event); //whatever your logic is and states

    if(potentialState != currentState){
         lastChange = System.currentTimeInMillis();
         potentialState = currentState;
    }
    else if(lastChange + 800 < System.currentTimeInMillis()){
         //execute state change, 800ms have elapses with no further change in state
         stateChange(currentState);
    }
于 2013-09-23T15:51:16.290 回答