Сглаживание данных с датчика

У меня есть 3D-датчик, который измеряет данные v (x, y, z). Я использую только данные x и y. Сглаживания только x и y было бы достаточно.

Если я использую журнал для отображения данных, он показывает мне что-то вроде этого: (время) 0,1 ... (журнал данных) x = 1,1234566667 (время) 0,2 ... (журнал данных) x = 1,1245655666 (время) 0,3 . .. (журнал данных) х = 1,2344445555

Что ж, на самом деле данные более точны, но я хочу сгладить значение между значением 1,1234 и значением 1,2344, потому что для меня это одно и то же, я могу использовать целые числа, показывая только «x = 1», но мне также нужны десятичные дроби, тогда , мне нужно показать здесь своего рода «сглаженное» значение.

У кого-нибудь есть идеи? Я программирую на С#, но не все функции работают, поэтому мне нужно создать свою собственную функцию.


person Mworks    schedule 06.01.2011    source источник


Ответы (5)


Самый простой — сделать скользящее среднее ваших данных. То есть хранить массив показаний датчиков и усреднять их. Что-то вроде этого (псевдокод):

  data_X = [0,0,0,0,0];

  function read_X () {
      data_X.delete_first_element();
      data_X.push(get_sensor_data_X());
      return average(data_X);
   }

При этом есть компромисс. Чем больше массив, который вы используете, тем более гладким будет результат, но тем больше задержка между результатом и фактическим значением. Например:

                           /\_/\
                        /\/     \_/\
  Sensor reading:  __/\/            \/\
                                       \/\  _/\___________
                                          \/
                              _
                           __/ \_
                       ___/      \__
  Small array:     ___/             \_/\_       _
                                         \   __/ \________
                                          \_/

                                 ____
                              __/    \__
                           __/           \__
  Large array:     _______/                 \__      __
                                               \_   /  \__
                                                 \_/


(forgive my ASCII-ART but I'm hoping it's good enough for illustration).

Если вам нужен быстрый отклик, но в любом случае хорошее сглаживание, вам следует использовать средневзвешенное значение массива. Это в основном цифровая обработка сигналов (с большой буквы DSP), которая, вопреки своему названию, более тесно связана с аналоговым дизайном. Вот короткая статья в Википедии об этом (с хорошими внешними ссылками, которые вы должны прочитать, если хотите пойти по этому пути): http://en.wikipedia.org/wiki/Digital_filter

Вот некоторый код из SO о фильтре нижних частот, который может удовлетворить ваши потребности: Программное обеспечение фильтра нижних частот?. Обратите внимание, что в коде этого ответа он использует массив размером 4 (или 4-го порядка в терминологии обработки сигналов, поскольку такие фильтры называются фильтрами четвертого порядка, на самом деле их можно смоделировать полиномиальным уравнением 4-го порядка: ax^ 4 + bx^3 + cx^2 + dx).

person slebetman    schedule 06.01.2011
comment
Большое Вам спасибо!!! Мне это очень помогло, я прямо сейчас реализую код, отныне буду помогать таким же людям, как и вы, ну если смогу конечно. Такс!! - person Mworks; 07.01.2011
comment
Фильтр скользящего среднего значения — это частный случай фильтра нижних частот, который является довольно неудобным фильтром (с точки зрения производительности). Фильтр нижних частот первого порядка часто (обычно?) лучше, чем скользящие средние, с точки зрения частотной характеристики, вычислительной нагрузки и сложности программы. Для многих приложений вы можете игнорировать эти детали, например, дисплей компаса, который может медленно реагировать, было бы здорово использовать скользящее среднее значение. Если у вас есть игра, в которой вам нужен быстрый отклик с использованием шумных датчиков, скользящее среднее будет плохим решением из-за задержки, которую оно вызывает при заданной степени фильтрации. - person Hucker; 05.10.2011
comment
Отличный ответ, сделанный потрясающим благодаря искусству ASCII - person Spaceghost; 29.01.2012
comment
Другой вопрос, связанный с этим, вот код Java + Android, который я создал на основе вашего псевдокода: stackoverflow.com/a/24600534 /663058 - person CenterOrbit; 07.07.2014

Итак, я пришел сюда, чтобы решить ту же проблему (сглаживание сенсорного ввода в Android), и вот что я придумал:

/*
 * time smoothing constant for low-pass filter
 * 0 ≤ α ≤ 1 ; a smaller value basically means more smoothing
 * See: http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
 */
static final float ALPHA = 0.2f;

protected float[] accelVals;

public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
        accelVals = lowPass( event.values, accelVals );

    // use smoothed accelVals here; see this link for a simple compass example:
    // http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
}

/**
 * @see http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
 * @see http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
 */
protected float[] lowPass( float[] input, float[] output ) {
    if ( output == null ) return input;

    for ( int i=0; i<input.length; i++ ) {
        output[i] = output[i] + ALPHA * (input[i] - output[i]);
    }
    return output;
}

Спасибо @slebetman за то, что указали мне на ссылку в Википедии, которая после небольшого чтения привела меня к алгоритму в статье о фильтре нижних частот в Википедии. Я не буду клясться, что у меня есть лучший алгоритм (или даже правильный!), но неофициальные данные, похоже, указывают на то, что он делает свое дело.

person thom_nic    schedule 25.04.2011
comment
просто хотел сказать, что если вы используете этот код на Android, сначала верните input.copy() вместо самого массива. Мой датчик пишет в один и тот же массив, т.е. вход и выход - это один и тот же массив, и сглаживание работать не будет. - person Robert Ende; 12.10.2014

Ну, есть много способов сгладить данные датчика, зависит от того, какой это датчик и какая аналогия подойдет. Я использовал эти алгоритмы в своих проектах:

  1. Фильтр высоких частот [HPF] и фильтры нижних частот [LPF] - как указано в выбранном ответе.
  2. Алгоритм скользящего среднего -MAA
  3. Алгоритм Гаэли [улучшенная версия для MAA]
  4. Быстрое преобразование Фурье-БПФ

Код:

HPF-фильтр высоких частот

private float[] highPass(float x, float y, float z) {
    float[] filteredValues = new float[3];
    gravity[0] = ALPHA * gravity[0] + (1 – ALPHA) * x;
    gravity[1] = ALPHA * gravity[1] + (1 – ALPHA) * y;
    gravity[2] = ALPHA * gravity[2] + (1 – ALPHA) * z;
    filteredValues[0] = x – gravity[0];
    filteredValues[1] = y – gravity[1];
    filteredValues[2] = z – gravity[2];
    return filteredValues;   
    }

ФНЧ-фильтр нижних частот

private float[] lowPass(float x, float y, float z) {
    float[] filteredValues = new float[3];
    filteredValues[0] = x * a + filteredValues[0] * (1.0f – a);
    filteredValues[1] = y * a + filteredValues[1] * (1.0f – a);
    filteredValues[2] = z * a + filteredValues[2] * (1.0f – a);
    return filteredValues;
    }

MAA-скользящая средняя

     private final int SMOOTH_FACTOR_MAA = 2;//increase for better results   but hits cpu bad

     public ArrayList<Float> processWithMovingAverageGravity(ArrayList<Float> list, ArrayList<Float> gList) {
            int listSize = list.size();//input list
            int iterations = listSize / SMOOTH_FACTOR_MAA;
            if (!AppUtility.isNullOrEmpty(gList)) {
                gList.clear();
            }
            for (int i = 0, node = 0; i < iterations; i++) {
                float num = 0;
                for (int k = node; k < node + SMOOTH_FACTOR_MAA; k++) {
                    num = num + list.get(k);
                }
                node = node + SMOOTH_FACTOR_MAA;
                num = num / SMOOTH_FACTOR_MAA;
                gList.add(num);//out put list
            }
            return gList;
        }
person Jayant Arora    schedule 29.04.2016
comment
Что такое алгоритм Гейли? - person Christopher Oezbek; 28.08.2018
comment
@Jayant для данных датчика ускорения, должны ли мы использовать фильтр верхних частот или фильтр нижних частот? - person RainCast; 10.07.2020
comment
После некоторых чтений я выбрал фильтр нижних частот для сглаживания всплесков данных датчика ускорения. - person RainCast; 10.07.2020
comment
Кстати, это очень хороший ответ, он заслуживает большего количества голосов. - person RainCast; 10.07.2020

Вот пример, основанный на логике раздела MotionEvents Руководство по обработке событий для iOS.

float ALPHA = 0.1;

protected float[] lowPass( float[] input, float[] output ) {
    if ( output == null ) return input;

    for ( int i=0; i<input.length; i++ ) {
        output[i] = (input[i] * ALPHA) + (ouptut[i] * (1.0 - ALPHA));
    }
    return output;
}
person rockfakie    schedule 18.12.2012
comment
Содержимое URL-адреса руководства изменено, не удалось найти раздел MotionEvents, можете ли вы его найти? - person RainCast; 10.07.2020

Здесь я поднимаю старый вопрос, но если вы находитесь в стране .NET, вы можете использовать RX, чтобы сделать это за вас.

Например, использование RX в сочетании с WebClient.DownloadFileAsync для расчета «сглаженной» скорости загрузки:

double interval = 2.0; // 2 seconds
long bytesReceivedSplit = 0;

WebClient wc = new WebClient();
var downloadProgress = Observable.FromEventPattern<
    DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
    h => wc.DownloadProgressChanged += h,
    h => wc.DownloadProgressChanged -= h)
    .Select(x => x.EventArgs);

downloadProgress.Sample(TimeSpan.FromSeconds(interval)).Subscribe(x =>
    {
        Console.WriteLine((x.BytesReceived - bytesReceivedSplit) / interval);
        bytesReceivedSplit = x.BytesReceived;
    });

Uri source = new Uri("http://someaddress.com/somefile.zip");
wc.DownloadFileAsync(source, @"C:\temp\somefile.zip");

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

person CatBusStop    schedule 22.05.2012