├── .gitattributes ├── .github └── workflows │ └── tg-send.yml ├── LICENSE ├── README.md ├── README_EN.md ├── examples ├── GFilterRA │ └── GFilterRA.ino ├── GLinear_arrays │ ├── GLinear_arrays.ino │ └── excel.jpg ├── GLinear_running │ └── GLinear_running.ino ├── RingAverage │ └── RingAverage.ino ├── alphabeta_example │ └── alphabeta_example.ino ├── fastFilter │ └── fastFilter.ino ├── filters_comparsion │ └── filters_comparsion.ino ├── kalman_example │ └── kalman_example.ino ├── median3_example │ └── median3_example.ino └── median_example │ └── median_example.ino ├── keywords.txt ├── library.properties └── src ├── GyverFilters.h └── filters ├── FastFilter.h ├── RingAverage.h ├── alfaBeta.h ├── kalman.h ├── linear.h ├── median.h ├── median3.h └── runningAverage.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/tg-send.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Telegram Message 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | build: 8 | name: Send Message 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: send telegram message on push 12 | uses: appleboy/telegram-action@master 13 | with: 14 | to: ${{ secrets.TELEGRAM_TO }} 15 | token: ${{ secrets.TELEGRAM_TOKEN }} 16 | disable_web_page_preview: true 17 | message: | 18 | ${{ github.event.repository.name }} v${{ github.event.release.tag_name }} 19 | ${{ github.event.release.body }} 20 | https://github.com/${{ github.repository }} 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 AlexGyver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![latest](https://img.shields.io/github/v/release/GyverLibs/GyverFilters.svg?color=brightgreen)](https://github.com/GyverLibs/GyverFilters/releases/latest/download/GyverFilters.zip) 2 | [![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/GyverFilters.svg)](https://registry.platformio.org/libraries/gyverlibs/GyverFilters) 3 | [![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/) 4 | [![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/) 5 | [![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/GyverFilters?_x_tr_sl=ru&_x_tr_tl=en) 6 | 7 | [![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](https://t.me/GyverLibs) 8 | 9 | # GyverFilters 10 | GyverFilters - библиотека с некоторыми удобными фильтрами для Arduino 11 | - GFilterRA - компактная альтернатива фильтра экспоненциальное бегущее среднее (Running Average) 12 | - GMedian3 - быстрый медианный фильтр 3-го порядка (отсекает выбросы) 13 | - GMedian - медианный фильтр N-го порядка. Порядок настраивается в GyverFilters.h - MEDIAN_FILTER_SIZE 14 | - GABfilter - альфа-бета фильтр (разновидность Калмана для одномерного случая) 15 | - GKalman - упрощённый Калман для одномерного случая (на мой взгляд лучший из фильтров) 16 | - GLinear - линейная аппроксимация методом наименьших квадратов для двух массивов 17 | - FastFilter - быстрый целочисленный экспоненциальный фильтр 18 | - RingAverage - бегущее среднее с кольцевым буфером (не работает для float!) 19 | 20 | Основано на [уроке по цифровым фильтрам](https://alexgyver.ru/lessons/filters/) 21 | 22 | ### Совместимость 23 | Совместима со всеми Arduino платформами (используются Arduino-функции) 24 | 25 | ### Документация 26 | К библиотеке есть [расширенная документация](https://alexgyver.ru/GyverFilters/) 27 | 28 | ## Содержание 29 | - [Установка](#install) 30 | - [Использование](#usage) 31 | - [Версии](#versions) 32 | - [Баги и обратная связь](#feedback) 33 | 34 | 35 | ## Установка 36 | - Библиотеку можно найти по названию **GyverFilters** и установить через менеджер библиотек в: 37 | - Arduino IDE 38 | - Arduino IDE v2 39 | - PlatformIO 40 | - [Скачать библиотеку](https://github.com/GyverLibs/GyverFilters/archive/refs/heads/main.zip) .zip архивом для ручной установки: 41 | - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64) 42 | - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32) 43 | - Распаковать и положить в *Документы/Arduino/libraries/* 44 | - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив 45 | - Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA) 46 | ### Обновление 47 | - Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи 48 | - Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить" 49 | - Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам! 50 | 51 | 52 | 53 | ## Использование 54 | ```cpp 55 | // =========== ALPHA-BETTA alfaBeta.h =========== 56 | // период дискретизации (измерений), process variation, noise variation 57 | GABfilter(float delta, float sigma_process, float sigma_noise); 58 | 59 | // период дискретизации (измерений), process variation, noise variation 60 | void setParameters(float delta, float sigma_process, float sigma_noise); 61 | 62 | // возвращает фильтрованное значение 63 | float filtered(float value); 64 | 65 | // ========== FAST FILTER FastFilter.h ========== 66 | FastFilter; 67 | FastFilter(uint8_t k = 20); // коэффициент 0-31, dt 0 68 | FastFilter(uint8_t k = 20, uint16_t dt = 0); // коэффициент 0-31, dt в миллисекундах 69 | 70 | void setK(uint8_t k); // коэффициент 0-31 71 | void setDt(uint16_t dt); // установить период фильтрации в мс 72 | void setPass(uint8_t pass); // установить режим пропуска (FF_PASS_MAX / FF_PASS_MIN) 73 | void setRaw(long raw); // установить исходное значение для фильтрации 74 | void setFil(long fil); // установить фильтрованное значение 75 | bool checkPass(long val); // проверка на переполнение 76 | void compute(); // расчёт по таймеру 77 | void computeNow(); // произвести расчёт сейчас 78 | int getFil(); // получить фильтрованное значение 79 | int getRaw(); // получить последнее сырое значение 80 | 81 | // =========== SIMPLE KALMAN kalman.h =========== 82 | // разброс измерения, разброс оценки, скорость изменения значений 83 | GKalman(float mea_e, float est_e, float q); 84 | // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) 85 | GKalman(float mea_e, float q); 86 | 87 | // разброс измерения, разброс оценки, скорость изменения значений 88 | void setParameters(float mea_e, float est_e, float q); 89 | 90 | // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) 91 | void setParameters(float mea_e, float q); 92 | 93 | // возвращает фильтрованное значение 94 | float filtered(float value); 95 | 96 | // ======== LINEAR APPROXIMATION linear.h ======= 97 | GLinear<тип_данных> filter; 98 | 99 | void compute(*x_array, *y_array, arrSize); // аппроксимировать 100 | float getA(); // получить коэффициент А 101 | float getB(); // получить коэффициент В 102 | float getDelta(); // получить аппроксимированное изменение 103 | 104 | // ============= MEDIAN N median.h ============== 105 | GMedian<порядок, тип_данных> median; 106 | 107 | тип_данных filtered(тип_данных value); // получить результат 108 | 109 | // ============= MEDIAN 3 median3.h ============== 110 | GMedian3<тип_данных> median; 111 | 112 | тип_данных filtered(тип_данных value); // получить результат 113 | 114 | // ========= RING AVERAGE RingAverage.h ========= 115 | RingAverage<тип_данных, порядок> filter; 116 | // не работает для float! 117 | 118 | тип_данных filtered(тип_данных val); // получить результат 119 | float filteredFloat(тип_данных val); // получить результат float 120 | 121 | // ====== RUNNING AVERAGE runningAverage.h ====== 122 | GFilterRA filter; 123 | GFilterRA(float coef); // установить коэффициент 124 | GFilterRA(float coef, uint16_t interval); // установить коэффициент и период 125 | 126 | void setCoef(float coef); // установить коэффициент 127 | void setPeriod(uint16_t interval); // установить период 128 | float filteredTime(float value); // получить результат по таймеру 129 | float filtered(float value); // получить результат сейчас 130 | ``` 131 | 132 | 133 | ## Версии 134 | - v1.6 от 12.11.2019 135 | - v1.7: исправлен GLinear 136 | - v1.8: небольшие улучшения 137 | - v2.0: 138 | - Улучшен и исправлен median и median3 139 | - Улучшен linear 140 | - Смотрите примеры! Использование этих фильтров чуть изменилось 141 | - v2.1: Исправлен расчёт дельты в линейном фильтре 142 | - v2.2: Исправлена ошибка компиляции 143 | - v3.0: Добавлен FastFilter и RingAverage 144 | - v3.1: Оптимизация кода у многих фильтров 145 | - v3.1.1: Исправлена ошибочка 146 | - v3.2: мелкие фиксы, обновлена документация 147 | 148 | 149 | ## Баги и обратная связь 150 | При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru) 151 | Библиотека открыта для доработки и ваших **Pull Request**'ов! 152 | 153 | 154 | При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать: 155 | - Версия библиотеки 156 | - Какой используется МК 157 | - Версия SDK (для ESP) 158 | - Версия Arduino IDE 159 | - Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде 160 | - Какой код загружался, какая работа от него ожидалась и как он работает в реальности 161 | - В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код 162 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | This is an automatic translation, may be incorrect in some places. See sources and examples! 2 | 3 | # GyverFilters 4 | GyverFilters - library with some handy filters for Arduino 5 | - GFilterRA - a compact alternative to the filter exponential running average (Running Average) 6 | - GMedian3 - fast 3rd order median filter (cuts out outliers) 7 | - GMedian - N-th order median filter. The order is configured in GyverFilters.h - MEDIAN_FILTER_SIZE 8 | - GABfilter - alpha-beta filter (Kalman version for one-dimensional case) 9 | - GKalman - a simplified Kalman for the one-dimensional case (in my opinion the best of the filters) 10 | - GLinear - linear least squares approximation for two arrays 11 | - FastFilter - fast integer exponential filter 12 | - RingAverage - running average with ring buffer 13 | 14 | Based on [Digital Filters Tutorial](https://alexgyver.ru/lessons/filters/) 15 | 16 | ### Compatibility 17 | Compatible with all Arduino platforms (using Arduino functions) 18 | 19 | ### Documentation 20 | The library has [extended documentation](https://alexgyver.ru/GyverFilters/) 21 | 22 | ## Content 23 | - [Install](#install) 24 | - [Usage](#usage) 25 | - [Versions](#versions) 26 | - [Bugs and feedback](#feedback) 27 | 28 | 29 | ## Installation 30 | - The library can be found by the name **GyverFilters** and installed via the library manager in: 31 | - Arduino IDE 32 | - Arduino IDE v2 33 | - PlatformIO 34 | - [Download library](https://github.com/GyverLibs/GyverFilters/archive/refs/heads/main.zip) .zip archive forfor manual setting: 35 | - Unzip and put in *C:\Program Files (x86)\Arduino\libraries* (Windows x64) 36 | - Unzip and put in *C:\Program Files\Arduino\libraries* (Windows x32) 37 | - Unpack and put in *Documents/Arduino/libraries/* 38 | - (Arduino IDE) automatic installation from .zip: *Sketch/Include library/Add .ZIP library…* and specify the downloaded archive 39 | - Read more detailed instructions for installing libraries [here] (https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE% D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA) 40 | 41 | 42 | ## Usage 43 | ```cpp 44 | // =========== ALPHA-BETTA alfaBeta.h =========== 45 | // sampling period (measurements), process variation, noise variation 46 | GABfilter(float delta, float sigma_process, float sigma_noise); 47 | 48 | // sampling period (measurements), process variation, noise variation 49 | void setParameters(float delta, float sigma_process, float sigma_noise); 50 | 51 | // returns the filtered value 52 | float filtered(float value); 53 | 54 | // ========== FAST FILTER FastFilter.h ========== 55 | fastfilter; 56 | FastFilter(uint8_t k = 20); // coefficient 0-31, dt 0 57 | FastFilter(uint8_t k = 20, uint16_t dt = 0); // coefficient 0-31, dt in milliseconds 58 | 59 | void setK(uint8_tk); // coefficient 0-31 60 | void setDt(uint16_t dt); // set filtering period in ms 61 | void setPass(uint8_t pass); // set pass mode (FF_PASS_MAX / FF_PASS_MIN) 62 | void setRaw(long raw); // set initial value for filtering 63 | void setFil(long fil); // set filtered value 64 | bool checkPass(long val); // check for overflow 65 | void compute(); // timer calculation 66 | void computeNow(); // calculate now 67 | int getfil(); // get filtered value 68 | int getRaw(); // get last raw value 69 | 70 | // =========== SIMPLE KALMAN kalman.h =========== 71 | // measurement spread, estimate spread, speedvalue changes 72 | GKalman(float mea_e, float est_e, float q); 73 | // measurement scatter, rate of change of values ​​(measurement scatter is taken equal to the estimate scatter) 74 | GKalman(float mea_e, float q); 75 | 76 | // measurement spread, estimate spread, rate of change of values 77 | void setParameters(float mea_e, float est_e, float q); 78 | 79 | // measurement scatter, rate of change of values ​​(measurement scatter is taken equal to the estimate scatter) 80 | void setParameters(float mea_e, float q); 81 | 82 | // returns the filtered value 83 | float filtered(float value); 84 | 85 | // ======== LINEAR APPROXIMATION linear.h ======= 86 | GLinear filter; 87 | 88 | void compute(*x_array, *y_array, arrSize); // approximate 89 | float getA(); // get factor A 90 | float getB(); // get factor B 91 | float getDelta(); // get approximate change 92 | 93 | // ============= MEDIAN N median.h ============== 94 | GMedian median; 95 | 96 | data_type filtered(data_type value); // get result 97 | 98 | // ============= MEDIAN 3 median3.h ============== 99 | GMedian3 median; 100 | 101 | data_type filtered(data_type value); // get result 102 | 103 | // ========= RING AVERAGE RingAverage.h ========= 104 | RingAverage filter; 105 | 106 | datatype filtered(datatype val); // get result 107 | float filteredFloat(datatype val); // get float result 108 | 109 | // ====== RUNNING AVERAGE runningAverage.h ====== 110 | GFilterRA filter; 111 | GFilterRA(floatcoef); // set coefficient 112 | GFilterRA(float coef, uint16_t interval); // set coefficient and period 113 | 114 | void setCoef(floatcoef); // set coefficient 115 | void setPeriod(uint16_t interval); // set period 116 | float filteredTime(float value); // get result by timer 117 | float filtered(float value); // get result now 118 | ``` 119 | 120 | 121 | ## Versions 122 | - v1.6 from 11/12/2019 123 | - v1.7: fixed GLinear 124 | - v1.8: minor improvements 125 | - v2.0: 126 | -Improved and fixed median and median3 127 | - Improved linear 128 | - See examples! The use of these filters has changed slightly 129 | - v2.1: Fixed delta calculation in linear filter 130 | - v2.2: Compilation error fixed 131 | - v3.0: Added FastFilter and RingAverage 132 | - v3.1: Code optimization for many filters 133 | - v3.1.1: Bug fixed 134 | - v3.2: minor fixes, updated documentation 135 | 136 | 137 | ## Bugs and feedback 138 | When you find bugs, create an **Issue**, or better, immediately write to the mail [alex@alexgyver.ru](mailto:alex@alexgyver.ru) 139 | The library is open for revision and your **Pull Request**'s! -------------------------------------------------------------------------------- /examples/GFilterRA/GFilterRA.ino: -------------------------------------------------------------------------------- 1 | #include "GyverFilters.h" 2 | GFilterRA analog0; // фильтр назовём analog0 3 | 4 | void setup() { 5 | Serial.begin(9600); 6 | 7 | // установка коэффициента фильтрации (0.0... 1.0). Чем меньше, тем плавнее фильтр 8 | analog0.setCoef(0.01); 9 | 10 | // установка шага фильтрации (мс). Чем меньше, тем резче фильтр 11 | analog0.setStep(10); 12 | } 13 | 14 | void loop() { 15 | Serial.println(analog0.filteredTime(analogRead(0))); 16 | } 17 | -------------------------------------------------------------------------------- /examples/GLinear_arrays/GLinear_arrays.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример линейной аппроксимации методом наименьших квадратов 3 | Два массива: по оси Х и по оси У 4 | Линейная аппроксимация повозоляет получить уравнение прямой, 5 | равноудалённой от точек на плоскости ХУ. Удобно для расчёта 6 | роста изменяющейся шумящей величины. Уравнение вида у = A*x + B 7 | В папке с данным примером есть скриншот из excel, 8 | иллюстрирующий работу аппроксимации с такими же исходными 9 | */ 10 | 11 | // два массива с данными (одинаковой размероности и размера) 12 | int x_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 13 | int y_array[] = {1, 5, 2, 8, 3, 9, 10, 5, 15, 12}; 14 | 15 | #include 16 | GLinear test; // указываем тип данных в <> 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | 21 | // передаём массивы и размер одного из них 22 | test.compute((int*)x_array, (int*)y_array, 10); 23 | 24 | // Уравнение вида у = A*x + B 25 | Serial.println(test.getA()); // получить коэффициент А 26 | Serial.println(test.getB()); // получить коэффициент В 27 | Serial.println(test.getDelta()); // получить изменение (аппроксимированное) 28 | } 29 | 30 | void loop() { 31 | 32 | } 33 | -------------------------------------------------------------------------------- /examples/GLinear_arrays/excel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GyverLibs/GyverFilters/a66f63b561c219b303d96894a0b87ee0611cf246/examples/GLinear_arrays/excel.jpg -------------------------------------------------------------------------------- /examples/GLinear_running/GLinear_running.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример линейной аппроксимации методом наименьших квадратов 3 | Два массива: по оси Х и по оси У 4 | Наполнение массивов осуществляется динамически: сдвигом и записью в крайнюю ячейку, 5 | то есть аппроксимация по последним ARRAY_SIZE изменениям!! 6 | */ 7 | #define ARRAY_SIZE 10 // размер пространства для аппроксимации 8 | 9 | // два массива с данными (одинаковой размероности и размера) 10 | int x_array[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // ось x от 1 до 10, допустим СЕКУНД 11 | int y_array[ARRAY_SIZE]; // значения по оси У будем брать с датчика 12 | 13 | #include 14 | GLinear test; // указываем тип данных в <> 15 | 16 | void setup() { 17 | Serial.begin(9600); 18 | } 19 | 20 | void loop() { 21 | for (byte i = 0; i < ARRAY_SIZE - 1; i++) { // счётчик от 0 до ARRAY_SIZE 22 | y_array[i] = y_array[i + 1]; // сдвинуть массив давлений КРОМЕ ПОСЛЕДНЕЙ ЯЧЕЙКИ на шаг назад 23 | } 24 | // последний элемент массива теперь - новое значение (просто с аналог. датчика) 25 | y_array[ARRAY_SIZE - 1] = analogRead(0); 26 | 27 | // передаём массивы и размер одного из них 28 | test.compute((int*)x_array, (int*)y_array, sizeof(x_array)); 29 | 30 | // по нашим исходным данным это будет производная, т.е. "изменение единиц в секунду" 31 | Serial.println(test.getDelta()); // получить изменение (аппроксимированное) 32 | 33 | delay(1000); // секундная задержка 34 | } 35 | -------------------------------------------------------------------------------- /examples/RingAverage/RingAverage.ino: -------------------------------------------------------------------------------- 1 | #include "GyverFilters.h" 2 | RingAverage fil; 3 | 4 | void setup() { 5 | Serial.begin(9600); 6 | } 7 | 8 | void loop() { 9 | Serial.println(fil.filtered(random(50))); 10 | delay(10); 11 | } 12 | -------------------------------------------------------------------------------- /examples/alphabeta_example/alphabeta_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример альфа-бета фильтра 3 | */ 4 | 5 | #include "GyverFilters.h" 6 | 7 | // параметры: период дискретизации (измерений), process variation, noise variation 8 | GABfilter testFilter(0.08, 40, 1); 9 | 10 | void setup() { 11 | Serial.begin(9600); 12 | } 13 | 14 | void loop() { 15 | delay(80); 16 | int value = analogRead(0); 17 | value += random(2) * random(-1, 2) * random(10, 70); 18 | Serial.print("$"); 19 | Serial.print(value); 20 | Serial.print(" "); 21 | value = testFilter.filtered((int)value); 22 | Serial.print(value); 23 | Serial.println(";"); 24 | } 25 | -------------------------------------------------------------------------------- /examples/fastFilter/fastFilter.ino: -------------------------------------------------------------------------------- 1 | // быстрый запаздывающйи фильтр 2 | 3 | #include 4 | FastFilter fil(29); // 0-32 5 | void setup() { 6 | Serial.begin(9600); 7 | fil.setK(30); 8 | fil.setRaw(1000); 9 | fil.setFil(0); 10 | } 11 | 12 | void loop() { 13 | fil.computeNow(); 14 | Serial.println(fil.getFil()); 15 | delay(100); 16 | } 17 | -------------------------------------------------------------------------------- /examples/filters_comparsion/filters_comparsion.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Сравнение калмана и бегущего среднего 3 | */ 4 | #include "GyverFilters.h" 5 | 6 | // параметры: разброс измерения, разброс оценки, скорость изменения значений 7 | // разброс измерения: шум измерений 8 | // разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения 9 | // скорость изменения значений: 0.001-1, варьировать самому 10 | 11 | GKalman kalman(90, 90, 0.5); 12 | GFilterRA average(0.5, 80); 13 | 14 | void setup() { 15 | Serial.begin(9600); 16 | } 17 | 18 | void loop() { 19 | int value = analogRead(0); 20 | value += random(2) * random(-1, 2) * random(50, 100); 21 | Serial.print("$"); 22 | Serial.print(value); 23 | Serial.print(" "); 24 | 25 | Serial.print((int)kalman.filtered(value)); 26 | Serial.print(" "); 27 | Serial.print((int)average.filtered(value)); 28 | Serial.println(";"); 29 | delay(80); 30 | } 31 | -------------------------------------------------------------------------------- /examples/kalman_example/kalman_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример простого одномерного фильтра 3 | */ 4 | 5 | #include "GyverFilters.h" 6 | 7 | // параметры: разброс измерения, разброс оценки, скорость изменения значений 8 | // разброс измерения: шум измерений 9 | // разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения 10 | // скорость изменения значений: 0.001-1, варьировать самому 11 | 12 | GKalman testFilter(40, 40, 0.5); 13 | 14 | // также может быть объявлен как (разброс измерения, скорость изменения значений) 15 | // GKalman testFilter(40, 0.5); 16 | 17 | void setup() { 18 | Serial.begin(9600); 19 | } 20 | 21 | void loop() { 22 | delay(80); 23 | int value = analogRead(0); 24 | value += random(2) * random(-1, 2) * random(10, 70); 25 | Serial.print("$"); 26 | Serial.print(value); 27 | Serial.print(" "); 28 | value = testFilter.filtered((int)value); 29 | Serial.print(value); 30 | Serial.println(";"); 31 | } 32 | -------------------------------------------------------------------------------- /examples/median3_example/median3_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример использования быстрого медианного фильтра 3 порядка 3 | */ 4 | 5 | #include "GyverFilters.h" 6 | GMedian3 testFilter; // указываем тип данных в <> 7 | 8 | void setup() { 9 | Serial.begin(9600); 10 | } 11 | 12 | void loop() { 13 | int value = analogRead(0); 14 | // добавляем шум "выбросы" 15 | value += random(2) * random(2) * random(-1, 2) * random(50, 250); 16 | Serial.print(value); 17 | Serial.print(','); 18 | value = testFilter.filtered(value); 19 | Serial.println(value); 20 | delay(80); 21 | } 22 | -------------------------------------------------------------------------------- /examples/median_example/median_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Пример использования медианного фильтра. 3 | */ 4 | 5 | #include "GyverFilters.h" 6 | 7 | // указываем размер окна и тип данных в <> 8 | GMedian<10, int> testFilter; 9 | 10 | void setup() { 11 | Serial.begin(9600); 12 | } 13 | 14 | void loop() { 15 | delay(80); 16 | int value = analogRead(0); 17 | // добавляем шум "выбросы" 18 | value += random(2) * random(2) * random(-1, 2) * random(50, 250); 19 | Serial.print(value); 20 | Serial.print(','); 21 | value = testFilter.filtered(value); 22 | Serial.println(value); 23 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For GyverFilters 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | GyverFilters KEYWORD1 10 | GFilterRA KEYWORD1 11 | GMedian3 KEYWORD1 12 | GMedian KEYWORD1 13 | GABfilter KEYWORD1 14 | GKalman KEYWORD1 15 | GLinear KEYWORD1 16 | RingAverage KEYWORD1 17 | FastFilter KEYWORD1 18 | 19 | ####################################### 20 | # Methods and Functions (KEYWORD2) 21 | ####################################### 22 | 23 | setK KEYWORD2 24 | setCoef KEYWORD2 25 | setPeriod KEYWORD2 26 | filteredTime KEYWORD2 27 | filtered KEYWORD2 28 | setParameters KEYWORD2 29 | getA KEYWORD2 30 | getB KEYWORD2 31 | getDelta KEYWORD2 32 | setRaw KEYWORD2 33 | setFil KEYWORD2 34 | computeNow KEYWORD2 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=GyverFilters 2 | version=3.2 3 | author=AlexGyver 4 | maintainer=AlexGyver 5 | sentence=Library with few filters for data processing 6 | paragraph=Library with few filters for data processing 7 | category=Data Processing 8 | url=https://github.com/GyverLibs/GyverFilters 9 | architectures=* -------------------------------------------------------------------------------- /src/GyverFilters.h: -------------------------------------------------------------------------------- 1 | /* 2 | GyverFilters - библиотека с некоторыми удобными фильтрами для Arduino 3 | Документация: https://alexgyver.ru/gyverfilters/ 4 | GitHub: https://github.com/GyverLibs/GyverFilters 5 | - GFilterRA - компактная альтернатива фильтра экспоненциальное бегущее среднее (Running Average) 6 | - GMedian3 - быстрый медианный фильтр 3-го порядка (отсекает выбросы) 7 | - GMedian - медианный фильтр N-го порядка. Порядок настраивается в GyverFilters.h - MEDIAN_FILTER_SIZE 8 | - GABfilter - альфа-бета фильтр (разновидность Калмана для одномерного случая) 9 | - GKalman - упрощённый Калман для одномерного случая (на мой взгляд лучший из фильтров) 10 | - GLinear - линейная аппроксимация методом наименьших квадратов для двух массивов 11 | - FastFilter - быстрый целочисленный экспоненциальный фильтр 12 | - RingAverage - бегущее среднее с кольцевым буфером 13 | 14 | AlexGyver, alex@alexgyver.ru 15 | https://alexgyver.ru/ 16 | MIT License 17 | 18 | Версии: 19 | v1.6 от 12.11.2019 20 | v1.7: исправлен GLinear 21 | v1.8: небольшие улучшения 22 | v2.0: 23 | - Улучшен и исправлен median и median3 24 | - Улучшен linear 25 | - Смотрите примеры! Использование этих фильтров чуть изменилось 26 | v2.1: Исправлен расчёт дельты в линейном фильтре 27 | v2.2: Исправлена ошибка компиляции 28 | v3.0: Добавлен FastFilter и RingAverage 29 | v3.1: Оптимизация кода у многих фильтров 30 | v3.1.1: Исправлена ошибочка 31 | v3.2: мелкие фиксы, обновлена документация 32 | */ 33 | 34 | #ifndef _GyverFilters_h 35 | #define _GyverFilters_h 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #endif -------------------------------------------------------------------------------- /src/filters/FastFilter.h: -------------------------------------------------------------------------------- 1 | // быстрый целочисленный экспоненциальный фильтр 2 | 3 | #ifndef _FastFilter_h 4 | #define _FastFilter_h 5 | 6 | #define FF_PASS_MAX 1 7 | #define FF_PASS_MIN 2 8 | 9 | class FastFilter { 10 | public: 11 | // коэффициент 0-31 12 | FastFilter(uint8_t k = 20, uint16_t dt = 0) { 13 | setK(k); 14 | setDt(dt); 15 | } 16 | 17 | // коэффициент 0-31 18 | void setK(uint8_t k) { 19 | _k1 = k; 20 | _k2 = 32 - k; 21 | } 22 | 23 | // установить период фильтрации 24 | void setDt(uint16_t dt) { 25 | _dt = dt; 26 | } 27 | 28 | // установить режим пропуска (FF_PASS_MAX / FF_PASS_MIN) 29 | void setPass(uint8_t pass) { 30 | _pass = pass; 31 | } 32 | 33 | // установить исходное значение для фильтрации 34 | void setRaw(long raw) { 35 | _raw = raw; 36 | } 37 | 38 | // установить фильтрованное значение 39 | void setFil(long fil) { 40 | _raw_f = fil; 41 | } 42 | 43 | // проверка на переполнение 44 | bool checkPass(long val) { 45 | if (_pass == FF_PASS_MAX && val > _raw_f) { 46 | _raw_f = val; 47 | return 1; 48 | } else if (_pass == FF_PASS_MIN && val < _raw_f) { 49 | _raw_f = val; 50 | return 1; 51 | } 52 | return 0; 53 | } 54 | 55 | // расчёт по таймеру 56 | void compute() { 57 | if (_dt == 0 || millis() - _tmr >= _dt) { 58 | _tmr = millis(); 59 | computeNow(); 60 | } 61 | } 62 | 63 | // произвести расчёт сейчас 64 | void computeNow() { 65 | _raw_f = ((long)_k1 * _raw_f + _k2 * _raw) >> 5; 66 | } 67 | 68 | // получить фильтрованное значение 69 | long getFil() { 70 | return _raw_f; 71 | } 72 | 73 | // получить последнее сырое значение 74 | long getRaw() { 75 | return _raw; 76 | } 77 | 78 | private: 79 | uint32_t _tmr = 0; 80 | uint16_t _dt = 0; 81 | uint8_t _k1 = 20, _k2 = 12; 82 | uint8_t _pass = 0; 83 | long _raw_f = 0, _raw = 0; 84 | }; 85 | #endif -------------------------------------------------------------------------------- /src/filters/RingAverage.h: -------------------------------------------------------------------------------- 1 | // бегущее среднее с кольцевым буфером 2 | 3 | #ifndef _RingAverage_h 4 | #define _RingAverage_h 5 | 6 | template < typename TYPE, int SIZE > 7 | class RingAverage { 8 | public: 9 | RingAverage() { 10 | for (int i = 0; i < SIZE; i++) buf[i] = 0; 11 | } 12 | TYPE filtered(TYPE val) { 13 | if (++t >= SIZE) t = 0; // перемотка t 14 | sum -= buf[t]; // вычитаем старое 15 | sum += val; // прибавляем новое 16 | buf[t] = val; // запоминаем в массив 17 | return (sum / SIZE); 18 | } 19 | float filteredFloat(TYPE val) { 20 | if (++t >= SIZE) t = 0; // перемотка t 21 | sum -= buf[t]; // вычитаем старое 22 | sum += val; // прибавляем новое 23 | buf[t] = val; // запоминаем в массив 24 | return ((float)sum / SIZE); 25 | } 26 | 27 | private: 28 | TYPE buf[SIZE]; 29 | int32_t sum = 0; 30 | int t = 0; 31 | }; 32 | #endif -------------------------------------------------------------------------------- /src/filters/alfaBeta.h: -------------------------------------------------------------------------------- 1 | // альфа-бета фильтр 2 | 3 | #ifndef _GABfilter_h 4 | #define _GABfilter_h 5 | 6 | class GABfilter { 7 | public: 8 | // период дискретизации (измерений), process variation, noise variation 9 | GABfilter(float delta, float sigma_process, float sigma_noise) { 10 | setParameters(delta, sigma_process, sigma_noise); 11 | } 12 | 13 | // период дискретизации (измерений), process variation, noise variation 14 | void setParameters(float delta, float sigma_process, float sigma_noise) { 15 | dt = delta; 16 | float lambda = (float)sigma_process * dt * dt / sigma_noise; 17 | float r = (4.0 + lambda - sqrt(8.0 * lambda + lambda * lambda)) / 4.0; 18 | a = 1.0 - r * r; 19 | b = 2.0 * (2.0 - a) - 4.0 * sqrt(1.0 - a); 20 | } 21 | 22 | // возвращает фильтрованное значение 23 | float filtered(float value) { 24 | xm = value; 25 | xk = xk_1 + (float)vk_1 * dt; 26 | vk = vk_1; 27 | rk = xm - xk; 28 | xk += (float)a * rk; 29 | vk += (float)b * rk / dt; 30 | xk_1 = xk; 31 | vk_1 = vk; 32 | return xk_1; 33 | } 34 | 35 | private: 36 | float dt; 37 | float xk_1, vk_1, a, b; 38 | float xk, vk, rk; 39 | float xm; 40 | }; 41 | #endif -------------------------------------------------------------------------------- /src/filters/kalman.h: -------------------------------------------------------------------------------- 1 | // упрощённый Калман для одномерного случая 2 | 3 | #ifndef _GKalman_h 4 | #define _GKalman_h 5 | 6 | class GKalman { 7 | public: 8 | // разброс измерения, разброс оценки, скорость изменения значений 9 | GKalman(float mea_e, float est_e, float q) { 10 | setParameters(mea_e, est_e, q); 11 | } 12 | 13 | // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) 14 | GKalman(float mea_e, float q) { 15 | setParameters(mea_e, mea_e, q); 16 | } 17 | 18 | // разброс измерения, разброс оценки, скорость изменения значений 19 | void setParameters(float mea_e, float est_e, float q) { 20 | _err_measure = mea_e; 21 | _err_estimate = est_e; 22 | _q = q; 23 | } 24 | 25 | // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) 26 | void setParameters(float mea_e, float q) { 27 | setParameters(mea_e, mea_e, q); 28 | } 29 | 30 | // возвращает фильтрованное значение 31 | float filtered(float value) { 32 | float _kalman_gain, _current_estimate; 33 | _kalman_gain = _err_estimate / (_err_estimate + _err_measure); 34 | _current_estimate = _last_estimate + _kalman_gain * (value - _last_estimate); 35 | _err_estimate = (1.0 - _kalman_gain)*_err_estimate + fabs(_last_estimate-_current_estimate)*_q; 36 | _last_estimate=_current_estimate; 37 | return _current_estimate; 38 | } 39 | 40 | private: 41 | float _err_measure = 0.0; 42 | float _err_estimate = 0.0; 43 | float _q = 0.0; 44 | float _last_estimate = 0.0; 45 | }; 46 | #endif -------------------------------------------------------------------------------- /src/filters/linear.h: -------------------------------------------------------------------------------- 1 | // линейная аппроксимация методом наименьших квадратов 2 | 3 | #ifndef _GLinear_h 4 | #define _GLinear_h 5 | 6 | template < typename TYPE > 7 | class GLinear { 8 | public: 9 | // аппроксимировать 10 | void compute(TYPE *x_array, TYPE *y_array, int arrSize) { 11 | int32_t sumX = 0, sumY = 0, sumX2 = 0, sumXY = 0; 12 | for (int i = 0; i < arrSize; i++) { // для всех элементов массива 13 | sumX += x_array[i]; 14 | sumY += (int32_t)y_array[i]; 15 | sumX2 += x_array[i] * x_array[i]; 16 | sumXY += (int32_t)y_array[i] * x_array[i]; 17 | } 18 | a = (int32_t)arrSize * sumXY - (int32_t)sumX * sumY; // расчёт коэффициента наклона приямой 19 | a = (float)a / (arrSize * sumX2 - sumX * sumX); 20 | b = (float)(sumY - (float)a * sumX) / arrSize; 21 | delta = a * (x_array[arrSize-1] - x_array[0]); // расчёт изменения 22 | } 23 | 24 | // получить коэффициент А 25 | float getA() { 26 | return a; 27 | } 28 | 29 | // получить коэффициент В 30 | float getB() { 31 | return b; 32 | } 33 | 34 | // получить аппроксимированное изменение 35 | float getDelta() { 36 | return delta; 37 | } 38 | 39 | private: 40 | float a, b, delta; 41 | }; 42 | 43 | /* 44 | Сам алгоритм выглядит так: 45 | void loop() { 46 | sumX = 0; 47 | sumY = 0; 48 | sumX2 = 0; 49 | sumXY = 0; 50 | for (int i = 0; i < steps; i++) { 51 | sumX += X[i]; 52 | sumY += Y[i]; 53 | sumX2 += X[i] * X[i]; 54 | sumXY += X[i] * Y[i]; 55 | } 56 | a = (steps * sumXY - sumX * sumY) / (steps * sumX2 - sumX * sumX); 57 | b = (sumY - a * sumX) / steps; 58 | int delta = steps * a; 59 | } 60 | */ 61 | #endif -------------------------------------------------------------------------------- /src/filters/median.h: -------------------------------------------------------------------------------- 1 | // медианный фильтр N-го порядка 2 | 3 | #ifndef _GMedian_h 4 | #define _GMedian_h 5 | 6 | template < uint8_t SIZE, typename TYPE > 7 | class GMedian { 8 | public: 9 | TYPE filtered(TYPE newVal) { 10 | buffer[_count] = newVal; 11 | if ((_count < SIZE - 1) && (buffer[_count] > buffer[_count + 1])) { 12 | for (int i = _count; i < SIZE - 1; i++) { 13 | if (buffer[i] > buffer[i + 1]) { 14 | TYPE buff = buffer[i]; 15 | buffer[i] = buffer[i + 1]; 16 | buffer[i + 1] = buff; 17 | } 18 | } 19 | } else { 20 | if ((_count > 0) && (buffer[_count - 1] > buffer[_count])) { 21 | for (int i = _count; i > 0; i--) { 22 | if (buffer[i] < buffer[i - 1]) { 23 | TYPE buff = buffer[i]; 24 | buffer[i] = buffer[i - 1]; 25 | buffer[i - 1] = buff; 26 | } 27 | } 28 | } 29 | } 30 | if (++_count >= SIZE) _count = 0; 31 | return buffer[SIZE / 2]; 32 | } 33 | private: 34 | TYPE buffer[SIZE]; 35 | uint8_t _count = 0; 36 | }; 37 | #endif -------------------------------------------------------------------------------- /src/filters/median3.h: -------------------------------------------------------------------------------- 1 | // быстрый медианный фильтр 3-го порядка 2 | 3 | #ifndef _GMedian3_h 4 | #define _GMedian3_h 5 | 6 | template < typename TYPE > 7 | class GMedian3 { 8 | public: 9 | TYPE filtered(TYPE value) { // возвращает фильтрованное значение 10 | buf[_counter] = value; 11 | if (++_counter > 2) _counter = 0; 12 | return (max(buf[0], buf[1]) == max(buf[1], buf[2])) ? max(buf[0], buf[2]) : max(buf[1], min(buf[0], buf[2])); 13 | } 14 | 15 | private: 16 | TYPE buf[3]; 17 | uint8_t _counter = 0; 18 | }; 19 | #endif -------------------------------------------------------------------------------- /src/filters/runningAverage.h: -------------------------------------------------------------------------------- 1 | // экспоненциальное бегущее среднее 2 | 3 | #ifndef _GFilterRA_h 4 | #define _GFilterRA_h 5 | 6 | class GFilterRA { 7 | public: 8 | GFilterRA(){} 9 | 10 | GFilterRA(float coef, uint16_t interval) { 11 | _coef = coef; 12 | _prd = interval; 13 | } 14 | 15 | GFilterRA(float coef) { 16 | _coef = coef; 17 | } 18 | 19 | void setCoef(float coef) { 20 | _coef = coef; 21 | } 22 | 23 | void setPeriod(uint16_t interval) { 24 | _prd = interval; 25 | } 26 | 27 | float filteredTime(float value) { 28 | if (millis() - _tmr >= _prd) { 29 | _tmr += _prd; 30 | filtered(value); 31 | } 32 | return _fil; 33 | } 34 | 35 | float filtered(float value) { 36 | return _fil += (value - _fil) * _coef; 37 | } 38 | 39 | // 40 | void setStep(uint16_t interval) { 41 | _prd = interval; 42 | } 43 | 44 | private: 45 | float _coef = 0.0, _fil = 0.0; 46 | uint32_t _tmr = 0; 47 | uint16_t _prd = 0; 48 | }; 49 | #endif --------------------------------------------------------------------------------