Далеко не пятничный вопрос про алгоритм

Я с форума gamedev.ru сюда недавно пришёл - и почти в каждой игре есть реализация этого алгоритма чтобы считать FPS (frames per second - кадры в секунду) - почти любой разработчик интересуется этим параметром.
Но мгновенное значение между предыдущим и текущим кадром часто скачет как заяц промеж капустных полей от залпов ружей охотников - и 60 раз в секунду сливается в неразличимую кашу наложения кадров с 55, 57, 58 и 60. Поэтому усреднение - единственный выход.
Оптимизацией в виде кольцевого буфера даже не заморачиваются обычно, т.к. на гигагерцовых ЦПУ на то чтобы достаточно точно определить усредненные кадры какая то там возня с массивом на 20 даже ячеек не повлияет.
Но на микроконтроллерах - да, это может быть разумно.

  1. АЦП это в любом случае некие “попугаи”, зачем что-то делить и к чему-то непонятному приводить?
  2. поэтому просто делайте буфер по тому, сколько времени нужно усреднять и…
  3. потом берите сумму этого буфера и используйте где там вам нужны были эти АЦП и подобное. Делить и сдвигать не нужно.

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

В 99% случаев “сглаживания” данных с датчиков достаточно рекурсивного фильтра первого порядка:

vFiltred = 0;
for (;;) 
{
  vFiltred = (pKoeff * vFiltred + analogRead(nSensor)) / (pKoeff + 1);
  Serial.println(vFiltred);
}

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

Тут ведь важно для чего фильтруем? Скользящее Среднее это тоже рекурсивный фильтр, если “по гамбургскому счету”. Какие его особенности стоят того, чотбы еще и место под буфер держать. Что конкретно в передаточной функции у СС лучше, чем у РФ 1 порядка?

“А если нет разницы, зачем платить больше?” (с)

АПДЕЙТ. Конечно лучше, а иногда необходимо, считать с плавающей точкой.

1 лайк

Не иногда. Если использовать целые, то даже если имеем постоянное значение с датчика то до него этим методом в целых никогда не достигнем. Так что только float, только нардкор.

1 лайк

но об этом история умалчивает, но коли у него моросит не по децки и выборка раз в секунду ясно что для управления чего либо это не годится, от слова совсем, помедитировать на эти данные разве что

Меня устраивает алгоритм арифметического скользящего среднего. Всем спасибо за участие. Особенно @ЕвгенийП (отдельное спасибо за предоставленный код), @Arduman как первому ответившему и по существу, @DetSimen за подсказку с быстродействием, всем остальным тоже спасибо за свой вклад.
Вопросы, скорее всего возникнут, но пока я еще в железе это не воплотил (а то что на макете было - хрень полная, от касания к проводу так всё «плясало», что ну его нафик).

Хорошо конечно что устраивает, но Влад предложил действительно хорошее решение. Всего одна строка кода и никаких массивов.

Я не могу спорить с этим, так как всё равно не понимаю (полностью) что в этих строках происходит. Вам с ним, скорее всего, это как «прямую линию через две точки провести», а мне не понятно и просить обьяснить не вижу смысла (ввиду особенностей наших с ним взаимоотношений на форуме, и методов его объяснения в частности).

А вот и не надо лишний раз.

vFiltred = (pKoeff * vFiltred + analogRead(nSensor)) / (pKoeff + 1);

Если analogRead(nSensor) будет равно vFiltred результат не изменится:
pKoeff * vFiltred + analogRead(nSensor) / (pKoeff + 1) = (pKoeff * vFiltred + vFiltred ) / (pKoeff + 1) = vFiltred * (pKoeff + 1) / (pKoeff + 1) = vFiltred

Если же значение analogRead(nSensor) изменится в ту или иную сторону, то влияние его на результирующее vFiltred будет тем слабее, чем больше коэффициент. Но и реакция фильтра будет медленнее.

Всё просто. Почти тоже самое что и с массивом. Но организовано по другому. Берёшь последнее значение после фильтра и множишь его коэффициент К, который собственно является глубиной усреднения, прибавляешь значение датчика и делишь полученную сумму на К+1. Тем самым ты точно также усредняешь текущее и все(!) предыдущие значения. Однако вес в усреднении более поздних значений оказывается выше. Например если взять глубину 9, то получиться что к предыдущему значению усреднения ты добавишь 1/10 (K+1) от текущего значения датчика.

Фильтры долго выходят на значение по ср с плавающим средним. Тут буфер весь обновился - и мы четко и сразу на новом уровне.

От задачи. Как шумодав фильтр гораздо лучше.

Может быть в таком случае надо фиксировать еще и временной момент каждого замера? И учитывать его при вычислении?

Почему бы и нет? Может нужно при замерах представлять график, по Ox время замера, по Oy - амплитуда. Соединять эти точки и находить площадь под графиком.

Не знаю, что Вы понимаете под “учитывать”. Можно, конечно, изобретать велосипед и что-то колхозить, но в цифровой фильтрации всё это уже сделано до нас. Надо просто посчитать для данного конкретного фильтра его фазочастотную характеристику (ФЧХ). Погуглите.

Попытался найти старую тему про усреднение, ввёл в поиске старого форума слово усреднение. Выпало много тем. Пару часов читал с удовольствием. Старых участников, которых , к сожалению, не видно.

1 лайк

Logik там был?

А как же. Без его логики мало где обходилось.

Не надо ничего фиксировать.
Если вы подумаете - поймёте что положение значения в буфере это и есть момент замера.

поздравляю, вы придумали взвешенное среднее

А я вот без сарказма не удивлен. Я уже столько всего «придумал», а потом оказывалось, что «уже придумано до меня» :smiley: