Задумал я как-то поиграться с датчиками 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. Нужно лишь помнить что датчик должен быть на шине один, иначе будет коллизия адресов.
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 пропускает только те кадры, на которых светодиод потушен.
Frame Rate Display отображает текущий FPS. Вся обработка идет в блоке LEDs. Его мы рассмотрим следующим.
Модуль LEDs
Фиолетовым сгруппированы блоки получения 2D гауссиан. Нам нужны две: Interference и Opacity. Отличаются сигмой. Их центр в точке максимума (где горел светодиод). Координаты находятся блоком Maximum. Вместо постоянного генерирования таких гауссиан (а экспонента очень трудоемкая мат. операция) было принято решение их вырезать. Для этого, в m-файле, генерируются две Int и Op размерами в 2 раза больше с центром посредине, из которых, дальше, просто кропаются нужные, блоками Crop interference и Crop opacity.
Зеленым обведены блоки памяти. Их назначение — хранить координаты и гауссианы для каждого светодиода. Детальнее рассмотрим ниже. Блок To color предназначен для постройки распределения температур и цветовой карты. Также будет рассмотрен ниже. Блок композиции сигнала Compositing смешивает два изображения Image1 и Image2 по следующему закону:
Блок Insert Text накладывает форматированный текст (в данном случае температуру) на изображение. Принимает n переменных и координат в формате [X Y]. Можно выбирать шрифт и его размер. Блоки внутри красного прямоугольника реализуют алгоритм скользящего среднего. Переходы становятся менее дерганными, что сохраняет нервы и радует глаз.
Модули memory
Memory interference и Memory opacity хранят наборы 2D гауссиан, Memory Pts — координаты для каждого LED.
Модуль 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 количество усреднений в скользящем среднем. Чем оно больше — тем плавнее переходы, но теряется актуальность (появляется временной сдвиг). Чтобы понять влияние оставшихся переменных, без наглядных примеров не разобраться.
Эксперименты
Ниже приведены некоторые эксперименты.
Открытое окно
Датчики разбросаны на кровати у окна. Окно открывается и через время закрывается. Наглядный пример различия относительного (слева) и абсолютного (справа) режимов. С помощью первого удобно рассматривать распределение, а второго — как распространяется холод или восстанавливается тепло.
Подоконник
Датчики расположены вдоль подоконника. Окно открывается — профиль меняется. Наглядно видны самая холодная и теплая зоны.
Сверху теплее?
Говорят мол сверху теплее. Этот эксперимент полное тому подтверждение. На 10-ой секунде окно открывается, на 30-ой — закрывается.
Заключение
Такая схема не заменит полноценный тепловизор. Но и он не способен увидеть распространение воздушных масс. А по цене эта конструкция несоизмеримо ниже. В ней можно использовать другую цветовую карту. Можно вместо гауссиан взять другие функции. Можно даже изменить законы построения. Или переписать на OpenCV + QT для скорости и удобства. Но того чего задумал я — достиг. Просто Just For Fun.
ссылка на оригинал статьи https://geektimes.ru/post/271592/
Добавить комментарий