ATmega16 + DS18B20 + LED + Matlab/Simulink = AR

от автора

Задумал я как-то поиграться с датчиками DS18B20. Да не просто получить значения температуры (что умеет каждый), но и как-то ее визуализировать. Возникла простая идея. Ставим вебкамеру. Зажигаем светик на четном кадре, на не четном — тушим. Вычитаем картинку — остается только вспышка. По ней и ищем местоположение датчика, который физически привязан к светодиоду в пространстве. А дальше математическая обработка. Ну и все это в симулинке. Под катом описано как получить красивые картиночки. А для тех кто разбираться не желает — предлагаю посмотреть эксперименты в конце статьи.


Схемотехника

Схемотехника крайне проста. Сердцем является ATmega16. Все датчики DS18B20 висят на одном пине (в моем случае на PB0 порта PORTB). Сам пин подтянут к напряжению питания через резистор 4.7 кОм. Схема масштабируема. Картинка кликабельна.

Все LEDы подключены к порту PORTA через ограничительные резисторы. Серый полигон означает что данный LED физически связан с DS18B20. Вывод ресет подтянут к высокому через резистор в 10 кОм для избежания случайного сброса из-за наводок. Микроконтроллер тактируется кварцем в 16 МГц. Ставить как можно ближе к выводам. Нагрузочные емкости используются внутренние. Настраиваются через фъюзы. Отдельно выведены разъемы ICP (для заливки прошивки) и UART для «общения». Ёмкости С1 (электролит 10 мкФ) и C2 (керамика 100 нФ). Ставить как можно ближе к выводам питания микроконтроллера. Используются во избежания случайных сбросов во время переключения нагрузки.

Схемотехника в сборе

Что представляет собой серый полигон

Прошивка + алгоритм работы

Прошивка писалась на C в Atmel Studio 7 IDE. Исходники выложены на GitHub. Код максимально документируем.
Проект разбит на несколько уровней абстракции:

  • Hardware — наинизший уровень, максимальная привязка к железу. Работа с периферией микроконтроллера.
  • Middleware — связующий уровень между Hardware и Drivers. К примеру реализация протокола 1-Wire.
  • Drivers — уровень драйверов. К примеру работа с микросхемой DS18B20.
  • Application — наивысший уровень абстракции. Например получение и передача температуры по UART.

Бегло пробежимся по main функции. Вначале стоит таблица ROM адресов. Необходимо чтобы в нулевой позиции был адрес датчика физически связанного с нулевым светодиодом (висящем на PA0 порта PORTA) ну и т.д. Для получения ROM есть функция sendROMToUART. Нужно лишь помнить что датчик должен быть на шине один, иначе будет коллизия адресов.

main

int main(void) {	 	const uint8_t ROM[][sizeof(ROM_T)] = /* ROM array */ 	{ 		{0x26, 0x00, 0x00, 0x04, 0x4B, 0x15, 0x89, 0x28}, // 0 		{0x71, 0x00, 0x00, 0x04, 0x4A, 0xC0, 0x65, 0x28}, // 1 		{0xA5, 0x00, 0x00, 0x04, 0x4A, 0xCB, 0xCE, 0x28}, // 2 		{0x41, 0x00, 0x00, 0x04, 0x4A, 0xAC, 0x65, 0x28}, // 3 		{0x22, 0x00, 0x00, 0x04, 0x4B, 0x06, 0x0D, 0x28}, // 4 		{0x86, 0x00, 0x00, 0x04, 0x4A, 0xF6, 0x46, 0x28}  // 5 	}; 	 	uint8_t nDevices = sizeof(ROM) / sizeof(ROM_T); /* Number of DS18B20 devices */ 	 	initUART(MYUBRR); /* Initialization of UART with appropriate baudrate */ 	initTimer0(); /* Initialization of Timer/counter0 */ 	initLED(nDevices); /* Initialization of LEDs */ 	 	{ /* DS18B20s initialization */ 		uint8_t nDevices = sizeof(ROM) / sizeof(ROM_T); /* Number of DS18B20 devices */ 		ROM_T *pROM = (ROM_T *)&ROM; /* Pointer to ROM array */ 		 		initDQ(); /* Initialization of DQ pin */ 		 		while (nDevices--) /* For all DS18B20 */ 			initDS18B20(pROM++, RESOLUTION_11BIT); /* Initialization of DS18B20 with appropriate resolution */ 	} 	 	sei(); /* Global enable interrupts */ 	 	while (1) /* Infinite loop */ 	{ 		sendTemperatureToUART((ROM_T *)&ROM, nDevices); /* Execute function routine */ 	} } 

Далее идет инициализация периферии и самих DS-ок соответствующим разрешением. От него зависит период выборки температуры. Для 11 бит это 375 мс. В бесконечном цикле программа беспрерывно вычитывает температуру с каждого датчика и шлет ее по UART.

Работа со светодиодами основана на прерываниях. По UART приходит ID светодиода 2 раза подряд на четном и нечетном кадре. На первом светодиод зажигается. Тушит его таймер через определенное время (в моем случае 15 мс). Второй раз просто игнорируем. Таймер настроен в режиме CTC так, чтобы прерывание происходило раз в 1 мс.

код

volatile uint8_t ledID = 0; /* Current ledID value */ volatile uint8_t ledID_prev = 255;  /* Previous ledID value */ volatile uint8_t duration = FLASH_DURATION; /* Flash duration value */  ISR(USART_RXC_vect) /* UART interrupt handler */ { 	ledID = UDR; /* Assign ledID to receive via UART value */ 	if (ledID != ledID_prev) /* If current ledID equal to previous value */ 	{ 		turnOnLED(ledID); /* Turn on the ledID LED */ 		timer0Start(); /* Start Timer0 */ 		ledID_prev = ledID; /* Previous ledID assign to current */ 		duration = FLASH_DURATION; /* Update LED flash duration */ 	} }  ISR(TIMER0_COMP_vect) /* Timer0 compare interrupt handler */ { 	if (--duration == 0) /* Decrement Duration value each 1ms and if it reach to 0 */ 	{ 		timer0Stop(); /* Stop Timer0 */ 		turnOffAllLED(); /* Turn off all LEDs */ 		timer0Clear(); /* Clear Timer0 counter register */ 	} } 

Участки кода, чувствительные ко времени, а это 1-Wire сигналы, обернуты в ATOMIC_BLOCK конструкцию. Все основные настройки вынесены в global.h. UART работает на скорости 250000. Быстро и без ошибок для кварца в 16 МГц. Драйвер DS18B20 имеет функционал минимально необходимый для данного проекта. Остальное — смотрите код. Будут вопросы — спрашивайте, не стесняйтесь. Отдельно хочется напомнить о настройках фъюзов. В них необходимо выставить возможность тактирования от внешнего кварца иначе будет от внутреннего генератора (а он максимум на 8 МГц и не очень стабильный). Ну и запрограммировать CKOPT бит, иначе кварцы выше 8 МГц не заведутся. У меня High Fuse = 0xD9, Low Fuse = 0xFF.

Модель Simulink + алгоритм работы

Версия Matlab R2015b. В Simulink, помимо встроенной библиотеки, в основном использовались Computer Vision System Toolbox и Image Aquisition Toolbox. Вся модель и сопутствующие файлы выложены на GitHub. Ниже детальное описание с наглядными примерами. Все картинки кликабельны.

Модуль WebCamTemp

Желтым цветом обозначены блоки COM-порта. Отдельно передатчик, приемник и конфигуратор. Настройки порта должны в точности совпадать с микроконтроллерскими (скорость, четность, кол-во бит и т.д.). Приемник принимает температуру, группируя ее в одномерный массив размера [n 1] типа int16, где n — количество DS18B20 ( у меня 6). Каждый элемент этого массива далее делится на 16. Это из даташита стр. 6. Передатчик отправляет текущее значение счетчика Counter. Оно как раз зажигает определенный светодиод. Тикает от 0 до n. Период 2 семпла. Синим сгруппированы блоки отвечающие за отображение/сохранение видеопотока. Зеленым — блоки получения видеоинформации. Собственно сама веб-камера. Тут куча настроек, разных, зависит от производителя. Картинка выдается в серых тонах. Так интереснее. Блок Diff производит разницу между предыдущим и текущим кадрами. Блок Downsample odd выделяет только разницу зажженный — не зажженный светодиод, но не наоборот. Блок Downsample even пропускает только те кадры, на которых светодиод потушен.

Img diff

Слева оригинал, справа — разностная картинка. По ней в дальнейшем ищутся координаты светодиодов (соответственно датчиков тоже). Лучше смотреть по кадрам, но видимо в ютубе такой возможности нет. Можно поставить скорость 0.25.

Img gray

Слева оригинал, справа — картинка, на которую впоследствии накладывается карта. После выхода в стабильный режим видно, что светодиоды не моргают. Сделано чтобы не напрягало.

Frame Rate Display отображает текущий FPS. Вся обработка идет в блоке LEDs. Его мы рассмотрим следующим.

Модуль LEDs

Фиолетовым сгруппированы блоки получения 2D гауссиан. Нам нужны две: Interference и Opacity. Отличаются сигмой. Их центр в точке максимума (где горел светодиод). Координаты находятся блоком Maximum. Вместо постоянного генерирования таких гауссиан (а экспонента очень трудоемкая мат. операция) было принято решение их вырезать. Для этого, в m-файле, генерируются две Int и Op размерами в 2 раза больше с центром посредине, из которых, дальше, просто кропаются нужные, блоками Crop interference и Crop opacity.

Пример работы

Сверху слева — входящее разностное изображение. Снизу большое — статическое изображение с разрешением в два раза больше требуемого. Бегающий прямоугольник — область которая вырезается с нужным разрешением. Вверху справа — то что имеем на выходе. Лучше смотреть на скорости 0.25.

Зеленым обведены блоки памяти. Их назначение — хранить координаты и гауссианы для каждого светодиода. Детальнее рассмотрим ниже. Блок To color предназначен для постройки распределения температур и цветовой карты. Также будет рассмотрен ниже. Блок композиции сигнала Compositing смешивает два изображения Image1 и Image2 по следующему закону:

Блок Insert Text накладывает форматированный текст (в данном случае температуру) на изображение. Принимает n переменных и координат в формате [X Y]. Можно выбирать шрифт и его размер. Блоки внутри красного прямоугольника реализуют алгоритм скользящего среднего. Переходы становятся менее дерганными, что сохраняет нервы и радует глаз.

Пример

Слева оригинал, справа — скользящее среднее для 8-ми выборок. Когда температуры всех датчиков отличаются между собой на десятые доли градусов (парочку разрешений датчика) такие рывки имеют место быть.

Модули memory

Memory interference и Memory opacity хранят наборы 2D гауссиан, Memory Pts — координаты для каждого LED.

Memory interference и Memory opacity

Эти два модуля идентичны. На вход Address подается номер ячейки куда запишется входной гауссиан. Приходит от счетчика. Совпадает с номером горящего светодиода. Блок Delay модуля LEDs служит для дополнительной синхронизации (пока приходит картинка, счетчик уже успевает оттикать). Так что все у нас синхронизировано. Сигнал Enable разрешает запись. Имеет истинное значение, если значение максимума выше порога (смотрите блок Maximum и Threshold модуля LEDs). Если значение ложно — содержание ячейки не меняется. На выходе все склеивается по третьему измерению. Получается такой себе бутерброд размера [H W n], где HxW — разрешение веб-камеры, ну а n — количество датчиков/светодиодов.

Memory Pts

Идентичен двум предыдущим за небольшим исключением. На выходе все склеивается не по третьему а по первому измерению. А блок Permute Matrix просто меняет местами столбцы, так как формат координат [Y X], а нужен [X Y].

Модуль To color

Зеленым — обработка Opacity. Суммируем входной массив по третьему измерению. Нормализуем его по максимуму. Умножаем на значение gain (от 0 до 1) (1). Итого имеем массив с наложенными друг на друга гауссианами и максимумом gain. Используется как Factor для смешения изображений. Красное — получение цветовой карты температур. Тут немного другая математика, все тех же гауссиан. Описывается формулой (2). Грубо говоря, температура в произвольной точке — средневзвешенная ото всех датчиков. Но влияние каждого датчика в процентном соотношении пропорционально значению гауссианы в ней. Сумма всех принимается за 100%.

Осталось рассмотреть как распределение температур превращается в цветовую карту. То что обведено синим превращает конкретную температуру в значение между 0 и 1. В красной зоне блоком Prelookup вычисляется индекс, по которому ищется значение красного, зеленого и синего. Массив цветов содержит 64 значения. Промежуточные вычисляются методом интерполяции. Из особенностей есть два режима: относительный и абсолютный. В относительном самому холодному и горячему месту соответствует минимум и максимум входного массива. В абсолютном — некие константные значения. В первом удобнее смотреть профиль распределения температур. В другом — ее абсолютные изменения.

m-файл

Выполняется вначале, перед симуляцией, внося переменные в Workspace.

код

H = 480; % Height of image W = 640; % Width of image minT = 20; % Min temperature maxT = 25; % Max temperature sigmaInt = 40; % Sigma interference sigmaOp = 80; % Sigma opacity gain = 1.0; % Gain value T = 0.3; % Threshold value nAvr = 8; % number of means % ------------------------------------------------------ [M,N] = meshgrid(-W:W, -H:H); % Meshgrid function  R = sqrt(M.^2 + N.^2); % Distance from the center  Int = normpdf(R, 0, sigmaInt); % 2D gaussian for interference Op = normpdf(R, 0, sigmaOp); % 2D gaussian for opacity  Int = Int/max(max(Int)); % Normalization of interference gaussian Op = Op/max(max(Op)); % Normalization of opacity gaussian  clear M N R sigmaInt sigmaOp % Delete unused variables from memory  load('MyColormaps','mycmap'); % Load colormap 

Содержит основные управляющие переменные среди которых:

  • H — разрешение видео по высоте.
  • W — разрешение видео по ширине.
  • minT — минимальная абсолютная температура.
  • maxT — максимальная абсолютная температура.
  • sigmaInt — сигма гауссианы Interference.
  • sigmaOp — сигма гауссианы Opacity.
  • gain — максимальное значение Factor.
  • T — порог для избежания ошибок.
  • nAvr — количество усреднений для скользящего среднего.

H и W должно совпадать с текущим в блоке WebCamera. minT и maxT влияют на цветовую карту в режиме абсолютной температуры. T устанавливается от 0 до 1. Иногда происходит рассинхронизация COM-порта и веб-камеры. Фаза разностного изображения может поменяться на 180°. И там где должен быть максимум — находится минимум. А координату система может выбрать произвольную — не соответствующую действительности. Для этого и существует система порога. nAvr количество усреднений в скользящем среднем. Чем оно больше — тем плавнее переходы, но теряется актуальность (появляется временной сдвиг). Чтобы понять влияние оставшихся переменных, без наглядных примеров не разобраться.

Влияние sigmaInt

При картинка вырождается в некое подобие разбиения Вороного. При вся карта будет одного цвета соответствующего средней, от всех датчиков, температуре. При увеличении — границы как бы размываются.

Влияние sigmaOp

Устанавливает скорость спада прозрачности от расстояния.

Влияние gain

По-сути устанавливает прозрачность карты. Само значение соответствует «прозрачности» самого «не прозрачного» пикселя карты.

Эксперименты

Ниже приведены некоторые эксперименты.

Открытое окно

Датчики разбросаны на кровати у окна. Окно открывается и через время закрывается. Наглядный пример различия относительного (слева) и абсолютного (справа) режимов. С помощью первого удобно рассматривать распределение, а второго — как распространяется холод или восстанавливается тепло.

Подоконник

Датчики расположены вдоль подоконника. Окно открывается — профиль меняется. Наглядно видны самая холодная и теплая зоны.

Сверху теплее?

Говорят мол сверху теплее. Этот эксперимент полное тому подтверждение. На 10-ой секунде окно открывается, на 30-ой — закрывается.

Заключение

Такая схема не заменит полноценный тепловизор. Но и он не способен увидеть распространение воздушных масс. А по цене эта конструкция несоизмеримо ниже. В ней можно использовать другую цветовую карту. Можно вместо гауссиан взять другие функции. Можно даже изменить законы построения. Или переписать на OpenCV + QT для скорости и удобства. Но того чего задумал я — достиг. Просто Just For Fun.

ссылка на оригинал статьи https://geektimes.ru/post/271592/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *