В статье будет показано несколько методов борьбы с этими дрожаниями, начиная от самых простых (которые в подавляющем большинстве случаев достаточны), через более сложные и вплоть до алгоритма, придуманного специально для недавнего проекта. Наверняка, я уже 100500-й, кто такой алгоритм придумал, но после опроса знакомых, оказалось, что в списке тех, кто его собирается опубликовать, мой номер значительно меньше. Среди типовых он не находится. Возможно, кому-то он тоже окажется полезным.
Введение
Как обычно, намечалась рутинная работа. Меня попросили помочь коллеге устранить несколько мелких проблем в его проекте. Он делал «прошивку» для платы на базе процессора AtMega… Да, процессор староват. Но тут мы были просто подрядчиками, нам заказали разработать «прошивку», а к железу мы не имели никакого отношения. Так что Мега, значит Мега.
Устройство на базе Меги управляет неким шпинделем. В нормальном режиме работы требуемые обороты задаются в цифровом виде через мобильное приложение. Но иногда требуется переключиться на задание оборотов при помощи потенциометра. Угол поворота ручки задаёт требуемую скорость вращения.
И вот данные с этого потенциометра, считанные «прошивкой», дрожали. В целом… Шпиндель достаточно инертен. Все эти дрожания на плюс-минус несколько оборотов в минуту, ему не сделают никакой погоды. Мало того, измерение этих оборотов имеет намного большую погрешность, чем та самая ошибка считанных с потенциометра данных. Вроде, и ненужная работа намечалась, если бы не одно «но»: эти требуемые обороты также отображаются всё в том же мобильном приложении.
Любому уважающему себя пользователю категорически не нравится, когда какие-то показания дрожат. Даже я, когда у меня заканчивается работа, и я перехожу из программистов в пользователи, начинаю сильно возмущаться, видя подобные дрожания. Правда, когда снова начинаю работать – не менее рьяно доказываю, что не так это и страшно…
В общем, надо было убрать те мелкие дрожания значений на экране, которые зависят от положения ручки потенциометра. Ну, и сделать так, чтобы в оборудование уходили эти стабильные значения.
Уже по тому факту, что некоторые программисты просто читают показания АЦП и сразу пользуются ими, можно было бы написать короткую поучительную статью на тему «Почему нельзя просто читать аналоговые показания, почему их надо ещё и усреднять». Но реальность внесла свои коррективы. Простое усреднение не принесло желаемых результатов. Поэтому статья получилась не короткая. И она будет перечислять действия, которые можно предпринять, по мере их усложнения. В зависимости от обстоятельств, кто-то сможет остановиться на одном из описанных шагов, и следующие ему уже не потребуются. Но вот мне пришлось пройти все, причём последним шагом была разработка довольно забавного алгоритма фильтрации. Наверняка не я первый, кто его придумал, но мне кажется, что он должен валяться на каждом углу, чтобы разработчики «прошивок» могли пользоваться либо им, либо чем-то подобным, если вдруг это потребуется.
Итак, переходим к самому простому, но стопроцентно необходимому шагу – усреднению аналоговых значений. Без него не обойтись. О нём обязан знать каждый.
Усреднение значений
Философы говорят, что нельзя войти в одну реку дважды. Мы, программисты, вполне можем сказать, что нельзя считать одно и то же значение из АЦП дважды. Аналоговый мир полон шумов и помех. Давайте я покажу шум на линии, идущей от термодатчика, который я когда-то ловил на своём 3D-принтере. Всё-таки это мой личный прибор, хочу и публикую его осциллограммы, а много снимков с реального Изделия я публиковать не рискну. Вот этот шум:
Любые данные, считанные напрямую со входа АЦП, будут шуметь примерно так же. Они всё время плавают вокруг какого-то среднего значения. Так что это только блогеры, быстренько освоив чтение данных с АЦП, пишут, что «вот так мы будем читать потенциометр на Ардуинке», и скорей бегут строчить другие записи в блоге. На самом деле, хрестоматийным решением будет усреднять эти считанные значения. Решение настолько хрестоматийно, что многие производители контроллеров встраивают функцию усреднения значений прямо в АЦП. Вот так выглядит фрагмент первой попавшейся под руку документации на STM32 (я выделил слово «усреднение»):
Производители других современных контроллеров не отстают… Но в проекте использовалась весьма древняя по своей архитектуре AtMega. Там усреднение надо делать программно.
Я попробовал сначала усреднять 8 показаний. Вышло маловато. Потом 16. Тоже маловато. Перед тем, как начать говорить о количестве усредняемых отсчётов, существенно превышающих эти величины, сначала рассмотрим более продвинутый метод фильтрации.
Медианная фильтрация
На самом деле, простое усреднение хоть и хорошо, но не решает некоторых проблем. Давайте я покажу полную осциллограмму, считанную с термодатчика 3D-принтера:
Видите ту «иголку»? Откуда она берётся, я расскажу чуть позже. А пока просто примем за должное, что если начать усреднять в её районе, то ничего хорошего не получим. Один мой коллега продвигает идею медианной фильтрации. При ней мы сначала отбрасываем максимум и минимум из накопленного массива, а уже оставшиеся значения усредняем. Если разрешение будет таким, что «иголка» внесёт всего один вклад, всё отфильтруется в лучшем виде, она просто будет отброшена.
Ну, или я как-то делал двухпроходный фильтр. Сначала вычислял среднее, затем – отбрасывал те значения, которые отстояли от него более, чем на определённый процент, после чего усреднял оставшееся. Там отбрасывались массово влезшие «иголки».
В общем, нужна фильтрация, при которой всё, что сильно отличается от среднего, отбрасывается, а усредняется только то, что осталось. А уж как этого добиться — каждый может подойти к этой задаче творчески.
Большинству читателей этого может хватить, и они на этом смогут остановиться, но мне это не подошло. В первую очередь, вот сколько иголок было на реальной системе (я закрыл вход для постоянной составляющей и увеличил чувствительность осциллографа, чтобы помехи было лучше видно):
Это просто ёжик какой-то! Чтобы хоть что-то усреднить, надо отснять сотни показаний. В итоге, я остановился на двухстах пятидесяти. Под них надо выделить 250 слов, то есть, 500 байт. Напомню, у нас весьма скромный контроллер! Причём скромен он не только по объёмам ОЗУ и не только по быстродействию. У него ещё и восьмибитное АЛУ, так что работа по усреднению большого количества шестнадцатибитных значений превратится в те ещё вычислительные задачи. Поэтому я пошёл другим путём.
Я решил усреднять всё на лету. Запустил АЦП в режиме Free Running, а в обработчике прерывания копил данные не в массив, а в одну переменную. Как насуммировал 250 значений, так разом эту сумму на 250 и поделил:
ISR(ADC_vect)
{
static uint32_t avg_buf = 0;
static uint8_t avg_cnt = 0;
avg_buf += ADCW;
avg_cnt += 1;
if (avg_cnt == ADC_AVERAGE_CNT)
{
lastAvgValue = (uint16_t)(avg_buf / ADC_AVERAGE_CNT);
avg_buf = 0;
avg_cnt = 0;
}
}
Я измерил при помощи осциллографа: при моих настройках АЦП переменная lastAvgValue обновляется примерно 10 раз в секунду. Честно говоря, на основе расчётов, ожидал я не 10, а 50, но, когда просто улучшаешь работающий проект, лучше не лезть в недра, чтобы не испортить какие-нибудь таймауты. Так что десять, и ладно. Приемлемая цифра. Жаль только, что даже при таком усреднении, всё стало намного лучше, но осталось на неприемлемом уровне.
А она всё равно дёргается
Да, стало всё существенно лучше, но показания стояли не как вкопанные. Они всё равно периодически «дышали». Взлетали вверх, потом, постояв некоторое время там, падали вниз. Причём дело явно не в ошибках реализации усреднения. Переключим осциллограф на другую развёртку:
Сигнал достаточно медленно гуляет вверх-вниз. Понятно, что, когда усредняешь период 100 мс, в пределах периода всё будет хорошо. Но у разных периодов средние значения будут разными. Усреднять по большему промежутку? Тогда данные в мобильном приложении будут «замирать». Заказчик начинает нервничать уже не потому, что всё прыгает, а потому, что он крутит ручку, а реакции никакой.
Чтобы решить проблему, было решено делать усреднение в два этапа. Тот самый период 100 мс усреднять описанным выше способом, а полученные значения уже складывать в кольцевой буфер. И в любой произвольный момент времени выдавать среднее значение для того, что накопилось в буфере. То есть, сначала усредняем с «замиранием» процесса кусочки, длительность которых не ощущается человеком, а уже их усредняем, складывая в буфер, так что тут, с точки зрения пользователя, всё происходит «на лету». Буфер же получается небольшой, я остановился на хранении пяти последних отсчётов. Обработчик прерывания стал выглядеть так:
#define vspAvgCnt 5
uint16_t secondVspAverager [vspAvgCnt];
…
ISR(ADC_vect)
{
static uint8_t secondAvgPtr = 0;
static uint32_t avg_buf = 0;
static uint8_t avg_cnt = 0;
avg_buf += ADCW;
avg_cnt += 1;
if (avg_cnt == ADC_AVERAGE_CNT)
{
lastAvgValue = (uint16_t)(avg_buf / ADC_AVERAGE_CNT);
avg_buf = 0;
avg_cnt = 0;
secondVspAverager [secondAvgPtr++] = lastAvgValue;
if (secondAvgPtr == vspAvgCnt)
{
secondAvgPtr = 0;
}
}
}
Стало лучше, но не идеально. Но перед тем, как рассказать о придуманном третьем этапе фильтрации, я сначала остужу пыл тех, что уже кипит от возмущения: «Вечно эти программисты делают какую-то ерунду, когда можно всё зло зарубить на корню» … Действительно, зачем устранять то, чего можно избежать? Поэтому поговорим о том, что можно сделать на аппаратном уровне… И почему в нашем случае, это не было сделано…
Добавляем фильтр сигнала
Очень интересный случай был у меня, когда я гасил ту самую помеху от термодатчика в 3D-принтере. Как работает тот термодатчик? Давайте посмотрим схему древней, но зато классической платы RAMPS. Схемы всех прочих плат основывались на тех же самых принципах (я специально использую прошедшее время, но почему – расскажу чуть позже).
Что это за резисторы такие, и как они работают? Вспоминается анекдот, где идут два мужика, один ямку выкапывает, второй – закапывает… Их спрашивают: «Что вы делаете? Почему один выкапывает ямку, второй закапывает?». Второй и говорит: «Я не второй, я третий, второй должен в ямку дерево сажать, но он заболел». Вот и тут та же история. Перед нами только половина схемы. Вторая половина расположена не на плате, а на головке. Там к соответствующей линии подключён термистор, идущий на землю. Получается такая схема:
Пара резисторов R7 и Rt образуют классический делитель напряжения, коэффициент деления которого вычисляется по известной формуле
В зависимости от температуры головки, сопротивление Rt будет изменяться, а значит – напряжение Vcc будет делиться на некий коэффициент. Измерив это напряжение, по формулам мы можем вычислить температуру. Ура? В целом, да, но в частности, в первых 3D-принтерах показания температуры были весьма нестабильными, хоть значения с АЦП вполне себе усреднялись. Почему? О-о-о-о-о! Когда я это понял, то сразу всё исправил. Помните ту самую иголку?
Знаете, откуда она берётся? Давайте в схеме я отмечу один провод красным
В классическом 3D-принтере этот провод идёт от платы к головке в толстом шлейфе. Длина шлейфа у Дельты может достигать метра. Ну, или близко к тому. А что ещё идёт в этом же шлейфе? Во-первых, кабели к нагревателю. А по ним, извините, передаётся минимум двенадцативольтовый ШИМ-сигнал. Там постоянно идут то положительные, то отрицательные прямоугольные фронты высокой амплитуды. Дальше, там идёт четыре провода к шаговому двигателю. В них фронты не такие крутые, но всё же… Как минимум, они тоже высоковольтные. Но если честно, нам и ШИМа к нагревателю хватит.
И вот все эти провода идут одним длинным жгутом. А что такое жгут? Это конденсатор, в котором провода – это обкладки. Дёрганья силовых проводов и наводятся на нежный выход с термистора через емкостную связь. Вот откуда эти иголки лезут!
Поняв это, я просто добавил небольшой RC-фильтр. Для этого, около платы поставил низкоомный резистор. Такой, чтобы он гасил помеху, но не вносил существенного изменения в коэффициент деления. Ну, и небольшой конденсатор добавил, чтобы линия уж совсем не заводилась. Слева конденсатор и так уже имеется.
Теперь в длинном кабеле помеха как была, так и осталась, но на АЦП сигнал стал приходить уже отфильтрованный. Показания температуры застабилизировались сами собой, без внесения изменений в «прошивку».
Правда, это всё относится к красивым воспоминаниям. Сегодня вместо толстенного шлейфа с кучей источников помех, идут два провода цифровой шины CAN. Это решение создаёт новые проблемы, но обсуждаемую проблему устраняет идеально. Аналоговые и силовые линии стали короткими и разнесёнными, эффект конденсатора пропал, наводки стали минимальными.
Однако подумать об аппаратной фильтрации всегда полезно. В случае того железа, с которым шла работы (но фрагменты схемы которого я не могу публиковать, как бы ни были они просты), там не было длинной линии, которую имело бы смысл отрывать. Оба резистора там расположены прямо на плате, рядом друг с другом, так что описанная фильтрация там получается сама собой. Но возможно, помогло бы увеличение ёмкости конденсатора. Это в 3D-принтере стоит кондёр на 10 мкф, а в изделии – жалкие 100 нФ.
Жаль, узнать о том, помогла бы замена конденсатора или нет, мы не сможем. Те представители Заказчика, с которыми мы общались, явно не рисовали ту схему. Не факт, что они вообще понимали, чего мы от них хотим, когда вносим такие предложения. Но оставим все домыслы за рамками статьи. Просто укажем, что, когда программист может влиять на схему, он должен посмотреть, не может ли он добавить фильтрацию на аппаратном уровне. То ли, увеличив ёмкость фильтрующего конденсатора, то ли посмотрев, а нет ли в схеме неявного источника помех, который можно было бы задавить, внеся какие-либо несложные доработки. И может оказаться, что после этого править код и не придётся (если в нём уже применено усреднение, разумеется, без него никуда).
Дайте мне точку опоры, и я переверну мир
Работая с аналого-цифровым преобразователем, всегда полезно помнить, что на самом деле, это набор, состоящий из цифро-аналогового преобразователя, компаратора и управляющей логики. Вот эта троица в документации на AtMega:
На первых курсах нас заставляли изучать методы поиска пересечения монотонных функций, сравнивая их по принципу «больше-меньше». Мы делали лабораторные сначала при помощи метода половинного деления, потом – методами, которые обеспечивают как можно меньшее количество сравнений. Вот АЦП – это как раз пример того, что та задача имеет вполне себе прикладной смысл.
Входной сигнал подаётся на вход компаратора «+». В идеале, он должен там зафиксироваться на всё время преобразования (судя по комментарию на рисунке, так оно и есть). А дальше, при помощи ЦАП, формируется ряд напряжений, подаваемых на вход компаратора «-». По результату «Больше» или «Меньше» (где-то там добавляется «или равно», но это более низкий уровень, чем тот, на котором мы сейчас работаем, так что не будем даже разбираться, где именно) делаются выводы, в каком районе находится наш сигнал. Постепенно, логика выясняет то единственное напряжение на выходе ЦАП, которое соответствует напряжению на входе.
Теперь поговорим о том, что такое ЦАП. Это всего лишь управляемый многоразрядный делитель напряжения. Ему на вход подаётся опорное напряжение и коэффициент, на который оное следует поделить. В результате, на выходе получается уровень, пропорциональный опорному. Теперь нам уже не будет страшно, если я покажу чуть больший фрагмент структурной схемы АЦП.
Красная линия – это как раз путь опорного напряжения для ЦАП. Но в контроллере AtMega оно может быть получено совершенно разными путями, на выбор программиста. Можно просто закрыть транзистор, обеспечивающий жёлтый путь, и подать совершенно произвольное напряжение на ножку AREF. Тогда это напряжение войдёт по синему пути и перейдёт на красный. Про этот путь мы ещё поговорим в будущем.
Можно скоммутировать всё так, что напряжение аналогового питания AVCC (зелёный путь) пройдёт на жёлтый, а потом – разбежится и на вход ЦАП (красный путь), и на ножку AREF. Зачем так сложно? Просто в этом случае, на ножку AREF можно припаять фильтрующий конденсатор, а мы уже выяснили, что конденсаторы – это хорошо, они обеспечивают стабильность. Поэтому производитель обеспечил нам такую возможность. Если на AREF ничего не подаётся извне, она используется для подключения к внешним конденсаторам. В документации это показано так:
Ну, и наконец, путь от пурпурного на жёлтый с последующим разбегом на красный и синий – это сформированный высокоточным опорным источником уровень 1.1 или 2.56 вольта. Константа 2.56 выбрана не случайно. Она хорошо делится на 256, ну и на последующие степени двойки. Тогда нам, программистам, будет удобно пересчитывать милливольты в единицы, считанные из АЦП, и обратно.
Целевая плата сделана так, что вариантов нет. Ножка AREF припаяна чисто к фильтрующему конденсатору, так что на неё точно ничего опорного снаружи не подаётся. Ну, а входной делитель может пропускать напряжение, сильно превышающее 2.56 вольта. Остаётся только использовать в качестве опорного напряжение 5 вольт, имеющееся на входе AVСС. И вот здесь возникает целый ряд проблем. Давайте посмотрим на эквивалентную схему узла на плате:
Напряжение для входа АЦП получается путём двойного деления (сначала на потенциометре затем – на делителе R1R2) уровня 12 вольт. Откуда берётся этот уровень? Конечно же, с импульсного стабилизатора. У которого, конечно же, есть масса шумов и ошибка задания напряжения, вызванная погрешностями элементов в его обратной связи (что это такое – выходит за рамки статьи). А опорное напряжение для AVCC формируется другим импульсным стабилизатором, у которого есть свои помехи и своя ошибка выходного напряжения. Каждые помехи пляшут по-своему, а суммарная ошибка напряжений такая, что приходится максимальный уровень на АЦП для каждой платы сохранять в EEPROM.
Как я уже говорил, играть со схемой нам не довелось, но вот нутром чую, что вот такой вариант был бы поинтереснее:
Это тот самый сине-красный путь при отключённой жёлтой ветке, к которому я обещал вернуться. Здесь напряжение с потенциометра (идущее на вход АЦП) и опорное напряжение для АЦП формируются от одного источника. Иголки такая схема не отфильтрует (ЦАП же получает не зафиксированное опорное напряжение), а вот медленные колебания уровня, которые были показаны в предыдущем разделе – вполне могут оказаться для этой схемы невидимыми. Входное напряжение и опорное будут колебаться одновременно (лишь бы в пределах одного цикла измерения всё было более-менее стабильно). Ну, и все постоянные ошибки будут вызваны только погрешностями резисторов. Как минимум, эту калибровку сохранять в EEPROM не придётся.
Жаль, что в нашем случае, это всё было неприемлемо. Но те программисты, которые работают в содружестве со схемотехниками, вполне могут попытаться устранить проблему на аппаратном уровне. А что не устранится, то уже может быть добито простыми методами, описанными выше, без погружения в недра алгоритмов. Но увы. У нас схему править было нельзя, а простые программные методы хоть и существенно улучшили картину, но не решили проблему полностью, поэтому пришлось идти дальше.
Добавляем гистерезис
Подозреваю, что некоторые читатели уже устали разбираться в методах стабилизации показаний какого-то вшивого потенциометра… А уж я-то как устал, когда всё это проверял на практике! К этому месту я тоже был уже настроен решительно устранить помеху, не особо с нею церемонясь. Как мне верно подсказали ниже в комментариях, я решил добавить в систему гистерезис (сам я, каюсь, забыл про это слово в пылу борьбы с железом).
Давайте посмотрим на проблему с точки зрения пользователя. Вот у нас есть переменный резистор, который может совершать даже не целый оборот. И всё это делится чуть менее, чем на тысячу значений (в реальности, с имеющимися делителями и стабилизаторами, я видел значения на выходе АЦП от 93 до 930). Малейшее изменение угла ручки, даст существенное изменение значения. Не надо бояться и биться за каждую единицу на выходе АЦП! Правда не стоит и тупо отбрасывать младшие биты, там уже возникнут проблемы с разрешением. Надо анализировать все биты, но учитывать показания, только если значения изменились существенно.
Поэтому давайте к первым двум ступеням фильтрации, добавим третью. Она будет звучать так: «Если значение отклонилось вниз или вверх от текущего менее, чем на пять единиц, то это просто шум, оставляем предыдущее значение, а если отклонилось более, чем на эту величину – примем новое значение за новое положение ручки».
Идея оказалась вполне себе жизнеспособной, если не считать одной мелочи. Если мы угадали и встали строго по центру шумящего участка, мы выиграли. Значения колеблются, мы не выходим за коридор, удерживая это среднее положение в качестве текущего.
Но если мы совершенно случайно один раз попали рядом с границей, то начинаются скачки на плюс-минус пять. Нам нельзя прыгать сразу на большие величины, надо ещё и двигаться более плавно, чтобы постепенно нащупывать то самое центральное значение, к которому стоит стремиться.
Тогда я добавил ещё одно правило: «Если же напряжение отклонилось более чем на плюс-минус 4, но менее чем на плюс-минус 10, то считаем, что у нас на входе не это напряжение, а среднее между ним и предыдущим центральным». Благодаря такому правилу, система достаточно быстро находит центральную точку, и снова приходит в покой.
Алгоритмически, в код добавился следующий обработчик, расположенный в задаче, активирующейся пять раз в секунду… Кстати, в том проекте применён очень красивый планировщик с кооперативной многозадачностью и единым стеком. Самое то для восьмибитных процессоров, вместо ресурсоёмких вытесняющих планировщиков!
// Вторичное усреднение
uint32_t adcSum = 0;
for (uint8_t i=0;i<vspAvgCnt;i++)
{
adcSum += secondVspAverager [i];
}
uint16_t curVspValue = (uint16_t)(adcSum/vspAvgCnt);
// Третий уровень – фильтрация.
// Если находимся внутри малого диапазона
if ((curVspValue>=lastVSPvalue-4)&&(curVspValue<=lastVSPvalue+4))
{
// То оставляем значение на месте
curVspValue = lastVSPvalue;
// Если находимся внутри большого диапазона
} else if ((curVspValue>=lastVSPvalue-10)&&(curVspValue<=lastVSPvalue+10))
{
// То новое значение будет усреднено с предыдущим
curVspValue = (lastVSPvalue+curVspValue)/2;
}
// Если вылетели за диапазон – берём без обработки
lastVSPvalue = curVspValue;
Заключение
Работа с аналоговыми величинами – это нечто чуть более сложное, чем просто «считать АЦП». К счастью, чаще всего достаточно считать значение несколько раз и усреднить результат. Но иногда приходится делать какие-либо, более сложные действия. Знание сути работы АЦП, а также применение проблемно ориентированных алгоритмов, позволяет сделать всё с минимальным потреблением системных ресурсов ради вспомогательных задач (которых и так вечно не хватает для реализации основного функционала).
Комментарии (130)
YMA
14.11.2024 09:10Ещё можно дополнить алгоритм с окошками привязкой к реальности - крутануть потенциометр как можно быстрее и определить максимально возможную скорость изменения параметра. В случае, если параметр на входе меняется быстрее возможного - это повод для фильтрации или даже для алярмы (может, коротнуло или провод оторвался?) ;)
Vdm_ro
14.11.2024 09:10если вы делаете прошивку - вы к ней имеете доступ, что мешало програмно убрать из значений дребезжащий хвост?
ну то есть: у вас потенциометр 0-10 кОм(надеюсь 10 оборотный, а не фигня "попробуй попади в значение")
итого значения от 0 до 10000 +-99, обычно там дребезжание доли Ома, но хз как у вас там, пусть до 100 Ом, да и сами говорите, что запас по точности там есть
Value = датчик (0-10000)
Value = Value/100 (при условии, что это инт без дробной части)
Value=Value*100
Rezult = Value
Вот ваши 100% переменного значения, без дребезга... хоть в мониторинг на телефон выводи хоть в скаду.
Или я что-то не понимаю?
ИМХО: усреднять нужно приходящие переменные значения, в которых есть полезный сигнал(тот самый всплеск), когда переменная часть - ненужный мусор - его можно просто вынести....
EasyLy Автор
14.11.2024 09:10итого значения от 0 до 10000 +-99
В текста сказано, что значения были от 93 до 930 (вообще, у Меги АЦП 10 битный, выше, чем 1023 не может быть в принципе, но в конкретной схеме не приходило более, чем 930, проверено опытным путём, схему менять нельзя, это тоже указано в тексте). И что не хотелось терять точность. Поэтому среднее значение мы искали очень точное, просто вокруг него не давали прыгать, если было видно, что ручка не пришла в движение. Пришла - снова ищем точное среднее.
Vdm_ro
14.11.2024 09:10Ну хорошо, убираем "хвосты" с крайних положений потенциометра, итого значения от 100 до 900. На какие величины был дребезг? больше 8 единиц? И с какой точностью в этих же единицах пользователь выставляет значение?
EasyLy Автор
14.11.2024 09:10Нет, не больше. Ну вот, у нас показания равны, скажем, 567, дрожат на плюс-минус 8. Я очень боюсь переврать Ваш алгоритм, но мне кажется, что Вы просто предлагаете округлять. То есть, терять точность.
Vdm_ro
14.11.2024 09:10именно, терять точность, которая никому не нужна....
Шпиндель достаточно инертен. Все эти дрожания на плюс-минус несколько оборотов в минуту, ему не сделают никакой погоды. Мало того, измерение этих оборотов имеет намного большую погрешность, чем та самая ошибка считанных с потенциометра данных. Вроде, и ненужная работа намечалась, если бы не одно «но»: эти требуемые обороты также отображаются всё в том же мобильном приложении.
Вот мне и интересно, почему бы эту самую точность не привести к единицам измерения, которые действительно нужны...
Пусть шпиндель вращается на 3000 оборотов, шаг задания скорости вращения в 50 оборотов за шаг - более чем достаточно, скорее там и 100 на шаг - всех устроит.
Я с двигателями не работал, вот пытаюсь понять специфику - какая же точность позиционирования там нужна, что приходится усреднять дребезг потенциометра...
EasyLy Автор
14.11.2024 09:10Ну поделим мы на 10. У нас останется 90 измеряемых положений потенциометра... Это даже не по 2 градуса на шаг. Может даже по 3.
Единицы измерения реального шпинделя сейчас обсуждать не будем. Тем более, что они там меняются от модели к модели. Просто обсудим именно положения ручки, которые можно задать. 900 положений и 90.
В найденном алгоритме, вся точность сохранена, просто добавлен, как верно отметили в комментариях ниже, гистерезис. Поймали среднее положение и замерли в нём. Крутанули туда-сюда - за несколько итераций (незаметных глазу человека) найдём новое среднее положение с максимально возможной точностью. И снова замрём.
Gredko
14.11.2024 09:10А что мешает отбрасывать отсчёты АЦП, изменяющиеся на 8 единиц?
Цель же - зафиксировать статическое положение потенциометра как я понимаю?
Зачем тогда усреднять всплески?
Они же не несут полезной информации.
1.Фиксируем в регистре текущее значение.
2.Заполняем массив очередными отсчетами АЦП.
3.Вычисляем среднее значение и отклонение от среднего в массиве.
4. Если отклонение от среднего выше заданной величины (5 единиц к примеру) - бракуем замер и оставляем сохранённое в регистре значение.
5.Если отклонение от среднего ниже заданной планки - сохраняем среднее в регистре как текущее.
"Выбор метода усреднения шумов" - так себе технология ;)
Лучше подождать пока вал потенциометра перестанут вертеть, а потом уж мерить...
EasyLy Автор
14.11.2024 09:103.Вычисляем среднее значение и отклонение от среднего в массиве.
Да, но по условию задачи, у нас медленный восьмибитный процессор с сильно ограниченным ОЗУ. А задача стабилизации положения потенциометра настолько вспомогательная, что тратить на неё массу дефицитных ресурсов (которые нужны на задачи первого и второго приоритета) совсем не хочется. Под массив нужно ОЗУ и время на его обработку.
Лучше подождать пока вал потенциометра перестанут вертеть, а потом уж мерить...
Была такая претензия: "Если сдвинуть потенциометр, то потом всё замирает". Это у основного разработчика (меня привлекали только помочь устранить проблему) раз в секунду всё опрашивалось. При этом задержку на начало вращения Заказчик, как оказалось, не замечал, а вот что потом нет реакции - замечал. Так что как минимум, при приёмке, вал крутили нещадно, и были недовольны, что это не отражается на лету. Уже поэтому так не получилось бы.
Gredko
14.11.2024 09:10Желание потрафить заказчику в ущерб функционалу - такой себе пример для подражания... Все мы колхозим иногда, но зачем писать статью об этом?
EasyLy Автор
14.11.2024 09:10Как видно из комментариев, все сделанные шаги - типовые. Это не колхоз, а стандартный путь решения. Усреднение, фильтрация, гистерезис. А писать... Вот как раз многие носители знаний этим вопросом задаются, потом приходится велосипеды изобретать. И только на такие статьи сходятся комментаторы, которые говорят, мол, а чего такого? Вот три шага, всё же ясно...
Им ясно, но не опубликовано... Поэтому ясно ограниченному кругу лиц. Теперь - опубликовано...
aamonster
14.11.2024 09:10Листал статью, ожидая увидеть слово "гистерезис". Не увидел, озадачился.
Если вам надо, чтобы пока ручку потенциометра не крутят, показания стояли, как вкопанные – это и надо было делать. Обнаружили по сигналу, что ручку никто не крутит – зафиксировали значение.
EasyLy Автор
14.11.2024 09:10Так а у меня что сделано? Тут главное - не забыть, что ручка - вообще несерьёзная мелочь в рамках проекта. Что не стоит класть все силы разработчиков, всё ОЗУ, всё ПЗУ и все такты процессора на её обработку.
Результирующий алгоритм получился в экран размером. И по сути, это как раз он, гистерезис. Просто на фоне битвы, я про это слово забыл. Спасибо за напоминание!
Int_13h
14.11.2024 09:10"Заказчик крутит ручку а реакции никакой..."
А вот такой вопрос, частота опроса АЦП (семплирования) какая? Для вялотекущего процесса чем реже опрос - тем лучше, это уже будет ФНЧ, дальше в скользящее среднее, а для редкого опроса хватит уже и 5...10 точек ( и ведь тоже ФНЧ!) и еще улучшили характеристики.
EasyLy Автор
14.11.2024 09:10Ну, я в тексте показал, что 250 чтений происходят за 100 мс. То есть, чтения идут с частотой 2.5 КГц. Там на одно измерение тратится 13 тактов. Значит, тактовая у АЦП 35.5 КГц. У меня выбрана самая большая константа деления, какую только можно поставить в Меге - 128.
Если бы наши проекты не были ограничены по времени, там можно было бы ещё поисследовать, как влияет тактовая частота АЦП на точность в условиях, когда и измеряемое, и опорное напряжения вырабатываются импульсниками (то есть шумят, причём каждое по-своему). Но к сожалению, и так-то косо смотрят, что сроки затянуты были.
Int_13h
14.11.2024 09:10Не надо так часто опрашивать АЦП! Допустим, нам достаточно, чтобы показания менялись раз в 0.5 секунды (можно в 1 сек, но уже заказчик может занервничать), и делаем скользящее среднее из 5 значений - тогда опрашиваем АЦП раз в 0.1 секунду = 10 Гц. А у Вас в 250 раз чаще - но задача не требует такой высокой частоты оцифровки.
EasyLy Автор
14.11.2024 09:10При тех шумах и иголках, получим такое дёрганье, что ууууууу! Я начинал с малого числа опросов. Частоту АЦП ещё сильнее уменьшить нельзя. Коэффициент деления - максимальный.
А ещё меня смущает то, что в блок-схеме АЦП значение на входе компаратора отмечено, как фиксирующееся, а вот опорное AREF - нет. Считается, что его-то должны идеально застабилизировать и так. Но в нашем конкретном случае, не факт, что это было сделано идеально у разработчиков схемы. Поэтому если ещё уменьшить частоту, там уже ошибки оцифровки могут полезть.
Int_13h
14.11.2024 09:10Частота преобразования АЦП != частоте опроса АЦП. Опрашивается в цикле по таймеру, хоть раз в сутки.
Частота преобразования определяет быстродействие, как быстро мы получим результат после запуска процесса АЦП.
Частота опроса - это как часто мы хотим снимать с АЦП семплы (отсчеты и т.д.), например мы звук цифруем - 22 кГц частоту семплирования берем. Температуру в комнате - 1 Гц, нам чаще меняющиеся данные неинтересны. Так и с резистором, юзер его крутит и ждет что за 1 секунду он изменит свое значение. И мы получим это значение раз в секунду. И не надо усреднять 250 отсчетов,это избыточно.
EasyLy Автор
14.11.2024 09:10Я опрашивал АЦП редко. В конкретной системе, всё было очень кисло. Стал бы я городить огород, если бы всё решилось так просто. Я бы даже щуп осциллографа в руки не взял бы.
Andy_Big
14.11.2024 09:10Не поможет. Попали у вас в одном цикле 3 опроса из 5 на верхние иголки, а в другом на нижние - и здравствуй скачок показаний.
slog2
14.11.2024 09:10У вас очевидно косяки в схемотехнике и разводке платы. Я понимаю когда дрожит 16-24 битный АЦП, но с 10 битным, даже в AVR нет никаких проблем при правильной разводке правильной схемы. Ну может на +/- 1-2 из 1024 дрожит. Для считывания потенциометра вообще не мешает. Работает без всяких фильтраций и усреднений.
Vdm_ro
14.11.2024 09:10Ну там по условиям задачи разводка = дано, ее низзя крутить, так же как и рихтовать значения в мобильном приложении(тут тоже вопрос, но пусть)
EasyLy Автор
14.11.2024 09:10Там ещё есть возможность чтения этого параметра через ModBus, так что всё-таки надёжнее в прошивке. Вдруг при приёмке начнут проверять через неё. Задавить оптом, чтобы точно везде не дёргалось. Мы из этого исходили. Но я решил, что если перечислять все пути, то статья ещё сильнее перегрузится. Её и так многие явно по диагонали читают.
Vdm_ro
14.11.2024 09:10Я ее явно по диагонали читал, не спорю =) Но тут у меня есть оправдание - совсем не интересно было как выводили среднее из значений, был интересен вопрос - а нужна ли была вся эта работа, с разбором схемотехники платы/осцилографом и явно потраченным временем в попытке сохранить незначащие доли показаний. Но если задача была: максимально точное значение от показаний - значит проделанная работа была нужна, вопрос снимается.
EasyLy Автор
14.11.2024 09:10Однозначно косяк, и это отмечено в целых двух разделах статьи. Но не у нас. Как отмечено, мы не имеем отношения к разработке схемы и не имеем возможности влиять на её доработки. Но при этом, нашу (программную) часть проекта бы без стабилизации не приняли.
alexhott
14.11.2024 09:10Что то похожее делал в свое время, но со считываем температуры паяльника с термопары, где милливольты сначала усиливаются, потом на АЦП подаются, тоже помех со всех сторон много.
Раньше понимали проблему и решали чисто аппартно, в статье понимая проблему решили алгоритмом, через пару лет эту задачу будут решать с помощью ИИ, загрузят ЦОД на 50Мвт на пару дней и он им такой-же алгоритм по итогу выдаст.ЗЫ: И кстати в одном кабеле где силовые и сигнальные линии помех больше за счет магнитной индукции а не за счет конденсаторного эффекта.
RTFM13
14.11.2024 09:10Извините, текст очень длинный, воды много, до конца не осилил, а тезисного содержания не нашел. По этому возможно повторю то, что уже написано.
Тезисно:
шорох резистора убираем аппаратно
шум АЦП и питания убираем усреднителем наименее ресурсоёмким алгоритмом.
накладываем гистерезис для стабилизации
Далее подробнее.
Это аппаратная часть, но это будет актуально большинству.
конденсатор стоит не так дорого. RC фильтр НЧ наше всё. Для механической крутилки Тау подбираем порядка 0,1-1 сек. В качестве резистора для фильтра можно ограничиться самой крутилкой.
резистор включаем по схеме делителя. Чтобы сопротивление контакта движка не оказывало существенного влияния.
чтобы убрать шум питания и перетоков можно задействовать Vref+ и Vref- слегка отодвинув их от питания.
Теперь про софт.
Удивился числу 250 в накопителе усреднения. Проще использовать 256 и потом откинуть младший байт. Особенно на чахлых 8-битниках, но не только.
Для более плавной регулировки лучше использовать скользящее среднее.
Если жалко ресурсов на честное скользящее среднее, есть лайвхак.:
При старте после стабилизации входного сигнала считываем значение умножаем на глубину усреднения и записываем в переменную-накопитель. Можно этого не делать, но увеличится время начальной стабилизации показаний.
Далее каждую итерацию к накопителю прибавляем текущее значение АЦП и вычитаем предыдущий результат усреднения. Переменную делим на глубину фильтра и получаем новое значение. Если глубину фильтра выбирать кратной степени 2, то операция деления меняется на сдвиг и можно уложиться в пару десятков тактов на итерацию независимо от глубины фильтра.
Результат работы этого фильтра похож на RC ФНЧ. И никаких массивов с историей. Ресурсоемкость практически не зависит от глубины усреднения, потянет любой контроллер любую глубину и частоту. Актуально не только на древних 8-битниках, но и на устройствах с батарейным питанием, большой загрузкой, когда требуется очень малый отклик и т.п.
Для честного скользящего среднего тоже не надо каждый раз бегать по всему массиву, достаточно из накопителя вычесть самое старое значение и записать на его место и прибавить текущее. Тогда от глубины фильтра будет зависеть только расход памяти на хранение данных, но не будет зависеть процессорное время.
Так-то можно навернуть всякого, например отфильтровать по первой производной (вместо медианы) и т.п. но для механической крутилки этого совершенно точно не требуется (если не надо диагностировать саму крутилку).
Ну и самое главное: после усреднения для стабилизации накладываем гистерезис нужной амплитуды.
Вообще я в своих поделках давно от переменных резисторов ушел к энкодерам.
EasyLy Автор
14.11.2024 09:10Удивился числу 250 в накопителе усреднения. Проще использовать 256 и потом откинуть младший байт. Особенно на чахлых 8-битниках, но не только.
Вы не поверите, но как я ждал этого комментария! Потому что это очевидно, поэтому я думал об этом, и осмысленно отбросил такой вариант. 250 - это "близко к максимальному значению uint8_t". При выходе на 256, пришлось бы или менять счётчик на uint16_t с усложнением операции "+- 1" на каждом шаге, или сравнивать его с нулём, что могло бы ввести в ступор тех, кто потом будет сопровождать код. У нас же восьмибитная архитектура AVR!
Поэтому я предпочёл совместить классический, понятный даже школьнику, код с какой-никакой, а оптимизацией. Одно деление выполнится быстрее, чем загрузка-сложение с переносом-выгрузка, проведённые 256 раз...
достаточно из накопителя вычесть самое старое значение и записать на его место и прибавить текущее.
То есть, хранить весь этот массив в хилой памяти Меги.
Ну и самое главное: после усреднения для стабилизации накладываем гистерезис нужной амплитуды.
Вот как раз гистерезис и достигается в финальном разделе. Да, я забыл это слово в пылу битвы (были там и другие баталии, не описанные в статье), но мне его уже напомнили в комментариях.
Вообще я в своих поделках давно от переменных резисторов ушел к энкодерам.
Золотые слова! Но мы не могли влиять на схему, что отмечено в тексте.
Gryphon88
14.11.2024 09:10При выходе на 256, пришлось бы или менять счётчик на uint16_t с усложнением операции "+- 1" на каждом шаге, или сравнивать его с нулём, что могло бы ввести в ступор тех, кто потом будет сопровождать код.
Можно вместо cnt++ проверять бит переноса, например, с инкрементом через интрисик __builtin_add_overflow.
EasyLy Автор
14.11.2024 09:10Вот я и говорю. Кто потом полезет код сопровождать - много интересных слов в мой адрес скажет.
Взяв константу 250, я выжал не полную, но хоть какую-то оптимизацию, но оставил код универсальным. Если вдруг завтра его будет кто-то читать, ему не понадобится вникать в странные операторы. Мало того, если его соберут под ARM или ещё какой RISCV - он тоже заработает (хоть там все эти переменные, отличные от 32 битных, начнут замедлять процесс - там будет вставляться код, зануляющий лишние биты, есть у меня опыт изучения работы компиляторов под 32 битные архитектуры, когда используются такие укороченные переменные)..
nixtonixto
14.11.2024 09:10Одно деление выполнится быстрее, чем загрузка-сложение с переносом-выгрузка, проведённые 256 раз...
У вас же АТмега а не ПИК, зачем загрузка и выгрузка? У АТмеги 32 регистра, поэтому накапливать результат можно прямо в регистрах. Всего лишь 2 команды и вроде 2 машинных цикла на всё.
250 - это "близко к максимальному значению uint8_t"
Тут не понял. При сложении от 2 до 255 1-байтных чисел результат всегда 2-байтный если вам не нужны переполнения.
EasyLy Автор
14.11.2024 09:10У вас же АТмега а не ПИК, зачем загрузка и выгрузка? У АТмеги 32 регистра, поэтому накапливать результат можно прямо в регистрах. Всего лишь 2 команды и вроде 2 машинных цикла на всё.
Так накопление идёт в обработчике прерывания. Не выделять же два персональных регистра под задачу, которая не является даже второстепенной!
Тут не понял. При сложении от 2 до 255 1-байтных чисел результат всегда 2-байтный если вам не нужны переполнения.
Делить надо, когда счётчик достиг 256. Хотя, да, не подумал, что можно проверять до увеличения счётчика. Тут уже у меня инертность мышления сработала.
nixtonixto
14.11.2024 09:10При сложении 256 чисел, равных 255 - получается 255 255 т.е. всё те же 16 бит.
Вот как раз в прерывании и есть смысл выделить 2 персональных регистра, чтобы сэкономить на стёке и времени обработки прерывания. А у меня во времена АВР вообще вся программа крутилась на регистрах и ОЗУ использовалось в основном только под массивы.
EasyLy Автор
14.11.2024 09:10Вот как раз в прерывании и есть смысл выделить 2 персональных регистра, чтобы сэкономить на стёке и времени обработки прерывания. А у меня во времена АВР вообще вся программа крутилась на регистрах и ОЗУ использовалось в основном только под массивы.
Там в основном алгоритме такие вычисления, что ему регистры не помешают. А стабилизация потенциометра - даже не вторичная задача. При основной работе обороты задаются в цифровом виде через мобильное приложение. И не такие они частые, прерывания от АЦП. А вот данные от сопроцессора и ModBus стекаются всё время, и должны вовремя обсчитываться.
Я же говорю... Главное - не увлечься, и не положить все силы и средства на задачу третьего приоритета... Но без которой проект не приняли бы. Но силы надо рассчитывать.
Traveller0968
14.11.2024 09:10Вообще я в своих поделках давно от переменных резисторов ушел к энкодерам.
Вот вот, не понимаю как это в 21-м веке еще резюки ставят в качестве регуляторов... :)
EasyLy Автор
14.11.2024 09:10Ну, у в ту железку, про которую статья, его поставили те, кто в двадцатые годы 21 века делает схемы на Мегах. Вот в нулевые я лично на них поделки делал, но прогресс не стоит на месте. Схемотехник целевой железки явно так привык с более древних времён. И к тем процессорам, и к той обвязке. И этот схемотехник работает не у нас (и даже не у нас в стране).
slog2
14.11.2024 09:10Зря вы на мегу наезжаете. Нормальные контроллеры. В своё время очень хорошие. Да и сейчас вполне годные для любых проектов. Если бы не неадекватные цены на них и невозможность найти оригинальный, а не китайскую подделку. Я считаю это всё из-за ардуино. Оно же наплодило "разработчиков" способных накидать скетч из готовых библиотек, но не способных правильно подключить резистор к АЦП.
ABRogov
14.11.2024 09:10неадекватные цены на них и невозможность найти оригинальный
Посмотрел на маузере оригинал (понятно) стоит 2 евро за 328 и около 6 за 2560. Обычные вроде цены?
nixtonixto
14.11.2024 09:10Ф030 на 64 кБ - это по ПЗУ аналог 328-й в пересчёте на 32-битность - стоит $0,4 и в нём ещё есть часы, DMA и вытесняющие прерывания. А 8-битные аналоги младших АТтини начинаются от 9 центов.
vvzvlad
14.11.2024 09:10Зря вы на мегу наезжаете. Нормальные контроллеры. В своё время очень хорошие. Да и сейчас вполне годные для любых проектов.
Только очень бедные по ресурсам и очень дорогие. Какой смысл-то за х5 цены получать в десять раз меньше памяти и частоты?
lamer84
14.11.2024 09:10Я не разработчик аппаратуры, но когда-то сталкивался с Мегами, поэтому из чистого любопытства и интереса: а на чем сейчас собирают и какие преимущества/недостатки имеются по сравнению с Мегами?
EasyLy Автор
14.11.2024 09:10Ядра Cortex-M весьма хороши. Cortex M0+ - минимум, Cortex M3 - часто достаточно. Cortext M4 - это уже совсем круто (особенно Cortex M4F), но через это не всегда нужно. Более крутые - дорого, если они нужны - они точно не на замену Меге будут поставлены. Цена M0+ и даже M3 - почти как у Мег, но 32 битная архитектура. Контроллер прерываний покруче.
Дальше уже идут прелести, вытекающие из того, что разработка новее. Средний объём памяти выше при той же цене, тактовая частота может быть повыше, АЦП не 10, а 12 разрядов, у таймеров разрядность и функционал повыше и т.д. и т.п.
Но лично мне они нравятся именно за 32 битную архитектуру. Потому что расчёты обычно идут в таких разрядностях. А у Cortex M4 ещё и поддержка типа float может быть встроенная, что на ряде задач полезно.
Конкретные процессоры не называю, они бывают разные. Та же Atmel делала AT-SAM (сейчас MicroChip их выпускает). STM32... Ну, и куча других производителей, на выбор, включая Миландр. На многих доводилось делать проекты. Все плюс-минус одинаковы.
Ещё у Cortex M более правильное видение Гарвардской архитектуры. Если у Меги память программ и память данных имеют независимую адресацию, то у тех Cortex, которые Гарвард поддерживают, шины из процессора выходят разные, но потом они попадают на матрицу шин. И там адресация общая. Поэтому если запросы уходят на разные шины - имеем все прелести Гарварда. А вот программист всё видит по заветам Фон Неймана. Он не может различить код и данные.
Но если случайно запросы попадают на одну шину - ну просто чуть медленней всё выполнится. Код можно запускать в ОЗУ, данные грузить хоть из ОЗУ, хоть из флэша одними и теми же командами. Меня на Мегах разные адресные пространства из себя выводили!
Что касается модных RISC-V... Они однозначно круче Мег в силу 32 разрядности, но мне кажется, что слабее Кортексов. Особенно те, у которых нет Compressed- команд. Но это не точно. А то сейчас мне быстро докажут, что я не прав...
ESP32 также использует архитектуру xTensa, но её я на низком уровне не изучал. Она тоже не восьмибитная, уже этим хороша.
VT100
14.11.2024 09:10"У всех - свои недостатки". Вон, во вчерашней статье про ARM STM32... Переход в режим энергосбережения StandBy - [остановка тактирования периферии?] - все порты слетели на входы без подтяжек. Бр-р-р...
EasyLy Автор
14.11.2024 09:10Нашего шефа на выставке ребята из Nordic убедили, что энергосбережение лучше организовывать на их чипах. Он приехал, попробовал... И очень много выиграл в плане экономии по сравнению с тем, чего сам же и добился на ESP32 (не помню, C или S).
Но в обсуждаемой статье, экономией электроэнергии и не пахло.
Но в целом... Вот я считаю, что Мегам место на почётном месте в музее, но если надо - не прочь для них что-то написать. Вот, даже статья по итогу родилась. Не про них, но они - одни из главных героев. Но в целом... Очень почётное место... Но в музее. И все надписи - золотыми буквами.
EasyLy Автор
14.11.2024 09:10Чуть не забыл. У всех современных архитектур DMA обычно является обязательным. А через это можно многие интерфейсные вещи реализовывать по принципу "Настроил и забыл". Не надо следить, пришло чего-то там или нет. Я однажды на контроллере FreeScale настроил цепочку из трёх DMA так, что данные из линейной камеры принимались вообще без участия процессора. Там просто можно было цепочки настраивать.
Сначала я программно запускал оцифровку на АЦП. Камера в той схеме выдавала всё в аналоговом виде. По факту окончания, результат записывался в буфер с инкрементом указателя, после чего автоматически запускался второй блок DMA в цепочке. Тот в GPIO отправлял два слова. По результату, камера получала импульс "точка принята". Дальше запускался третий блок DMA, который писал в порт АЦП команду приёма. Точно такую же, какую в начале процесса посылал процессор. Система была подготовлена к тому, что в конце оцифровки начнётся работа первой записи в цепочке.
Приём из АЦП был закольцован на 128 байт (камера на машинке Freescale Cup давала линию из 128 точек). По окончании, формировался запрос на прерывание, и приём автоматически переходил к началу.
Правда, это не свойство архитектуры Cortex M. DMA каждый производитель делает, как считает нужным. На том же STM32, думаю, такой чисто аппаратный трюк бы не сработал, но всё равно, часть работ тоже можно было бы автоматизировать. Не отвлекать программу на каждую мелочь. На STM32 я усреднял и фильтровал данные от термодатчика как раз при помощи DMA. Во времена, когда придумывали Меги, это ещё не было принято, поэтому там DMA нет.
Traveller0968
14.11.2024 09:10Понятия не имею, я тоже не разработчик, Atmega, это целое семейство микроконтроллеров, которые ставят куда ни попадя в.т.ч. и в Arduino...
Traveller0968
14.11.2024 09:10Интересная статья и много слов, проглядел бегло и то только потому что тема очень близка, как раз сейчас пишу программулину под ПР для управления частотником, который в свою очередь крутит насос, и это все должно стабилизировать скорость потока в нужных границах.
Не, я понимаю что потенциометр вещь простая как оцинкованное ведро, но не понимаю почему их до сих пор ставят скажем в те же частотники, когда уже давно существует такая вещь, как энкодер инкрементный, есть даже с кнопками, ни каких тебе шумов и колебаний, работает четко как триггер, один щелчёк, увеличил или уменьшил значение, а нужно то только и всего, простой функциональный блок или в ПЛК или в ПР, что бы обрабатывать его вращение, а еще можно визуализировать как меняешь уставку, не понимаю как в наше время нужно пускаться во все тяжкие, что бы только устаканить значения древнейшего "целеуказателя"... непонимаю! :), а тех к то в современные цифровые устройства до сих пор ставят резистивные потенциометры, поубивав бы... :)))yappari
14.11.2024 09:10давно существует такая вещь, как энкодер инкрементный, есть даже с кнопками, ни каких тебе шумов и колебаний, работает четко как триггер, один щелчёк
К сожалению, не всё так здорово, ибо зависит от исполнения. С оптикой (наверное) всё отлично, но бывают контактные энкодеры, которые могут работать (и как правило, работают) отвратно, когда контакты подокислились (возможно, подвытерлись). Сейчас есть под рукой плеер, стоивший в своё время недурных денег, где производитель выпендрился, поставил утопленную крутилку на громкость. Сначала было нормально, теперь оно работает как генератор случайных чисел (хорошо хотя бы, что только когда крутишь, а не всегда). Надо вскрывать, но конструкция не очень располагает.
EasyLy Автор
14.11.2024 09:10"Налить масла в переменник" - классическая операция. У меня валяется старый лабораторный БП, у которого настройки на месте не стоят. Сильно не стоят. Как раз резисторы надо промаслить. Они дурят.
С другой стороны, у очень дорогой мышки ребёнок уже два раза энкодер в колёсике маслил. Первый раз было забавно. Мышка дурила во время поездки в Китай. Тот, у кого мы были, спустился с нами на подземную парковку, открыл капот своего автомобиля и капнул с кончика масляного щупа.
Так что тут проблема общая. Но зато пока сам крутящийся прибор целый - показания у энкодера стабильнее. Пусть даже у контактных пропуски бывают периодически. В нашем случае, они были бы не принципиальными. А потом - ну отключить от разъёма и поставить новый.
Обиднее всего то, что крутилка в обсуждаемом проекте - штука редко используемая. При основной работе, всё в числовом виде через мобильное приложение задаётся. Там ничего не окислится.
Traveller0968
14.11.2024 09:10Ну дык, контакт может и на потенциометре подокислиться, причем гораздо быстрее, тут вопрос не в том что там и тут может подокислиться, тут вопрос на чем быстрее это произойдет, если деталь качественная то и прослужит она долгие годы, опять же зависит от того с какой интенсивностью ее эксплуатируют, а так в качестве примера могу сказать, есть у нас один частотник Hitachi и там нужно менять частоту в пределах одного герца и на нем в качестве регулятора как раз и стоит потенциометр, который уже изношен дальше некуда, так вот мастера там все время плевались, когда нужно герцовку поменять на 0,3гц., стоял бы энкодер было бы гораздо проще, а так пришлось им на кнопки эту функцию переводить, щас вообще хочу сделать что бы они этот частотник не трогали от слова совсем, типа задал нужный поток, а ПР-ка сама подстроила частотник, а на ПР-ке нужный поток будет устанавливаться одним энкодером и не только поток, а еще несколько нужных настроек, в.т.ч. и для усреднения входящих в ПР-ку данных о самом потоке (допустимая дельта отклонения и кол-во усредняемых данных, в общем все то о чем здесь автор статьи нам и поведал) :) Проще говоря для настройки параметров будет использоваться одна крутилка и одна кнопка встроенная в крутилку, а в качестве индикатора настраиваемых параметров, цифровой 4-х значный индикатор работающий по RS-485, в общем любой олигофрен с настройками разберется даже без инструкции.... :)))
CitizenOfDreams
14.11.2024 09:10Не, я понимаю что потенциометр вещь простая как оцинкованное ведро, но не понимаю почему их до сих пор ставят скажем в те же частотники, когда уже давно существует такая вещь, как энкодер
Энкодер в качестве регулятора чего-то (громкости, скорости, яркости...) - это зло, чистое и незамутненное. А если самые умные программисты еще и добавляют ему переменную скорость (чем быстрее крутишь, тем больше величина инкремента/декремента) - это вообще жопа.
На частотнике нужен именно резистор. У которого каждому положению ручки соответствует одно определенное выходное значение. Независимо от скорости поворота, независимо от истории действий, независимо от включения-выключения питания.
И при минимальном соблюдении адекватной схемотехники этот резистор работает идеально. С примитивным программным фильтром, а то и без него.
Traveller0968
14.11.2024 09:10На частотнике нужен именно резистор. У которого каждому положению ручки соответствует одно определенное выходное значение. Независимо от скорости поворота, независимо от истории действий, независимо от включения-выключения питания.
Притянуто за уши, ибо уже есть частотники с энкодерами, например у Toshiba или у Shnaider Electric, ну вот же:
Им же и настройки производятся и чёт я не встречал что бы кто то из за этого испытывал какие то неудобства, обратные ситуации видал постоянно, когда резистором не получается выставить точное значение... Не, это не аргумент... :)))
Кстати, если что, то при потере питания, последнее заданное значение сохраняется в энерго независимой памяти, это даже работает на частотниках с резисторами, который отключен, а энкодер дает дополнительные плюшки, например, можно записать некоторое кол-во пресетов и легко их менять...
CitizenOfDreams
14.11.2024 09:10уже есть частотники с энкодерами
Отличный пример хренового UI - частотники Шнайдер с их головоломными "мЕнЮ", в которых изменение одного пункта влияет на кучу других параметров. Энкодер в них далеко не самое худшее.
Traveller0968
14.11.2024 09:10Ну для не квалифицированного персонала любой UI будет вызывать трудности, по мне так лучше "хреновый" юзер интерфейс с энкоденром, чем резистор в понятном... :))), Хотя на мой скромный взгляд у всех частотников UI, +/- одинаковый, хотя вон Owen "снизошел" до конфигуратора для ПЧВ, чего я у других не встречал, только толстенные талмуды, возможно они есть, но в наших краях почему то не распространено... :)
CitizenOfDreams
14.11.2024 09:10Хотя на мой скромный взгляд у всех частотников UI, +/- одинаковый
Да ладно.
Большинство частотников: открываешь инструкцию, находишь страницу со таблицей параметров, проходишь по списку, ставишь их в нужные значения, профит.
Шнайдер Альтивар: чтобы получить доступ к параметру X, нужно изменить параметр Y в подменю aBcDe/kLmN (см. страницу 456 инструкции), при этом автоматически изменятся параметры A, B и С...
Traveller0968
14.11.2024 09:10Ну дык , так у всех что я видел, параметр не включается ибо он противоречит, тому что вы хотите включить, и выключите сначала это потом позволю включить то, и в шнайдеровских инструкциях все тоже самое и таблицы тоже есть, не понимаю, что там не так, хотя да, программирование частотников даже в 21-м веке, квест еще тот, "Ассемблер" в чистом виде :)))
По мне так на современном частотнике вообще не должно быть ни каких кнопок, крутилок и индикаторов, шучу конечно, а программироваться он должен без инструкции через WEB интерфейс, который уже научились в "прикуриватели" встраивать... :)))CitizenOfDreams
14.11.2024 09:10Не надо мне там веб-интерфейсов. По крайней мере в качестве единственной опции. Возможность при отладке станка поменять, скажем, время раскрутки шпинделя с 10 секунд на 11 несколькими нажатиями штатных кнопок - без ноутбуков, программ, облаков, кабелей и донглов - это бесценно.
Traveller0968
14.11.2024 09:10Ну дык, вот тут энкодер и бесценен, сделал несколько пресетов, какие нужны в работе, нажал на энкодер, выставил нужный пресет и все само настроилось, причем с абсолютной точностью, это не резистором ловить десятые доли герца, тут только вопрос в другом, нужен специалист, кто это сможет грамотно один раз и навсегда настроить, а я вижу что со специалистами беда, поэтому и ловят десятые доли герца, изношенным потенциометром, при этом плюясь и матерясь на чем свет стоит... :)))
CitizenOfDreams
14.11.2024 09:10поэтому и ловят десятые доли герца, изношенным потенциометром
Если в изделии приходится "ловить доли герца изношенным потенциометром", то что-то в нем сделано не так на стадии разработки. Точные пресеты в частотнике можно выставить кнопками в меню, а потом переключаться между ними через входы или по цифровой шине. Либо подключить кнопки "частота вверх/вниз", если скорость нужно точно регулировать в процессе.
Traveller0968
14.11.2024 09:10Ну дык, расскажите это тетеньке мастеру, которая на пенсии подрабатывает, что можно на кнопки перепрограммировать... :))) и про цифровую шину и про входы.... :))) для нее и потенциометр, это просто крутилка, а так да, уже перенастроил на кнопки, вздохнули с облегчением... :), сл. этап вобще сделать так что бы они этот частотник не трогали и забыли про него, будет у них 1 энкодер с функцией нажатия, а частотник вобще будет в другом помещении находится... Все равно не понимаю зачем там аналоговый потенциометр нужен, не иначе ума не хватает энкодер поставить или просто инерция мышления... :)
RTFM13
14.11.2024 09:10Станок в котором нало лазить в частотник (кроме как для ремонта этого частотника) надо выкинуть на свалку истории.
CitizenOfDreams
14.11.2024 09:10в шнайдеровских инструкциях все тоже самое и таблицы тоже есть, не понимаю, что там не так
В обычных частотниках проходишь по параметрам линейно, один раз, в любом удобном порядке. Параметр B0001 - 50. Параметр B0002 - 380 вольт. Параметр B0045 - 17.
В шнайдеровских - прыгаешь по разным подменю в сложном и неочевидном порядке. И сами параметры вместо "A1234" называются "CtL-tEr" или "drC-SrF", сложным образом влияют друг на друга и дублируются в нескольких меню.
inakrin
14.11.2024 09:10У меня как раз похожая проблема в маленьком хобби проекте. Атмега8 управляет двухступенчатым повышателем напряжения, (два шим 100khz всего по одному для каждой ступени), выходное напряжение от каждой ступени через делитель передается на аналоговые пины, атмега решает когда шим duty cycle понизить/выключить. Все питается от двух 18650, атмега через lc1805. Встроенный aref.
Я отделил плату атмега8 от платы с повышателем, шлейфами соединяются входы и выходы, но всё-равно, шумит всё, считываемые значения скачут, ещё на плате с повышателем несколько мосфетов, которые также атмегой управляются через tc4421. (Выход второй ступени открывают к светодиоду (да, 300+вольт светодиод) по сигналу от атмеги.
Помехи на всех входах и выходах, особенно когда напряжение выхода второй ступени поднимается выше 300 вольт.
Достаточно ли будет добавить rc фильтр на затвор каждого мосфета кроме шим и рядом с пинами атмеги на которые приходит аналоговый сигнал? Я разнес индукторы и мосфеты с микросхемами так далеко друг от друга как мог, но всё равно устройство маленькое, всё очень рядом.
VT100
14.11.2024 09:10Без схемы и компоновки плат(ы) такое трудно обсуждать. Но "разнёс индукторы и мосфеты" - намекает на необходимость полной переделки (если я правильно понял несколько объединительных союзов).
Площади контуров, ометаемых импульсными токами преобразователей - должны быть минимальны.
inakrin
14.11.2024 09:10Мосфеты преобразователей вплотную к индукторам, имел в виду, что мосфет, который управляет диодом вместе с драйверами мосфетов подальше поставил.
Вот схема компоновка версии, которая сейчас в работе(недоделанная). В первой были ещё другие недостатки, которые после печати и тестирования выяснились, исправляю. Но шум на мосфетах q3, q1, драйверах и входах adc с делителей r9-r10 / - самый нериятный. Мосфеты должны открываться на милисекунды и только тогда, когда приходит сигнал от контроллера. А напряжение должно считываться точно, чтобы выключать шим вовремя. Я добавил 1uf конденсаторы перед затворами q3, q1 и ещё отодвинул их как можно дальше. В предыдущей версии их не было. Думаю сигнал от делителей пропустить через фильтр на операционном усилителе. Будет ли этого достаточно ?
В моей схеме ещё наверняка куча странных вещей, но я просто любитель. :-) Для меня уже успех, что даже первая версия работает примерно как надо, не сгорает, погрешность в напряжении не превышает 20 вольт (что я и хочу уменьшить).VT100
14.11.2024 09:10Перво-наперво - не выдержано пожелание DS по выходному сопротивлению источников аналогового сигнала для АЦП ATmega - менее 10 кОм. Желательно поставить буферные ОУ на выходах делителей R7-8 и R9-10.
Во-вторых, для такого большого повышения напряжения целесообразнее использовать трансформатор, а не две ступени.
По разводке - обнять и плакать. Тонюсенькие дорожечки и вот это всё...
inakrin
14.11.2024 09:10Да, думал про буферные ОУ с конденсатором для фильтрации на плате контроллера перед пином adc. Лучше же ближе к пинам?
Да, дорожки будут правильной толщины, не настроил ещё. Это плагин дорожки сделал, не я же сам вручную.
Про трансформатор думал, в следующей версии сделаю скорее всего. Не разобрался ещё как трансформатор для повышателя подбирать.
То есть конденсаторы лучше поменять местами с батарейками? Спасибо, я думал, что только индуктор с мосфетом имеют значениеVT100
14.11.2024 09:10буферные ОУ ..... Лучше же ближе к пинам?
Нет, ближе к делителям. Выход ОУ имеет меньший импеданс и менее подвержен влиянию помех. Может быть - даже подобрать точку соединения R8, R10 (и вторых входов ОУ) с общим проводом. Чтобы импульсные токи не протекали между этой точкой и контактами 1 и 2 J1 (относительно которых проводит измерение АЦП).
То есть конденсаторы лучше поменять местами с батарейками? Спасибо, я думал, что только индуктор с мосфетом имеют значение.
Минимизация площади контуров импульсных токов, которые излучают магнитную компоненту помех. Для L1 - это контура входной цепи из: Параллельные C1 и батарея - L1 - Q2. И выходной из: Параллельные C1 и батарея - L1 - D4 - C6.
Кстати - да, какова цель 1 мкФ конденсаторов?
Они уронят КПД под плинтус.
inakrin
14.11.2024 09:10Благодарю за совет! Добавлю на эту плату буферы значит.
Кондненсаторы чтобы отфильтровать наводки от индукторов, чтобы мосфеты которые управляют светодиодами не активировались сами. А что с кпд будет? Один мосфет - просто включает и выключает маленький светодиод, второй выдаёт по сигналу от контроллера милисекундные импульсы высокого напряжения на сверхмощный диод вспышки. Этот проект - это светодиодная вспышка для фотосъёмкиVT100
14.11.2024 09:10@RTFM13расписал всё о конденсаторах. Плавно включающийся транзистор - рассеивает энергию впустую. Хотя, включающийся слишком быстро - создаёт условия для излучения помех.
[Q1 и Q3] не активировались сами.
Скорее - дело в багах в прошивке. В крайнем случае - в плохой разводке платы и наводках на входные цепи U1.
второй выдаёт по сигналу от контроллера милисекундные импульсы высокого напряжения на сверхмощный диод вспышки
Хорошая идея для убивания светодиода. На светодиод нужно подавать ток, а не напряжение. Не говоря о том, что изменение тока от номинала - сдвигает цветовую температуру и CRI.
inakrin
14.11.2024 09:10Ниже ответил, по какой причине думаю, что наводки.
На светодиод ток подаётся, естественно. Он через резисторы подключен и у мосфета сопротивление в полностью открытом состоянии 3.5Ом. Я не стал резисторы на плату выводить, потому что у меня много светодиодов, с разным током и напряжением от 30 (~4A) до 360v(~10A). И я экспериментирую с тем какой ток больше номинального они могут выдержать если импульсы короткие и давать им остыть. По заверению производителя (заказал печать кастомных COB в китае) x2 c милисекундными импульсами - точно ок. А выше они сами не тестировали.
CRI не важно - светодиоды и вспышка ультрафиолетовые.
RTFM13
14.11.2024 09:10Никакие наводки от индукторов у вас мосфеты не активируют.
У вас самый большой ток это зарядка ваших безумных конденсаторов в цепи затворов. Остальное крохи.
Преобразователь надо поменять например на обратноходовый на трансформаторе от первого попавшегося китайского зарядника. Вот у вас 1 транзистор освободился. Отсечку по току сделайте с гистерезисом на операционнике и заведите её на прерывание и на драйвер. Можно сразу драйвер поставить с отсечкой (а также UVLO, если найдёте на такое напряжение)
Для включения низковольтного светодиода вам драйвер не нужен, можно смело ставить мосфет с "логическим входом". Вот у вас полностью освободился 1 драйвер.
Разведите плату нормально. Отделите силовую землю от сигнальной, в конце концов. Разведите сначала силовую часть максимально компактно без перехлёстов и т.п. Потом прицепите к ней управление. С заливкой полигона не торопитесь. Для токов до 10А достаточно широких дорожек. Сигнальную землю (включая питание микроконтроллера) соединяйте с силовой в одной точке. Обычно это в районе истока мосфета. А если заливаете общую землю одним полигоном, то прошейте её переходными отверстиями в направлении основных токов и в местах рассечения.
У вас там, что, мосфет в TO247? Серьёзно? У вас импульсная мощность больше 30 КВт? У вас тут 3 мосфета скорее всего в корпусах максимум SOT23, SOT223, SOT263. При чем на счет последнего - не уверен.
inakrin
14.11.2024 09:10Я думаю наводки, потому что осциллограф показывает что с контроллера сигнала нет, и проблема начинается при зарядке конденсатора второй ступени выше 250v. Но это в первой версии, там дроссели ближе и другие торроидальные и припаяны через дырку, а не smd.
У меня среди валяющихся мосфетов есть куча в to-247 подходящие по напряжению/току. В прошлой версии был to-220 тоже, но у меня только один подходящий и у него сопротивление когда он полностью открыт 3.5 ом. Я думал их тоже smd сделать и при печати поставить, но в jlcpcb все подходящие в статусе preorder были. А которые через дырку я сам уже припаяю, благо у меня их много.
Там двойной драйвер, всё равно один канал пока свободный. В следующей версии ещё больше будет дополнительных низковольтных диодов, возможно заменю на логический мосфет!
Благодарю за совет по заливке земли!RTFM13
14.11.2024 09:10Вообще ничего не говорит про наводки. Может земля плавает по тонким дорожкам.
Посмотрел разводку.
Обведено белым
что там с выводами Q3-2, Q3-3? что за дорожка между ними.
что с выводом U1-8
что с землёй у конденсатора? Полигон висит в воздухе?
Нарисовал серым как примерно должна выглядеть разводка цепи затвора. Прямоугольник это резистор. Ширина дорожки примерно такая.
Конденсатор в цепи затвора выкинуть однозначно. Резистор увеличить в 5-10 раз. Резистор на землю выкинуть или увеличить в 10 раз. Стабилитрон выкинуть. Термобарьер лучше убрать или сделать в нём дорожки существенно шире.
Подключение земли к драйверу вообще не вижу! Он у вас что, через clamping диоды работает?
Контакт 9 (подложку) подключить к земле без термобарьера! К выводам U1 ENA/B подключить конденсотор 0,1-1 uF.
Землю прошить около драйвера в нескольких местах. Проверить что хотябы на одном из полигонов нет разрыва между Q3-S и U1-GND.
inakrin
14.11.2024 09:10Это недоделанная вторая версия. Автороутинг пропускает дорожки, поэтому наверное нет подключения
Первая версия вот так выглядела. Драйверы другие были
inakrin
14.11.2024 09:10почему выкинуть стабилитрон? У меня в первой версии его не было и всё работает хорошо (за исключением проблем из оригинального вопроса и пропущенной дорожки, которую я припаял проводом), но на бредборде иногда мосфеты сгорали(вообще с другим драйвером, не ic, самодельным на транзисторах, решил от него отказаться), на всякий случай решил добавить
RTFM13
14.11.2024 09:10Я добавил 1uf конденсаторы перед затворами q3, q1 и ещё отодвинул их как можно дальше.
Это вы хотели таким образом растянуть фронты открытия закрытия мосфетов?
inakrin
14.11.2024 09:10Нет, там таким образом образуется low pass filter из резистора между драйвером и затвором и конденсатора. А разве нет?
RTFM13
14.11.2024 09:10Ну допустим, а цель то какая? Если не уменьшение dVds/dt, то что?
inakrin
14.11.2024 09:10у мосфета проблема - он иногда активируется на микросекунды наводками от индукторов. (так я думаю). Его ожидаемый режим работы - активация на милисекунды по сигналу от контроллера
RTFM13
14.11.2024 09:10По этому вы увеличили длину "антенны" между драйвером и мосфетом которая принимает эти наводки?
Вообще при нормальной разводке это одна из последних проблем. Сам затвор имеет далеко не нулевую емкость влияние которой усиливается эффектом Миллера.
В любом случае установкой конденсатора на затвор с этим не борются. Он только вхолостую нагружает драйвер и смещает работу мосфета ближе к пороговому напряжению благодаря тому же эффекту Миллера.
За то сам драйвер без малейшей необходимости нагружается этим конденсатором. Подозреваю у вас там пиковый ток как бы не больше чем ток DS мосфета (порядка 5А).
Мосфет у вас нагружен индуктивностью, большим пикам тока взяться неоткуда. Если только диод не подтупливает. Но в этом случае конденсатор надо ставить совсем в другое место (в составе снаббера) или на худой конец увеличить сопротивление резисторов в взатворе (его в любом случае надо увеличить) или и то и другое.
Отдельным анекдотом выглядят емкости на питании драйвера меньше емкости нагрузки.
Как у вас организована отсечка по току насыщения дросселей?
Вообще не обижайтесь, но вся схема и разводка ужасны. Можно продолжить обсуждать отдельные косяки, но по факту надо перерисовывать с нуля.
inakrin
14.11.2024 09:10Я не про два мосфета которые рядом с индуктором, а два, которые рядом с драйверами. Чем вам не нравятся емкости на питании драйвера? Вот, из даташита драйвера. И ёмкости какой нагрузки они меньше? С учетом конденсаторов на завторе? Ну в принципе логично увеличить наверное.
Обязательно переделаю после тестирования и учёта недостатков. Это уже вторая версия
Indemsys
14.11.2024 09:10Всяческие фильты типа бегущего среднего и экспенциального вносят задержки. Чем сильнее фильтрация тем сильнее задержка.
Между тем есть фильтры с меньшей задержкой - это медианный, Савицкого-Голлея, Калмана и т.д. Калман хорош тут, поскольку достаточно хорошо известно с какой скоростью может крутить человек и как быстро тормозить и ускоряться.Но лучше всех тут бы наверняка работал фильтр на нейросетях. И сделать довольно просто на каком-нибудь https://docs.edgeimpulse.com/docs
EasyLy Автор
14.11.2024 09:10Здесь самое главное - не забывать, что перед нами восьмибитный контроллер с малыми объёмами как памяти программ, так и памяти данных. Вдобавок - ещё и со скромной тактовой частотой... И всё это нужно не только для задачи стабилизации потенциометра, но и для выполнения основного функционала. Стабилизация потенциометра - очень второстепенная задача, на которую не стоит класть много ресурсов. Да и времени на подбор параметров фильтров - тоже. Так что все сложные фильтры лучше сразу отбросить. Только простые!
А самое обидное - всё так шумело, что фильтры пришлось заполировать алгоритмом, добавляющим гистерезис.
Но это замечательно, что под статьёй будет перечисление видов фильтров! Кто найдёт её во время поиска решения, тот и этот комментарий найдёт.
Indemsys
14.11.2024 09:10Смотрите эту статью - https://habr.com/ru/companies/ruvds/articles/853050/
А у вас задача еще проще - определить крутит/не крутит
Как бы нейросеть здесь не оказалась проще всех остальных решений.EasyLy Автор
14.11.2024 09:10Проще? Усреднять надо хошь-не хошь, чтобы взяв основное значение получить не что-то прыгнувшее. Усреднение нужно ради точности основного функционала.
А алгоритм "крутит-не крутит" занимает экран, который не требует обучения (то есть, часов на подготовку данных). И требует одно 16 битное слово в памяти данных.
Indemsys
14.11.2024 09:10Это может быть самообманом.
Чтобы понять поддается ли ваш шум хорошему усреднению он должен быть нормальным или хотя бы с одним горбом. Для этого надо строить гистограмму шума.
Если горбов несколько, то сколько нибудь адекватное по длине усреднение работать не будет.
Несколько горбов будет если реально есть доминирующие внешние имульсные шумы или ошибка в коммутации каналов АЦП.
А гистерезис это тот же обман, что и решатель "крутят/не крутят". Но решатель точно будет держать стабильнее, гистерезис же может перестать совсем работать при изменении уровня шума.EasyLy Автор
14.11.2024 09:10Когда время на разработку бесконечно, а задача "крутят-не крутят" основная - может быть. Когда задача даже не второстепенная (но без неё не примут), а проект идёт по методике Fixed Price, за любой лишний час руководитель... В общем, отругает.
Поэтому самообман, не самообман... Работает, у Заказчика претензий нет - ладушки. Я не зря про часы на обучение нейросети сказал. Кто бы их оплатил,?
В общем, для потенциометра - когда надо идеально, и есть много времени - может, Вы и правы. А так - оставим нейросетевые вещи для случаев, которые иначе не решаются. Ну, или для учёных, у которых сроки не поджимают, а результат им нужен не приемлемый, а идеальный.
CitizenOfDreams
14.11.2024 09:10Но лучше всех тут бы наверняка работал фильтр на нейросетях.
Пейн, я не чувствую тега "сарказм". Вы же не всерьез предлагаете сглаживать сигнал от ручки-крутилки нейросетью? (падмэ.жпг)
randomsimplenumber
14.11.2024 09:10Вы находитесь где-то здесь ;) А что не так? Нейросеть это матрица-шматрица с весовыми коеффициентами, сигнал на входе - сигнал на выходе, процессоры и память всё дешевле и дешевле..
maks_strachilov
14.11.2024 09:10можно для таких целей использавть CIC и IIR фильтры в фиксированной точке
главное уметь их расчитывать, особенно IIR
ну а CIC фильтры - это вообще имбаIndemsys
14.11.2024 09:10CIC - это же всего лишь несколько подряд бегущих средних, памяти требуют неадекватно. Медиана в связке с несильным экспонециальным (тот самый IIR, но без умножения) гораздо лучше.
RTFM13
14.11.2024 09:10но вот нутром чую, что вот такой вариант был бы поинтереснее:
Нет, это плохой вариант. Vref используют для пропорционального источника. А вы его фильтруете раздельно. Это может быть не только бесполезно, но и вредно. При "удачном" стечении обстоятельств можно получить увеличение амплитуды шума.
Лучше как-то так
Если прям так надо питать переменник от 12В, то R1 увеличиваем до 15-20К в зависимости от чистоты "12В".
Если переменник на другой номинал - корректируем R1, R2 пропорционально в разумных пределах (плюс/минус порядок).
Период АЦП выбирать примерно Тау/10 (порядка 22 ms) или меньше (частоту выше).
EasyLy Автор
14.11.2024 09:10Не совсем понял.
Сейчас два совсем независимых источника. Причём импульсных, которые как хотят, так и шумят.
Если один из них сделать основанным на втором, станет только хуже? Что не идеально - ну да, но хуже...
И откуда у Меги два входа VRef?
RTFM13
14.11.2024 09:10И откуда у Меги два входа VRef?
Это концепт. Для меги просто не подключаем Vref- и R2=0 (умозрительно соединяем Vref- и GND). Это кстати один из недостатков меги.
станет только хуже?
Да, если сигнал непропорциональный, то пиковое значение шума АЦП = сумме шумов входного сигнала и опорного напряжения. Если шум периодический (например ШИМ в БП) то можно словить полную амплитуду на протяжении приличного времени. Или получить систематическую погрешность (смещение сигнала).
И еще момент. У вас на схеме ползунок потенциомметра перманентно нагружен вторым делителем. И, соответственно, выходной сигнал будет зависеть от сопротивления ползунка. Т.е. всё искрение ползунка будет в выходном сигнале. В той схеме, что я привёл этого недостатка нет, ползунок нагружен лишь крошечным током АЦП.
Кроме того, нагрузка из второго делителя вносит нельнейность относительно пропорциональности резистора. Если нелинейность большая и у крутилки есть шкала и в ней это не учтено, то это может быть нежелательно.
RTFM13
14.11.2024 09:10Сейчас два совсем независимых источника. Причём импульсных, которые как хотят, так и шумят.
Питания от "совсем" разных источников вообще лучше избегать. Ничего хорошего из этого не выйдет. Там есть тьма нюансов, например, с нештатными ситуациями. Вообще с измерительными цепями есть нюансы с разводкой платы - их нельзя подключать как попало. В данном случае все соединения надо тянуть от микроконтроллера даже если есть возможность "прицепиться" где-то поближе.
Если блоки питания разные значит между платами желательна (а иногда обязательна) гальваническая развязка. Или должен использоваться дифференциальный сигнал и вход допускающий смещения на существенную амплитуду (типа как у RS485). Особенно если у вас там силовая электроника рядом.
А если резистор и Vref запитаны от разных источников это вообще не должно нормально работать.
EasyLy Автор
14.11.2024 09:10А если резистор и Vref запитаны от разных источников это вообще не должно нормально работать.
А в статье что сказано? Именно от разных!
И дана рекомендация: "Если есть возможность сотрудничать со схемотехниками - делать это, чтобы совместно приводить систему в нормальный вид". Жаль, что в нашем случае, мы даже не знаем, кто они, схемотехники этой платы.
RTFM13
14.11.2024 09:10Вашу ситуацию я понял. Но на месте тех кто в неё еще не попал я бы сосредоточился на том, как в такую ситуацию не попасть, а не на том, как из неё
лучшеменее худше выйти.Так делать вообще нельзя. Во-первых дрейф блока питания вы простыми фильтрами не отловите. Во-вторых обрыв земли в данной ситуации чреват влетанием емкостных токов с ЭДС амплитудой вплоть до напряжения питающей электросети на вход микроконтроллера.
diakin
14.11.2024 09:10На Uref стабилитрон надо ставить. А потом уже делить. Да и потенциометр от стабилитрона питать.
Второй момент - с каким разрешением надо выставлять обороты? 1 об\мин, 0.1 об\мин? итд.
А в каком знаке начинает проявляться шум? Если дергается второй знак после запятой, а выставлять надо с точностью до единицы оборотов, то и черт бы с ним.
VT100
14.11.2024 09:10Отзываю данный этой статье плюс.
ЕМНИП, формула (алгоритм) расчёта медианы - совсем не таков.
Натянутые на глобус 250 вместо 256.
Манкирование этапом составления Т.З. Если там вращается что-то хоть немного инертное с небесконечной мощностью и механической жёсткостью привода, то вполне можно было получить от заказчика параметр "максимальная скорость изменения уставки" и, на её основе, строить фильтр (например - упомянутый @ABRogovи @RTFM13 простейший IIR) с достаточно низкой частотой среза. ЕМНИП, в частотниках Yaskawa потенциометр ручной установки выходной частоты весьма задемпфирован. Примерно на 0,1-0,2 Гц срез фильтра идёт.
Думал, было, хоть частично оправдать мучения пьезоэффектом 10 мкФ керамических(?) конденсаторов. Но, перечитав, нашёл, что это был другой случай - с 3Д-принтером.
Неплохо также - указать хотя бы сопротивления R1 и R2 в дополнение к 100 нФ С1. Чтобы соотнести выходное сопротивление делителя с рекомендованным макс. 10 кОм для АЦП (для оценки качества фазы выборки сигнала) с одной стороны и оценить ток ползунка потенциометра (не слишком ли мал) с другой.
И так далее.
С уверениями в совершеннейшем почтении за предыдущие статьи.
tbl
14.11.2024 09:10Вместо оконного фильтра, в котором вы ищете среднее по n последним значениям измерения, лучше использовать фильтр Калмана. Он как раз предназначен для оценки состояния системы (положение ручки резистора) по зашумленному сигналу с сенсора. В одномерном случае реализация фильтра будет очень простой. Единственное, что в окне можно считать - несмещенная дисперсия выборки для сигнала с сенсора по тем же последним n значениям, ну или подсчитать ее заранее при калибровке и зашить константой.
Yuri0128
14.11.2024 09:10Но ТС его чего-то не применил. Вопрос: почему? Возможно были на то причины. Или просто в голову не пришло, - такое тоже в запарке бывает.
Hlad
14.11.2024 09:10Медианная фильтрация - это не
При ней мы сначала отбрасываем максимум и минимум из накопленного массива, а уже оставшиеся значения усредняем
Это "накопили массив, сделали QSORT, взяли середину массива". И она в таком виде отлично работает
ifa21rus
14.11.2024 09:10Тоже удивился что автор использовал ключевое слово "Медианный фильтр" и совсем не понял о чем речь. А так для разовых выбросов истинный медианный фильтр как раз бы и помог даже в условиях "зажатого" ресурсами 8битного PIC контроллера....
Dark_Purple
14.11.2024 09:10Делим на 250, серьёзно???
EasyLy Автор
14.11.2024 09:10Серьёзно. Правда, мы в комментариях уже выяснили, что это у меня инертность мышления. Считать до 256 - надо было или счётчик делать 16 битным, или сравнивать с нулём, чем всех путать, кто когда-то будет этот код сопровождать. Я так рассуждал.
Уже потом я понял, что сравнивать с 255 можно было до инкремента счётчика... Это будет и восьмибитненько, и понятно всем.
Но в целом... На фоне тех вычислений, которые производятся в основном функционале, одно деление на 250 никакой погоды не сделает. Но таки да, глаз замылен. Что сравнивать можно до инкремента - вылетело из головы.
VT100
14.11.2024 09:10А так - можно?
if smth == (uint8_t)(0x100);
Если можно - то понятнее ли гипотетическому поддержателю?
EasyLy Автор
14.11.2024 09:10Ну, кроме тех, которые захотят ещё сильнее увеличить. Проект сдан, но в своём хранилище оставлю Ваш вариант, а "не увеличивать свыше 256" внесу в комментарий.
Кстати, оптимизатор для нового варианта даже на уровне -O2 оставил две лишних строки. R26 и R27 зря заполняет.
lastAvgValue = (uint16_t)(avg_buf / ADC_AVERAGE_CNT); 1cbe: 89 2f mov r24, r25 1cc0: 9a 2f mov r25, r26 1cc2: ab 2f mov r26, r27 1cc4: bb 27 eor r27, r27 1cc6: 90 93 1a 05 sts 0x051A, r25 ; 0x80051a <lastAvgValue+0x1> 1cca: 80 93 19 05 sts 0x0519, r24 ; 0x800519 <lastAvgValue>
sami777
14.11.2024 09:10У меня (обычно) во всех устройствах у микроконтроллера есть батарейка для RTC. Соответственно, если по какому то аналоговому входу что то начинает сильно блымать, что из-за самого сигнала блымать не должно, то сравниваю то, что получается при попытке измерить напряжение на внутренней батарее микроконтроллера. Обычно показания от батарейки (при 12 битном разрешении АЦП) скачут не более, чем на 1LBS ( О чем написано в RM для STM32F429). Если это так, то цепь аналогово питания микроконтроллера и ИОН выполнены правильно. А проблема в говенной схемотехнике аналогово входа, например, аналоговый общий неправильно развязали и т. д. Еще раз повторяю, что говорил о батарейке питания RTC микроконтроллера.
strvv
14.11.2024 09:10Интересен выбор 250 значений. Видел Ваши ответы, но...
В виду применения 10бит ацп что мешало не 250, а 64 раза (2^6) считывать, и тогда уж сдвигать обратно на 6 бит вправо. ;) А 250 значений грозили уйти в переполнение.
Это если брать весь диапазон UINT16.
Тогда и нет переполнения с одной стороны, с другой стороны, деление, которое отсутствует в AVR заменяется сдвигом вправо. То же деление, но на 2^N.
То есть:
Имеются сумма X64, Xold (среднее с прошлого этапа), Xnew - новое значение,
Можно так ещё сделать усреднение - X64new=X64-Xold+Xnew; Xold=X64new>>6; X64=X64new.
При включении значения будут подниматься с 0 до текущего значения.
Для удобства я вам указал отдельные значения X64new, Xold.
В реальности имеется X64, X, вычитаем Х из Х64 и прибавляем значение ацп. Слвиг вправо и полученое из X64 число положить в X.
По поводу частоты опроса - действительно, проще было не постоянный счет делать, а скажем по таймеру.
Но Ваш выбор уважаю.
fp777
14.11.2024 09:10Надо на резистор ёмкостный датчик повесить. Покрутил, убрал руку, датчик сообщил, что рука убрана и дальше показания АЦП игнорируем.
Понятно, что надо изменить аппаратную часть, но как вариант пусть будет.
Dimonij2
14.11.2024 09:10Тут бы неплохо смотрелся аппаратный фильтр какой-нибудь 2-го порядка, на паре ОУ и щепотке пассивки. Есть отличная книжка авторства Бонни Бейкер - "Что нужно знать цифровому разработчику об аналоговой электронике". Там вот таких случаев рассмотрено штуки 3, и все они решены вообще без программной фильтрации. То есть, там результаты не среднего значения обсуждаются, а шум прямо в отсчетах АЦП до и после применения коррекций в схемотехнике.
А, еще опорное для АЦП лучше делать на отдельном источнике, брать для этого линию цифрового питания не очень хорошо. Оно, как правило, зашумлено, да еще и стабильность там так себе.
Keroro
14.11.2024 09:10Понятно, что, когда усредняешь период 100 мс, в пределах периода всё будет хорошо. Но у разных периодов средние значения будут разными. Усреднять по большему промежутку? Тогда данные в мобильном приложении будут «замирать». Заказчик начинает нервничать уже не потому, что всё прыгает, а потому, что он крутит ручку, а реакции никакой.
Я уже лет 10 использую в своих проектах фильтр, описаный на easyelectrinics тов. drvlas. Суть его такова: берем разницу между предыдущим и текущим значением, и умножаем его на бэту. Поэтому крутанув условную ручку, мы сразу видим, как значения начинают "бежать" к новому установленному значению (а не ждать, пока 256 или сколько там значений сначала накопятся), и эту скорость (т.е. Тау) можно регулировать или иметь разную для разных параметров. И оно работает на 8-бит МК без всяких нейросетей.
Код примерно такой:
uint32_t filter(uint32_t x, uint8_t Nb, uint8_t k) { static uint32_t y=0; static uint32_t z=0; z += (x - y); return y = ((uint32_t) Nb * z) >> k; } temperature=filter(ADC_VAL[CHAN_TEMP], 1, 5);
ABRogov
Чем экспоненциальное сглаживание не угодило? Обычно дает не плохие результаты.
https://ru.wikipedia.org/wiki/Экспоненциальное_сглаживание
EasyLy Автор
Да там ничего не помогало, с теми двумя импульсными источниками питания, их иголками и персональными шумами. Только жёсткая алгоритмическая фильтрация всё спасла.
IgorPie
Добавить "Переключаемые конденсаторы" при резком изменении входной величины. Фильтры 2-6 порядка. Дельта-триггеры с гистерезисом, что если после фиксации величины играют 1-2 бита, то не дергаться.
А ваш велосипед - это балансный вход на коленке. Возможно, балансник можно было сделать попроще. Но, если по другому никак, то и ок.
EasyLy Автор
Велосипед, конечно, велосипед, но не от хорошей жизни он изобретён. Как я и отмечал во вступлении, всё уже наверняка 100500 раз написано, но намного меньше раз опубликовано. Я же постарался опубликовать не просто красивые названия, но ещё и готовое решение. Для вполне себе типовой задачи, которая наверняка встретится ещё многим.
А что может быть проще одного экрана кода на усреднение и одного экрана на гистерезис?
ABRogov
Так это того, что нормальная инженерная разработка не требует таких извращений. Там изначально не будет нужды в таких костылях. Потому и не публикуют. В вашем решении нет общности.
EasyLy Автор
Настолько общности нет, что усреднители даже в аппаратуру современную добавлены. Только не все ими пользуются (мне же пришлось за ребятами добавлять, но в старую аппаратуру - программно). Настолько нет общности, что даже в этой ветке комментариев говорится про гистерезис... И в ряде других веток - тоже. Собственно, моё решение - один из методов его реализации.
ABRogov
Я не критикую ваш метод, работает и слава богу, просто указал почему такие вопросы редко затрагивают в литературе.
Va_sil
А Вы не пробовали питание датчиков отдельным проводом ? Как во всех нормальных схеммах , просто слаботочку отдельно ?
EasyLy Автор
Этому посвящены целых два больших раздела статьи. Схема не наша. И похоже, Заказчик к её разработке отношения тоже не имеет. Все попытки обсудить улучшение схемы, наткнулись на полное непонимание. Но без устранения дёрганий, программную часть Заказчик принимать отказывался. Хорошо быть Заказчиком!