SMA Filter(Simple moving average) - Простое скользящее среднее или арифметическое скользящее среднее
Vložit
- čas přidán 24. 07. 2024
- Простое скользящее среднее, или арифметическое скользящее среднее (англ. simple moving average, англ. SMA) численно равно среднему арифметическому значений исходной функции за установленный период. В данном видео применим фильтр и посмотрим, как он поможет нам с обработкой внутреннего шума АЦП микроконтроллера.
Заходи в нашу группу ВК solderingiron.stm32
ЯндексДзен: zen.yandex.ru/id/622208eed2eb...
Скачать библиотеку: github.com/Solderingironspb/S...
00:00 Вступление
01:32 Создание проекта
05:45 Написание примера по замеру АЦП + фильтрация сигнала
18:53 Вычисление напряжение на входе канала АЦП - Věda a technologie
Отлично, поздравляю за етот ролик. Буду пользовать в будушем проекте етот замечательный филтер.
Взял на заметку. Спасибо!! Как раз сейчас буду использовать в проекте показания датчика давления жидкости. Сделал как раз схему по твоей наводке с ОУ и делителем.
Там это как никогда кстати подойдет) я к ОВЕН ПР200 недавно подключал датчик давления, так там после насоса давление очень сильно скакало и сложно было отстроить компаратор. (Т.е. не шум датчика, а именно сигнал) Пришлось тоже фильтровать, чтоб система более стабильной была.
@@Solderingironspb Извиняюсь, я могу если что обратиться к тебе за советом, если у меня будут трудности? )) Тем более живем в одном городе))
@@dr.emmettbrown8466 можно) я всегда на связи)
@@Solderingironspb Выражаю благодарность за схему с ОУ и делителем. Собрал, протестировал. Результат отличный. Показания очень точные. Плавают п пределах 2-4 соток без всяких усреднений.
Правда я поставил дополнительно отдельную прецизионную LDO на опорное напряжение на ногу Vref и разделил цифровую и аналоговые земли.
Первое что бросилось в глаза:
SMA_Filter_Result = SMA_Filter_Result >> 5u;
Человек качает библиотеку и меняет
#define SMA_FILTER_ORDER 48
Всё. Приехали.
Во вторых - зачем долбать контроллер вызовами прерываний, если выдёргиваем данные только 1 из 300? Просто настраиваем время срабатывания прерывания по TIM4.
В третьих - что-то намудрили Вы в функции фильтрации. Нафига что-то смещать?
Просто пишем:
uint16_t SMA_FILTER_Get_Value(uint16_t *SMA_Filter_buffer, uint16_t *RAW_Data) {
static uint32_t SMA_Filter_Result;
static uint16_t index_raw_buff;
index_raw_buff++; if (index_raw_buff>SMA_FILTER_ORDER) { index_raw_buff=0; }
SMA_Filter_buffer[index_raw_buff] = *RAW_Data;
SMA_Filter_Result = 0;
for (uint16_t i = 0; i < SMA_FILTER_ORDER; i++) {
SMA_Filter_Result += SMA_Filter_buffer[i];
}
return SMA_Filter_Result/SMA_FILTER_ORDER;
}
можно еще сортировать буфер по возростанию, потом отбрасывать самые маленькие и самые большие значения (шум) остальное усреднять как у тебя на видео. Но будет медленнее из-за сортировки.
Медиану можно использовать, но она тут не особо нужна будет, если исходный сигнал подготовлен аппаратно.
привет. увидел у тебя pycharm установлен. не будели уроков по пайтону в связке с МК?
Добрый день. Изучение python - это побочное явление у меня, когда нужно было сделать небольшой проект на работе. Уже давно там ничего не делаю, т.к. тянуть два разных языка очень сложно. Я и на C# тоже писал программы для работы с МК). В итоге понял, что лучше всего - это С на МК и С/С++ на PC. Так голова чище, т.к. не разрываешься уже между языками. Пишу консольные приложения и с интерфейсом на Qt 5.1x. Хотел делать уроки по нему, но пока стесняюсь, т.к. знаний в этой области куда меньше, поэтому часто на С++ делаю С вставки.
мы делаем весы, и с АЦП наморочились очень сильно. Единственное что важно сделать это опорное очень хорошее, есть специальные микры под опорку. MCP1725 что то там. И второе это правильно подобрать кондеры по входу измерения, тут не работает чем больше емкость тем лучше. Нужно правильно подобрать, чтоб не плыло и тд)
а так да, нужен только цифровой фильтр
все верно говоришь. Я сейчас тестирую TL431. Пробую вместо Vrefint его использовать. 2.5 вольта подаю на АЦП в 1 канал и 2 канал уже корректирую от результата с TL431. Потом видео может сниму. С источником тока дает очень хорошие результаты. Тут, как всегда, температура нам мешает. От нее зависит очень сильно точность. Но и то, TL431 дает очень хорошую стабильность. Но опять же их много разных. Я хз какие я использую, брал Б/У с плат питания.
@@Solderingironspb понял, интересно будет посмотреть)
@@Solderingironspb так, если верить datasheet, изменение vrefint во всем рабочем температурном диапазоне 5 mV (STM32G0x0), а у TL431 4mV или 6mV (для разных корпусов, производитель TI), то есть они одного поля ягоды. Но у TL431 максимальное возможное значение гораздо выше - 25mV или 16mV против 7,5mV у контроллера.
Для f103 такого не нашел, но у g030 в datasheet указан адрес в памяти где лежит значение ацп vrefint, которое было измерено на заводе при напряжении питания 3V. По нему мы можем пересчитать напряжение питания, оно же опорное напряжение ацп у многих мк. И разделив его на 4095 получить напряжение одной единицы ацп.
DMA у вас работает в циклическом режиме, как вы получаете 100 кГц. И зачем в прерывание стартуте DMA оно уже работает?
Преобразования АЦП я включаю в прерывании по таймеру программно(External Trigger Conversion Source: Regular Conversion launched by software).
Если выполнить функцию HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_RAW_Data, 2) один раз, то произойдет только одно преобразование и все.
@@Solderingironspb Почему только одно, если DMA работает циклически ?
Запустите проект и тестаните.
хмм... странно... а куда комментарии пропадают с конструктивной критикой?
ведь библиотека не идеальна. зачем народ вводите в заблуждение?
Что?) я ничего не удаляю. Если что-то пропадает-это ютуб чистит. Ну либо я уж совсем отморозков убираю, но и то, я их блокирую. Может Вы ссылку прилагали или еще чего. Что не так с либой?
@@Solderingironspb нет. ссылку не прилагал, но там была подкорректированная функция Вашей библиотеки.
Попробую ещё раз отправить разделив коммент на 2 части 🙄
@@Solderingironspb первое что бросилось в глаза:
SMA_Filter_Result = SMA_Filter_Result >> 5u;
Человек качает библиотеку и меняет
#define SMA_FILTER_ORDER 48
Всё. Приехали.
Во вторых - зачем долбать контроллер вызовами прерываний, если выдёргиваем данные только 1 раз из 300? Просто надо настроить нужное время срабатывания прерывания по TIM4.
@@Solderingironspb в третьих - что-то намудрили Вы в функции фильтрации. Нафига что-то смещать?
Просто пишем:
uint16_t SMA_FILTER_Get_Value(uint16_t *SMA_Filter_buffer, uint16_t *RAW_Data) {
static uint32_t SMA_Filter_Result;
static uint16_t index_raw_buff;
index_raw_buff++; if (index_raw_buff>SMA_FILTER_ORDER) { index_raw_buff=0; }
SMA_Filter_buffer[index_raw_buff] = *RAW_Data;
SMA_Filter_Result = 0;
for (uint16_t i = 0; i < SMA_FILTER_ORDER; i++) {
SMA_Filter_Result += SMA_Filter_buffer[i];
}
return SMA_Filter_Result/SMA_FILTER_ORDER;
}
Чет я туплю...а как static uint16_t index_raw_buff; будет влиять на разные буферы?
допустим мы вызовем функцию 2 раза, но в 1 раз первый канал АЦП со своим буфером, а во второй раз 2 канал АЦП со своим буфером.
есть строка index_raw_buff++;
после него SMA_Filter_buffer[index_raw_buff] = *RAW_Data;
получится же так, что в первый вызов мы положим в 1 элемент 1 буфера 1 канал АЦП, а при втором вызове у нас static uint16_t index_raw_buff уже будет равна 1 и мы тогда положим во 2 элемент 2 буфера 2 канал АЦП данные, а в первом элементе уже будет пусто...
Или я что-то неправильно прикинул?
т.е. вызовов много, а счетчик индекса один.
Прошу прощения, если что - утро, туплю)
Первое что бросилось в глаза:
SMA_Filter_Result = SMA_Filter_Result >> 5u;
Человек качает библиотеку и меняет
#define SMA_FILTER_ORDER 48
Всё. Приехали.
Во вторых - зачем долбать контроллер вызовами прерываний, если выдёргиваем данные только 1 из 300? Просто настраиваем время срабатывания прерывания по TIM4.
В третьих - что-то намудрили Вы в функции фильтрации. Нафига что-то смещать?
Просто пишем:
uint16_t SMA_FILTER_Get_Value(uint16_t *SMA_Filter_buffer, uint16_t *RAW_Data) {
static uint32_t SMA_Filter_Result;
static uint16_t index_raw_buff;
index_raw_buff++; if (index_raw_buff>SMA_FILTER_ORDER) { index_raw_buff=0; }
SMA_Filter_buffer[index_raw_buff] = *RAW_Data;
SMA_Filter_Result = 0;
for (uint16_t i = 0; i < SMA_FILTER_ORDER; i++) {
SMA_Filter_Result += SMA_Filter_buffer[i];
}
return SMA_Filter_Result/SMA_FILTER_ORDER;
}
В своем примере вы используете одну из самых дорогих операций - деление (особенно на младших кортексах без аппаратного деления), в отличие от предлагаемого автором сдвига, выполняемого гораздо быстрее.
@@PavelLaminar у автора библиотека в таком случае не работает с тем функционалом о котором он заявляет. Я указал на реальный косяк.
@@sozdatelEd никакого косяка нет, несовершенство реализации - да. Самое простое определить через define сдвиг. Еще можно магию на макросах написать.
@@PavelLaminar т.е. вы считаете не является косяком то, что автор вынес в define размер буффера для усреднения, а в реализации функции присутствует магический сдвиг? Или вы пользуетесь библиотеками где надо и в конфигурационном файле поменять значения и в файле с реализацией функций тоже найти магические комментарии где может быть будет понятно почему не взлетело после изменений значений в конфиге?
К чёрту подробности. Пруфы в студию.
А лучше разберите этот код на двух примерах:
1. скачана библиотека и изменил буффер на 16
2. скачана библиотека и изменил буффер на 48
Вы точно прочитали и поняли мой предыдущих ответ? Я не спорю, что магические числа и прибитые гвоздями реализации это никуда не годится, но это говорит только о плохой реализации. Косяк это когда задаешь размер буфера 10, а получаешь 5 к примеру. Да и вообще я изначально ответил почему вариант с делением хуже сдвига