Search
Write a publication
Pull to refresh
194
0
Павел Локтев @EasyLy

TinyML, исполнение нейросетей на микроконтроллерах

Send message

Ну, кроме тех, которые захотят ещё сильнее увеличить. Проект сдан, но в своём хранилище оставлю Ваш вариант, а "не увеличивать свыше 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>

3.Вычисляем среднее значение и отклонение от среднего в массиве.

Да, но по условию задачи, у нас медленный восьмибитный процессор с сильно ограниченным ОЗУ. А задача стабилизации положения потенциометра настолько вспомогательная, что тратить на неё массу дефицитных ресурсов (которые нужны на задачи первого и второго приоритета) совсем не хочется. Под массив нужно ОЗУ и время на его обработку.

Лучше подождать пока вал потенциометра перестанут вертеть, а потом уж мерить...

Была такая претензия: "Если сдвинуть потенциометр, то потом всё замирает". Это у основного разработчика (меня привлекали только помочь устранить проблему) раз в секунду всё опрашивалось. При этом задержку на начало вращения Заказчик, как оказалось, не замечал, а вот что потом нет реакции - замечал. Так что как минимум, при приёмке, вал крутили нещадно, и были недовольны, что это не отражается на лету. Уже поэтому так не получилось бы.

Серьёзно. Правда, мы в комментариях уже выяснили, что это у меня инертность мышления. Считать до 256 - надо было или счётчик делать 16 битным, или сравнивать с нулём, чем всех путать, кто когда-то будет этот код сопровождать. Я так рассуждал.

Уже потом я понял, что сравнивать с 255 можно было до инкремента счётчика... Это будет и восьмибитненько, и понятно всем.

Но в целом... На фоне тех вычислений, которые производятся в основном функционале, одно деление на 250 никакой погоды не сделает. Но таки да, глаз замылен. Что сравнивать можно до инкремента - вылетело из головы.

Настолько общности нет, что усреднители даже в аппаратуру современную добавлены. Только не все ими пользуются (мне же пришлось за ребятами добавлять, но в старую аппаратуру - программно). Настолько нет общности, что даже в этой ветке комментариев говорится про гистерезис... И в ряде других веток - тоже. Собственно, моё решение - один из методов его реализации.

Вот как раз в прерывании и есть смысл выделить 2 персональных регистра, чтобы сэкономить на стёке и времени обработки прерывания. А у меня во времена АВР вообще вся программа крутилась на регистрах и ОЗУ использовалось в основном только под массивы.

Там в основном алгоритме такие вычисления, что ему регистры не помешают. А стабилизация потенциометра - даже не вторичная задача. При основной работе обороты задаются в цифровом виде через мобильное приложение. И не такие они частые, прерывания от АЦП. А вот данные от сопроцессора и ModBus стекаются всё время, и должны вовремя обсчитываться.

Я же говорю... Главное - не увлечься, и не положить все силы и средства на задачу третьего приоритета... Но без которой проект не приняли бы. Но силы надо рассчитывать.

У вас же АТмега а не ПИК, зачем загрузка и выгрузка? У АТмеги 32 регистра, поэтому накапливать результат можно прямо в регистрах. Всего лишь 2 команды и вроде 2 машинных цикла на всё.

Так накопление идёт в обработчике прерывания. Не выделять же два персональных регистра под задачу, которая не является даже второстепенной!

Тут не понял. При сложении от 2 до 255 1-байтных чисел результат всегда 2-байтный если вам не нужны переполнения.

Делить надо, когда счётчик достиг 256. Хотя, да, не подумал, что можно проверять до увеличения счётчика. Тут уже у меня инертность мышления сработала.

А если резистор и Vref запитаны от разных источников это вообще не должно нормально работать.

А в статье что сказано? Именно от разных!

И дана рекомендация: "Если есть возможность сотрудничать со схемотехниками - делать это, чтобы совместно приводить систему в нормальный вид". Жаль, что в нашем случае, мы даже не знаем, кто они, схемотехники этой платы.

Не совсем понял.

Сейчас два совсем независимых источника. Причём импульсных, которые как хотят, так и шумят.

Если один из них сделать основанным на втором, станет только хуже? Что не идеально - ну да, но хуже...

И откуда у Меги два входа VRef?

Вот я и говорю. Кто потом полезет код сопровождать - много интересных слов в мой адрес скажет.

Взяв константу 250, я выжал не полную, но хоть какую-то оптимизацию, но оставил код универсальным. Если вдруг завтра его будет кто-то читать, ему не понадобится вникать в странные операторы. Мало того, если его соберут под ARM или ещё какой RISCV - он тоже заработает (хоть там все эти переменные, отличные от 32 битных, начнут замедлять процесс - там будет вставляться код, зануляющий лишние биты, есть у меня опыт изучения работы компиляторов под 32 битные архитектуры, когда используются такие укороченные переменные)..

Этому посвящены целых два больших раздела статьи. Схема не наша. И похоже, Заказчик к её разработке отношения тоже не имеет. Все попытки обсудить улучшение схемы, наткнулись на полное непонимание. Но без устранения дёрганий, программную часть Заказчик принимать отказывался. Хорошо быть Заказчиком!

Велосипед, конечно, велосипед, но не от хорошей жизни он изобретён. Как я и отмечал во вступлении, всё уже наверняка 100500 раз написано, но намного меньше раз опубликовано. Я же постарался опубликовать не просто красивые названия, но ещё и готовое решение. Для вполне себе типовой задачи, которая наверняка встретится ещё многим.

Возможно, балансник можно было сделать попроще. 

А что может быть проще одного экрана кода на усреднение и одного экрана на гистерезис?

В статье даже пара осциллограмм реального шума имеется. А так - ну да, он более-менее исследован, и зависимости выявлены.

Когда время на разработку бесконечно, а задача "крутят-не крутят" основная - может быть. Когда задача даже не второстепенная (но без неё не примут), а проект идёт по методике Fixed Price, за любой лишний час руководитель... В общем, отругает.

Поэтому самообман, не самообман... Работает, у Заказчика претензий нет - ладушки. Я не зря про часы на обучение нейросети сказал. Кто бы их оплатил,?

В общем, для потенциометра - когда надо идеально, и есть много времени - может, Вы и правы. А так - оставим нейросетевые вещи для случаев, которые иначе не решаются. Ну, или для учёных, у которых сроки не поджимают, а результат им нужен не приемлемый, а идеальный.

Проще? Усреднять надо хошь-не хошь, чтобы взяв основное значение получить не что-то прыгнувшее. Усреднение нужно ради точности основного функционала.

А алгоритм "крутит-не крутит" занимает экран, который не требует обучения (то есть, часов на подготовку данных). И требует одно 16 битное слово в памяти данных.

Здесь самое главное - не забывать, что перед нами восьмибитный контроллер с малыми объёмами как памяти программ, так и памяти данных. Вдобавок - ещё и со скромной тактовой частотой... И всё это нужно не только для задачи стабилизации потенциометра, но и для выполнения основного функционала. Стабилизация потенциометра - очень второстепенная задача, на которую не стоит класть много ресурсов. Да и времени на подбор параметров фильтров - тоже. Так что все сложные фильтры лучше сразу отбросить. Только простые!

А самое обидное - всё так шумело, что фильтры пришлось заполировать алгоритмом, добавляющим гистерезис.

Но это замечательно, что под статьёй будет перечисление видов фильтров! Кто найдёт её во время поиска решения, тот и этот комментарий найдёт.

"Налить масла в переменник" - классическая операция. У меня валяется старый лабораторный БП, у которого настройки на месте не стоят. Сильно не стоят. Как раз резисторы надо промаслить. Они дурят.

С другой стороны, у очень дорогой мышки ребёнок уже два раза энкодер в колёсике маслил. Первый раз было забавно. Мышка дурила во время поездки в Китай. Тот, у кого мы были, спустился с нами на подземную парковку, открыл капот своего автомобиля и капнул с кончика масляного щупа.

Так что тут проблема общая. Но зато пока сам крутящийся прибор целый - показания у энкодера стабильнее. Пусть даже у контактных пропуски бывают периодически. В нашем случае, они были бы не принципиальными. А потом - ну отключить от разъёма и поставить новый.

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

Ну, у в ту железку, про которую статья, его поставили те, кто в двадцатые годы 21 века делает схемы на Мегах. Вот в нулевые я лично на них поделки делал, но прогресс не стоит на месте. Схемотехник целевой железки явно так привык с более древних времён. И к тем процессорам, и к той обвязке. И этот схемотехник работает не у нас (и даже не у нас в стране).

Я опрашивал АЦП редко. В конкретной системе, всё было очень кисло. Стал бы я городить огород, если бы всё решилось так просто. Я бы даже щуп осциллографа в руки не взял бы.

Удивился числу 250 в накопителе усреднения. Проще использовать 256 и потом откинуть младший байт. Особенно на чахлых 8-битниках, но не только.

Вы не поверите, но как я ждал этого комментария! Потому что это очевидно, поэтому я думал об этом, и осмысленно отбросил такой вариант. 250 - это "близко к максимальному значению uint8_t". При выходе на 256, пришлось бы или менять счётчик на uint16_t с усложнением операции "+- 1" на каждом шаге, или сравнивать его с нулём, что могло бы ввести в ступор тех, кто потом будет сопровождать код. У нас же восьмибитная архитектура AVR!

Поэтому я предпочёл совместить классический, понятный даже школьнику, код с какой-никакой, а оптимизацией. Одно деление выполнится быстрее, чем загрузка-сложение с переносом-выгрузка, проведённые 256 раз...

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

То есть, хранить весь этот массив в хилой памяти Меги.

Ну и самое главное: после усреднения для стабилизации накладываем гистерезис нужной амплитуды.

Вот как раз гистерезис и достигается в финальном разделе. Да, я забыл это слово в пылу битвы (были там и другие баталии, не описанные в статье), но мне его уже напомнили в комментариях.

Вообще я в своих поделках давно от переменных резисторов ушел к энкодерам.

Золотые слова! Но мы не могли влиять на схему, что отмечено в тексте.

При тех шумах и иголках, получим такое дёрганье, что ууууууу! Я начинал с малого числа опросов. Частоту АЦП ещё сильнее уменьшить нельзя. Коэффициент деления - максимальный.

А ещё меня смущает то, что в блок-схеме АЦП значение на входе компаратора отмечено, как фиксирующееся, а вот опорное AREF - нет. Считается, что его-то должны идеально застабилизировать и так. Но в нашем конкретном случае, не факт, что это было сделано идеально у разработчиков схемы. Поэтому если ещё уменьшить частоту, там уже ошибки оцифровки могут полезть.

Information

Rating
1,487-th
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity