Как определить увеличение движения и внезапную остановку с помощью акселерометра устройства?

Я пытаюсь обнаружить движение, которое вы получите от столкновения двух телефонов друг с другом.

Мой вопрос, является ли акселерометр правильным датчиком для этого?

Если так, то как бы я это реализовал?

В противном случае, какой датчик я должен использовать и каким образом я должен его использовать?

Согласно руководству на https://developer.android.com/guide/topics/sensors/sensors_overview, TYPE_LINEAR_ACCELERATION кажется правильным, но я не могу понять, как его использовать.

1 ответ

Решение

Вот как ты мог это сделать.

1- Инициализируйте объект датчика и зарегистрируйтесь для обратного вызова событий обновления датчика, как это

    private void initSensorObject() {
       SensorManager sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
        Sensor _Sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorMgr.registerListener(sensorEventListener, _Sensor,
                SensorManager.SENSOR_DELAY_FASTEST);
    }

2- Обработайте обратный вызов датчика следующим образом, который обнаруживает линейное ускорение, исключая эффект гравитации, а затем резкую остановку в движении (я написал это для двух движений касания, как нажатие, затем остановка, снова нажатие, затем снова остановка. Объясните весь процесс для лучшего понимания, вы можете легко изменить его только для обнаружения одного касания.) Я добавил комментарии в коде, чтобы сделать его самоочевидным.

    /*
 * Following are the parameters for Tap detection Algorithm
 */
private static float SPEED_THRESHOLD_RISE1;
private static float SPEED_THRESHOLD_DROP1;
private static float SPEED_THRESHOLD_RISE2;
private static float SPEED_THRESHOLD_DROP2;

private static int DROP_DELTA;
private static int RISE2_DELTA;
private int SENSITIVITY_INDEX = TapParam.SEN_DEFAULT;

private static final int TAP_STATE_RISE1 = 0;
private static final int TAP_STATE_DROP1 = 1;
private static final int TAP_STATE_RISE2 = 2;
private static final int TAP_STATE_DROP2 = 3;

private int tappingState = TAP_STATE_RISE1;
private boolean tapLastStateOnce = false;

private long lastSensorUpdate;
private long tap1DroppedAt = 0;
private int mathMeanIndex = 0;
private float[] lastLinearAcc = new float[3];
private float[] acceleSet = new float[TapParam.AM_SIZE];
private int acceleIndex = 0;
private float[] gravity = new float[3];
private float lastAccele = -99; // an arbitrarily very small value


    /**
 * onSensorChanged is called when the Motion Sensor value
 * is changed and then run the algorithm to detect your desired motion.
 *
 * @return void
 */
private SensorEventListener sensorEventListener = new SensorEventListener() {

    @Override
    public void onSensorChanged(SensorEvent event) {

        long curSensorTime = System.currentTimeMillis();
        if ((curSensorTime - lastSensorUpdate) < TapParam.SENSOR_RE_READ_TIME)
            return;
        lastSensorUpdate = curSensorTime;

        acceleSet[acceleIndex] = getMotionAcceleration(event, curSensorTime);
        acceleIndex = (acceleIndex + 1) % TapParam.AM_SIZE;
        if (mathMeanIndex < TapParam.AM_SIZE)
            mathMeanIndex++;
        float accele = Util.getArithmeticMean(acceleSet);

        switch (tappingState) {
            case TAP_STATE_RISE1:
                if (accele > SPEED_THRESHOLD_RISE1) {
                    tappingState = TAP_STATE_DROP1;
                    resetTapStateDropping();
                    mathMeanIndex = 0;
                }
                break;
            case TAP_STATE_DROP1:
                if (accele <= SPEED_THRESHOLD_DROP1) {
                    tappingState = TAP_STATE_RISE2;
                    resetTapStateRise2();
                    tap1DroppedAt = curSensorTime;
                    mathMeanIndex = 0;
                }
                break;
            case TAP_STATE_RISE2:
                if (curSensorTime - tap1DroppedAt >= TapParam.DELAY_BETWEEN_TAPS) {
                    if (accele > SPEED_THRESHOLD_RISE2) {
                        tappingState = TAP_STATE_DROP2;
                        resetTapStateDropping();
                        mathMeanIndex = 0;
                    }
                }
                break;
            case TAP_STATE_DROP2:
                if ((!tapLastStateOnce) && (accele <= SPEED_THRESHOLD_DROP2)) {
                    tapLastStateOnce = true;
                    resetTapStateRise2();
                    mathMeanIndex = 0;

                    onTapTapDetected();
                }
                break;

            default:
                tappingState = TAP_STATE_RISE1;
                break;
        }
    }

    /**
     * onAccuracyChanged inter shall be called when hardware IMU
     * (Inertial Measurement Unit a.k.a Motion Sensor) of the device change
     * its accuracy value.
     *
     * @return void
     */
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        setThresholdValues();

    }
};
    /**
 * It shall return the Linear Acceleration of the device. The force of
 * gravity shall be filtered out.
 *
 * @return float - Linear acceleration
 */
private float getMotionAcceleration(SensorEvent event, long curSensorTime) {

    // In this code, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    final float alpha = 0.8f;
    float[] linearAcc = new float[3];

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

    // Remove the gravity contribution with the high-pass filter.
    linearAcc[0] = event.values[0] - gravity[0];
    linearAcc[1] = event.values[1] - gravity[1];
    linearAcc[2] = event.values[2] - gravity[2];

    float accele = (Math.abs(lastLinearAcc[0] - linearAcc[0])
            + Math.abs(lastLinearAcc[1] - linearAcc[1]) + Math
            .abs(lastLinearAcc[2] - linearAcc[2])) / 3;
    lastLinearAcc = linearAcc;

    return accele;
}

   /**
 * resetTapStateRise2 shall reset the tapping state if
 * second Tap is not detected within TAP_RISE2_TIME time.
 *
 * @return void
 */
private void resetTapStateRise2() {
    handleResetTapState.removeCallbacks(runResetTapState);
    handleResetTapState.postDelayed(runResetTapState, RISE2_DELTA);
}
    private Handler handleResetTapState = new Handler();
private Runnable runResetTapState = new Runnable() {

    @Override
    public void run() {
        tappingState = TAP_STATE_RISE1;
        tapLastStateOnce = false;
    }
};

    /**
 * resetTapStateDropping shall reset the tapping state if
 * Tap Drop is not detected within TAP_DROP_TIME time.
 *
 * @return void
 */
private void resetTapStateDropping() {
    handleResetTapState.removeCallbacks(runResetTapState);
    handleResetTapState.postDelayed(runResetTapState, DROP_DELTA);
}
    private Handler handleResetTapState = new Handler();
private Runnable runResetTapState = new Runnable() {

    @Override
    public void run() {
        tappingState = TAP_STATE_RISE1;
        tapLastStateOnce = false;
    }
};

3- Вот файл рабочих параметров, который поможет вам начать работу. thresholds Массив определяет 10 уровней чувствительности того, насколько сильно или мягко вы хотите коснуться телефона, чтобы быть обнаруженным как допустимое движение

TapParam.java

final class TapParam {

static final int SEN_DEFAULT = 4;
static final int SEN_MIN = 0;
static final int SEN_MAX = 9;
static final int DELAY_BETWEEN_TAPS = 75;
static final int SENSOR_RE_READ_TIME = 1;
static final int AM_SIZE = 5;

// Columns: A   B   Y   D   T1  T2  T3
private static final double[][] thresholds = new double[][]{
        {0.8483763, 0.33935052, 0.5655842, 0.33935052, 175, 300, 175},
        {0.95167595, 0.38067037, 0.6344506, 0.38067037, 175, 300, 175},
        {1.0836192, 0.4334477, 0.7224128, 0.4334477, 175, 300, 175},
        {1.8552876, 0.742115, 1.2368584, 0.742115, 175, 300, 175},
        {2.4327612, 0.9731045, 1.6218408, 0.9731045, 175, 300, 175},
        {3.5321822, 1.4128729, 2.354788, 1.4128729, 175, 300, 175},
        {6.4446864, 2.5778747, 4.296458, 2.5778747, 175, 300, 175},
        {8.2, 3.5, 5.4, 2.6, 175, 300, 175},
        {9.8, 4.0, 6.0, 2.9, 175, 300, 175},
        {12, 6.0, 8.0, 3.1, 175, 300, 175}
};

private static int indexLimiting(int index) {
    return (index > SEN_MAX) ? SEN_MAX : (index < SEN_MIN) ? SEN_MIN : index;
}

static float getRISE1(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][0];
}

static float getDROP1(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][1];
}

static float getRISE2(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][2];
}

static float getDROP2(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][3];
}

static float getDROP_DELTA1(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][4];
}

static float getRISE_DELTA2(int index) {
    index = indexLimiting(index);
    return (float) thresholds[index][5];
}
}

ОБНОВИТЬ:

/**
 * setThresholdValues method shall calculate the Threshold values according
 * to the accuracy value of the Motion Sensor.
 */
private void setThresholdValues() {

    if (_Sensor == null)
        return;

    SPEED_THRESHOLD_RISE1 = TapParam.getRISE1(SENSITIVITY_INDEX);
    SPEED_THRESHOLD_DROP1 = TapParam.getDROP1(SENSITIVITY_INDEX);
    SPEED_THRESHOLD_RISE2 = TapParam.getRISE2(SENSITIVITY_INDEX);
    SPEED_THRESHOLD_DROP2 = TapParam.getDROP2(SENSITIVITY_INDEX);
}

/**
 * Method shall return the average (Arithmetic Mean) of the set of values
 * passed as parameter.
 *
 * @param float[] set - the set of values
 * @return float - arithmetic mean
 */
static float getArithmeticMean(float[] set) {
    double sum = 0;
    for (float aSet : set) {
        sum += aSet;
    }
    return (float) sum / set.length;
}

ОБНОВЛЕНИЕ 2:

Вызов setTapTapSensitivity() в onCreate() вашей деятельности

    private void setTapTapSensitivity() {
    setTapTapSensitivity(3); //You can try 0 to 9 for 10 levels of sensitivity defined in TapParam.java. I have tried 3 and it works for a moderate tap
}

private void setTapTapSensitivity(int sensitivityIndex) {
    RISE2_DELTA = (int) TapParam.getRISE_DELTA2(sensitivityIndex);
    DROP_DELTA = (int) TapParam.getDROP_DELTA1(sensitivityIndex);
    SENSITIVITY_INDEX = sensitivityIndex;
}
Другие вопросы по тегам