Pull to refresh
187
41
Павел Локтев @EasyLy

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

Send message

Усреднение сигнала - одно, а усреднение дребезга контактов - другое.

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

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

Все дребезги контактов если и возникали, то на стороне Заказчика. И как видим, они были такими, что полученный результат всё равно был принят. А все проблемы на моей стороне прекрасно воспроизводились без них.

Как видно из комментариев, все сделанные шаги - типовые. Это не колхоз, а стандартный путь решения. Усреднение, фильтрация, гистерезис. А писать... Вот как раз многие носители знаний этим вопросом задаются, потом приходится велосипеды изобретать. И только на такие статьи сходятся комментаторы, которые говорят, мол, а чего такого? Вот три шага, всё же ясно...

Им ясно, но не опубликовано... Поэтому ясно ограниченному кругу лиц. Теперь - опубликовано...

Нашего шефа на выставке ребята из Nordic убедили, что энергосбережение лучше организовывать на их чипах. Он приехал, попробовал... И очень много выиграл в плане экономии по сравнению с тем, чего сам же и добился на ESP32 (не помню, C или S).

Но в обсуждаемой статье, экономией электроэнергии и не пахло.

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

Чуть не забыл. У всех современных архитектур DMA обычно является обязательным. А через это можно многие интерфейсные вещи реализовывать по принципу "Настроил и забыл". Не надо следить, пришло чего-то там или нет. Я однажды на контроллере FreeScale настроил цепочку из трёх DMA так, что данные из линейной камеры принимались вообще без участия процессора. Там просто можно было цепочки настраивать.

Сначала я программно запускал оцифровку на АЦП. Камера в той схеме выдавала всё в аналоговом виде. По факту окончания, результат записывался в буфер с инкрементом указателя, после чего автоматически запускался второй блок DMA в цепочке. Тот в GPIO отправлял два слова. По результату, камера получала импульс "точка принята". Дальше запускался третий блок DMA, который писал в порт АЦП команду приёма. Точно такую же, какую в начале процесса посылал процессор. Система была подготовлена к тому, что в конце оцифровки начнётся работа первой записи в цепочке.

Приём из АЦП был закольцован на 128 байт (камера на машинке Freescale Cup давала линию из 128 точек). По окончании, формировался запрос на прерывание, и приём автоматически переходил к началу.

Правда, это не свойство архитектуры Cortex M. DMA каждый производитель делает, как считает нужным. На том же STM32, думаю, такой чисто аппаратный трюк бы не сработал, но всё равно, часть работ тоже можно было бы автоматизировать. Не отвлекать программу на каждую мелочь. На STM32 я усреднял и фильтровал данные от термодатчика как раз при помощи DMA. Во времена, когда придумывали Меги, это ещё не было принято, поэтому там DMA нет.

Ядра 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, но её я на низком уровне не изучал. Она тоже не восьмибитная, уже этим хороша.

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

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

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

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

1
23 ...

Information

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