Что случилось с моей методикой мониторинга сенсора?

(пожалуйста, прочитайте ОБРАЗЕЦ 3 в конце). Я разрабатываю приложение, которое постоянно работает с датчиками устройства, работает с датчиками Accelerometer и Magnetic для извлечения ориентации устройства (указанная цель здесь). другими словами, моему приложению необходимо знать ориентацию устройства в режиме реального времени (однако это никогда не возможно, как можно быстрее, но действительно так быстро, как возможно!). как упоминалось в профессиональной разработке приложений Android 4 от Reto Meier:

Акселерометры могут обновлять сотни раз в секунду...

Я не должен потерять данные, которые сообщают датчики, и я также хочу выполнять трудоемкие операции над этими данными (получить ориентацию, а затем выполнить вычисления...). Я решил решить свою проблему, используя 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. я упомянул, что произойдет, мне нужно определить конкретную движение жест. так что, возможно, весь путь, который я выбрал ( техника выше) не является хорошим способом решения этой проблемы. может быть лучше обнаружить этот жест с помощью других датчиков или использовать аналогичные датчики другим способом. что вы думаете?

Ответ 1

Ваш код выглядит разумным. Большое неизвестно, насколько хороши датчики и слияние датчиков в вашем устройстве. Быстрые показания изменения угла зависят от интеграции ускорения angular или физического гироскопа с магнитными данными, смешанными с тем, чтобы результат полностью совпал с землей. Магнитные данные зависят от окружения. Если на вашем устройстве установлены датчики низкого качества или имеются магнитные помехи в вашей среде, вполне возможно увидеть виды ошибок, которые вы видите. Большие металлические конструкции и магнитное оборудование (например, двигатели или даже флуоресцентные световые балласты) могут заполнить поле или ввести произвольные ошибки. Для нормального использования устройству требуется только акселерометр, чтобы точно определить, какой путь вниз, поэтому экраны экрана точны. Это необходимо, только если устройство не движется, где гироскоп не играет никакой роли. Если у вас есть телефон или планшет с датчиками, предназначенными только для этой цели, поэтому без гироскопа или неточности - вы видите ограничение на устройство. Неустойчивые значения являются еще одним доказательством низкого качества вашего устройства и/или того, что вы находитесь в месте, где искажается земное магнитное поле. Попробуйте программу на другом (желательно дорогое) устройстве снаружи и в открытом доступе и посмотрите, что вы получаете.

Ответ 2

Итак, похоже, вы пытаетесь найти решение с высокой пропускной способностью с низкой пропускной способностью для стандартной проблемы "Продюсер-Потребитель". В принципе, идея довольно проста: уменьшить объем обработки данных, обрабатывать данные параллельно. Предложения следующие:

1. Используйте библиотеки с низкой задержкой

  • javolution.org - это библиотека в реальном времени, которая позволяет быстрее и быстрее прогнозировать Java или Java-Like/С++. Он включает поддержку Android.
  • mentaqueue - это супер-быстрая, без мусора, без блокировки, двухпоточная (производитель-потребитель) очередь на основе о идеях Disruptor. Поддержка Android - undefined (похоже, она должна работать).
  • disruptor - еще одна молниеносная библиотека
  • trove - обеспечивает высокоскоростные регулярные и примитивные коллекции для Java.

Любое из этих решений позволит вам сэкономить много циклов процессора.

enter image description here

2. Данные процесса разумно

Каждый раз, когда вы отправляете задание, есть накладные расходы. Пакетная обработка может быть действительно полезной.

enter image description here

Данные процесса непрерывно. Заметьте, executor.execute будет потреблять довольно много. Это может помочь несколько долгоживущих потребителей.

3. Наконец, используйте методы микрооптимизации

Например, избавьтесь от if-else-if в пользу switch.

Отслеживайте производительность все время, чтобы выявить хорошие и плохие решения. Эксперимент.

Счастливое кодирование.

Ответ 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]));
    }
}

Ответ 4

Обычно в блоке событий обычно делается почти ничего, потому что это очень быстро. "Почти" - важное слово. В вашем случае событие может просто добавить данные события (от параметра события) к некоторой структуре данных (список, стек, круговой буфер... ваш выбор). Таким образом, вы должны потерять меньше событий (если они есть).

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

Примечание. Это один из распространенных способов обработки ИТ в мире Linux.

Ответ 5

просто мысль. У меня есть аналогичная проблема, когда мне нужно было собрать несколько больших размеров выборки для выполнения вычислений. Моя ситуация, вероятно, сильно отличалась от вашей, поскольку мне просто нужно ускорение. То, что я сделал, это создать список массивов. рассчитанное ускорение на каждую записанную запись:

  @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 секунд. Если вам нужен еще один образец кода, дайте мне знать, но это суть. Извините, если я не понял ваш вопрос правильно, но я думаю, вы просили способ рассчитать данные, не потеряв много? Кроме того, у меня это работает на отдельном обработчике, когда я регистрирую слушателя, чтобы не мешать основному потоку, а не влиять на работу пользователя.

Ответ 6

  • Изменить объявление переменной:
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();
}

Ответ 7

Это неправильно с вашим кодом. Быстро, насколько это возможно, требуются быстрые методы кодирования. Сохраните тип датчика, а не дважды его оценивайте.

@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());
    }