DIY тепловизор на Raspberry PI или «Кажется теперь я знаю, чем займусь этим летом»

в 6:13, , рубрики: diy или сделай сам, python, Raspberry Pi, гаджеты, Разработка на Raspberry Pi, тепловизор, умный дом

image

Всем привет!

Пришла зима, а с ней и задача проверить теплоизолирующие свойства построек загородной резиденции дачи. А тут ещё оказалось, что на известном китайском сайте появились вполне доступные тепловизионные модули. Не собрать ли себе экзотическую и, возможно, даже полезную вещь — самодельный тепловизор? Почему бы и нет, вроде и Raspberry где-то валялась… Что из этого вышло — расскажу под катом.

MLX90640. Что это?

А это, собственно, тепловизионная матрица с микроконтроллером на борту. Производства ранее неизвестной мне фирмы Melexis. Тепловизионная матрица имеет размерность 32 на 24 пикселя. Это немного, но при интерполяции изображения вроде как достаточно, чтобы хоть что-нибудь разглядеть.

image

Сенсор выпускается в двух модификациях, корпуса которых отличаются углом обзора матрицы. Более приземистая конструкция A обозревает окружающий мир под углом 110 (по горизонтали) на 75 (по вертикали) градусов. B — под 55 на 37,5 градусов соответственно. Корпус устройства имеет только четыре вывода — два для питания, два для общения с управляющим устройством по I2C интерфейсу. Даташит для заинтересовавшихся можно скачать здесь.

А что тогда такое GY-MCU90640?

Китайские товарищи ставят MLX90640 на плату с еще одним микроконтроллером на борту (STM32F103). По всей видимости, для более простого управления матрицей. Называется всё это хозяйство GY-MCU90640. И стОит на момент приобретения (конец декабря 2018 года) в районе 5 тыс. руб. Выглядит следующим образом:

image

Как можно заметить, есть две разновидности плат, с узко- или широкоугольной версией сенсора на борту.

Какая из версий подойдет лучше всего? Хороший вопрос, к сожалению, возник он у меня только после того, как модуль был уже заказан и получен. Почему-то в момент заказа я не обратил внимания на эти нюансы. А зря.

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

image

Но для задач визуализации я бы больше рекомендовал более «дальнобойную» версию B. По одной очень значимой причине. В перспективе при съемке её можно разворачивать (вручную или на платформе с приводом) и делать составные «фото», увеличивая тем самым более чем скромное разрешение в 32 на 24 пикселя. Собирать тепловизионные снимки 64 на 96 пикселей, например… Ну да ладно, в дальнейшем по тексту фото будут с моей широкоугольной версии A.

Подключение к Raspberry PI

Управлять тепловизионным модулем можно двумя способами:
1. Закоротить перемычку «SET» на плате и по I2C обращаться напрямую к внутреннему микроконтроллеру MLX90640.
2. Оставить перемычку в покое и общаться с модулем через установленный на плате STM32F103 через RS-232 подобный интерфейс.

Если вы пишете на C++, наверное, будет удобнее проигнорировать лишний микроконтроллер, закоротить перемычку и воспользоваться API от производителя, которое лежит вот здесь.

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

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

Благодаря позорливости китайских инженеров или просто счастливому стечению обстоятельств у платки оказалось очень удачное расположение выводов:

image

Осталось только поставить колодку и вставить платку в разъем малины. На плате установлен преобразователь 5 в 3 Вольта, поэтому нежным Rx и Tx выводам Raspberry вроде как ничего не угрожает.

Надо добавить, что подключение по первому варианту тоже возможно, но требует бОльших трудозатрат и паяльной сноровки. Плату нужно ставить с другой стороны разъема Raspberry (изображено на заглавной фотографии этого поста).

Софт

На известном китайском сайте для доступа к GY-MCU90640 предлагается вот такое чудо:

image

По всей видимости, должно быть и какое-то описание протокола взаимодействия с установленным на плате микроконтроллером, по которому сей программный продукт работает! После недолгого общения с продавцом платки (респект этим уважаемым господам) таковой протокол мне и был выслан. Он оказался в pdf и на чистом китайском языке.

Благодаря переводчику Гугла и активному копипастингу примерно через час-полтора протокол был расшифрован. Надо будет не забыть выложить на Github, кстати. Оказалось, что платка понимает шесть базовых команд, среди которых есть запрос кадра по COM порту.

Каждый пиксель матрицы — это, по сути, значение температуры объекта, на который этот пиксель смотрит. Значение температуры в градусах Цельсия, умноженных на 100 (двухбайтное число). Собственно, есть даже специальный режим, в котором платка будет слать кадры с матрицы на Raspberry 4 раза в секунду.

Скрипт для получения тепловизионных снимков здесь:

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.uint16)
	
	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.

image

Для большей информативности я вывел минимальную и максимальную температуры на кадре. То есть глядя на окраску, можно видеть какая примерно температура у наиболее разогретых или охлажденных предметов. Погрешность измерения — примерно градус с бОльшую сторону.

Тепловой диапазон задан от 20 до 40 градусов. Выход из скрипта по нажатию Ctrl + C. Из-за реализованного протокола платы (температура представлена unsigned integer) тепловизор может измерять только положительные температуры. Похоже, для измерения отрицательных нужно будет адресовать сенсор уже напрямую.

image

Скрипт работает примерно одинаково и на Raspberry Pi Zero W и на Pi 3 B+. Я установил VNC сервер на смартфон. Таким образом, взяв в руки малину, подключенную к powerbank'у и смарфтон с запущенным VNC можно получить переносной тепловизор с возможностью сохранения тепловых снимков. Возможно, это не совсем удобно, но вполне функционально.

После первого запуска возможно некорректное измерение максимальной температуры. В этом случае нужно выйти из скрипта и запустить повторно.

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

Всем удачной трудовой недели и до новых встреч!

Автор: Walker2000

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js