Всем привет!
Пришла зима, а с ней и задача проверить теплоизолирующие свойства
MLX90640. Что это?
А это, собственно, тепловизионная матрица с микроконтроллером на борту. Производства ранее неизвестной мне фирмы Melexis. Тепловизионная матрица имеет размерность 32 на 24 пикселя. Это немного, но при интерполяции изображения вроде как достаточно, чтобы хоть что-нибудь разглядеть.
Сенсор выпускается в двух модификациях, корпуса которых отличаются углом обзора матрицы. Более приземистая конструкция A обозревает окружающий мир под углом 110 (по горизонтали) на 75 (по вертикали) градусов. B — под 55 на 37,5 градусов соответственно. Корпус устройства имеет только четыре вывода — два для питания, два для общения с управляющим устройством по I2C интерфейсу. Даташит для заинтересовавшихся можно скачать здесь.
А что тогда такое GY-MCU90640?
Китайские товарищи ставят MLX90640 на плату с еще одним микроконтроллером на борту (STM32F103). По всей видимости, для более простого управления матрицей. Называется всё это хозяйство GY-MCU90640. И стОит на момент приобретения (конец декабря 2018 года) в районе 5 тыс. руб. Выглядит следующим образом:
Как можно заметить, есть две разновидности плат, с узко- или широкоугольной версией сенсора на борту.
Какая из версий подойдет лучше всего? Хороший вопрос, к сожалению, возник он у меня только после того, как модуль был уже заказан и получен. Почему-то в момент заказа я не обратил внимания на эти нюансы. А зря.
Более широкоугольная версия будет хороша на самоходных роботах или в системах безопасности (поле зрения будет больше). Согласно даташиту, она обладает также меньшим шумом и большей точностью измерения.
Но для задач визуализации я бы больше рекомендовал более «дальнобойную» версию B. По одной очень значимой причине. В перспективе при съемке её можно разворачивать (вручную или на платформе с приводом) и делать составные «фото», увеличивая тем самым более чем скромное разрешение в 32 на 24 пикселя. Собирать тепловизионные снимки 64 на 96 пикселей, например… Ну да ладно, в дальнейшем по тексту фото будут с моей широкоугольной версии A.
Подключение к Raspberry PI
Управлять тепловизионным модулем можно двумя способами:
- Закоротить перемычку «SET» на плате и по I2C обращаться напрямую к внутреннему микроконтроллеру MLX90640.
- Оставить перемычку в покое и общаться с модулем через установленный на плате STM32F103 через RS-232 подобный интерфейс.
Если вы пишете на C++, наверное, будет удобнее проигнорировать лишний микроконтроллер, закоротить перемычку и воспользоваться API от производителя, которое лежит вот здесь.
Скромным питонистам тоже можно пойти первым путём. Вроде как есть пара библиотек на Python (здесь и здесь). Но у меня, к сожалению, с ходу ни одна не заработала.
Продвинутым питонистам можно в принципе написать драйвер управления модулем на Питоне. Процедура получения кадра подробно расписана в даташите. Но тогда придется прописывать все калибровочные процедуры, что кажется слегка обременительным. Поэтому пришлось пойти вторым путем. Он оказался умеренно тернистым, но вполне проходимым.
Благодаря прозорливости китайских инженеров или просто счастливому стечению обстоятельств у платки оказалось очень удачное расположение выводов:
Осталось только поставить колодку и вставить платку в разъем малины. На плате установлен преобразователь 5 в 3 Вольта, поэтому нежным Rx и Tx выводам Raspberry вроде как ничего не угрожает.
Надо добавить, что подключение по первому варианту тоже возможно, но требует бОльших трудозатрат и паяльной сноровки. Плату нужно ставить с другой стороны разъема Raspberry (изображено на заглавной фотографии этого поста).
Софт
На известном китайском сайте для доступа к GY-MCU90640 предлагается вот такое чудо:
По всей видимости, должно быть и какое-то описание протокола взаимодействия с установленным на плате микроконтроллером, по которому сей программный продукт работает! После недолгого общения с продавцом платки (респект этим уважаемым господам) таковой протокол мне и был выслан. Он оказался в pdf и на чистом китайском языке.
Благодаря переводчику Гугла и активному копипастингу примерно через час-полтора протокол был расшифрован, желающие могут ознакомиться с ним на Github. Оказалось, что платка понимает шесть базовых команд, среди которых есть запрос кадра по COM порту.
Каждый пиксель матрицы — это, по сути, значение температуры объекта, на который этот пиксель смотрит. Значение температуры в градусах Цельсия, умноженных на 100 (двухбайтное число). Собственно, есть даже специальный режим, в котором платка будет слать кадры с матрицы на Raspberry 4 раза в секунду.
Скрипт для получения тепловизионных снимков здесь:
"""MIT License
Copyright (c) 2019
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE."""
import serial, time
import datetime as dt
import numpy as np
import cv2
# function to get Emissivity from MCU
def get_emissivity():
ser.write(serial.to_bytes([0xA5,0x55,0x01,0xFB]))
read = ser.read(4)
return read[2]/100
# function to get temperatures from MCU (Celsius degrees x 100)
def get_temp_array(d):
# getting ambient temperature
T_a = (int(d[1540]) + int(d[1541])*256)/100
# getting raw array of pixels temperature
raw_data = d[4:1540]
T_array = np.frombuffer(raw_data, dtype=np.int16)
return T_a, T_array
# function to convert temperatures to pixels on image
def td_to_image(f):
norm = np.uint8((f/100 - Tmin)*255/(Tmax-Tmin))
norm.shape = (24,32)
return norm
########################### Main cycle #################################
# Color map range
Tmax = 40
Tmin = 20
print ('Configuring Serial port')
ser = serial.Serial ('/dev/serial0')
ser.baudrate = 115200
# set frequency of module to 4 Hz
ser.write(serial.to_bytes([0xA5,0x25,0x01,0xCB]))
time.sleep(0.1)
# Starting automatic data colection
ser.write(serial.to_bytes([0xA5,0x35,0x02,0xDC]))
t0 = time.time()
try:
while True:
# waiting for data frame
data = ser.read(1544)
# The data is ready, let's handle it!
Ta, temp_array = get_temp_array(data)
ta_img = td_to_image(temp_array)
# Image processing
img = cv2.applyColorMap(ta_img, cv2.COLORMAP_JET)
img = cv2.resize(img, (320,240), interpolation = cv2.INTER_CUBIC)
img = cv2.flip(img, 1)
text = 'Tmin = {:+.1f} Tmax = {:+.1f} FPS = {:.2f}'.format(temp_array.min()/100, temp_array.max()/100, 1/(time.time() - t0))
cv2.putText(img, text, (5, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 0), 1)
cv2.imshow('Output', img)
# if 's' is pressed - saving of picture
key = cv2.waitKey(1) & 0xFF
if key == ord("s"):
fname = 'pic_' + dt.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '.jpg'
cv2.imwrite(fname, img)
print('Saving image ', fname)
t0 = time.time()
except KeyboardInterrupt:
# to terminate the cycle
ser.write(serial.to_bytes([0xA5,0x35,0x01,0xDB]))
ser.close()
cv2.destroyAllWindows()
print(' Stopped')
# just in case
ser.close()
cv2.destroyAllWindows()
Результаты
Скрипт опрашивает тепловизионную матрицу и выводит кадры на консоль монитора, на который подключен Raspberry PI, 4 раза в секунду. Этого достаточно для того, чтобы не испытывать большого дискомфорта при съемке объектов. Для визуализации кадра используется пакет OpenCV. При нажатии на кнопку «s» в папку со скриптом сохраняются тепловизионные «тепловые карты» в формате jpg.
Для большей информативности я вывел минимальную и максимальную температуры на кадре. То есть глядя на окраску, можно видеть какая примерно температура у наиболее разогретых или охлажденных предметов. Погрешность измерения — примерно градус с бОльшую сторону. Тепловой диапазон задан от 20 до 40 градусов. Выход из скрипта по нажатию Ctrl + C.
Скрипт работает примерно одинаково и на Raspberry Pi Zero W и на Pi 3 B+. Я установил VNC сервер на смартфон. Таким образом, взяв в руки малину, подключенную к powerbank'у и смарфтон с запущенным VNC можно получить переносной тепловизор с возможностью сохранения тепловых снимков. Возможно, это не совсем удобно, но вполне функционально.
После первого запуска возможно некорректное измерение максимальной температуры. В этом случае нужно выйти из скрипта и запустить повторно.
Вот, собственно, и всё на сегодня. Эксперимент с самодельным тепловизором получился удачным. С помощью данного девайса вполне можно провести тепловизионное обследование дома своими силами, например. Если у кого-то возникнут дополнительные мысли насчет областей применения подобных штук, добро пожаловать в комментарии.
Всем удачной трудовой недели и до новых встреч!
UPD: В комментариях меня просили сделать снимок дома с улицы. Выкладываю здесь. Из-за меньшего контраста температур, чем в помещении, снимки получились не особо информативными. На фото сверху дом целиком с двух сторон. На нижних — фото разных окон.
В коде изменил только температурный диапазон. Вместо +20...+40 поставил -10...+5.