在Raspberry PI上DIY热成像仪或“现在我想我知道我今年夏天将要做什么”

图片

大家好!

冬天来了,随之而来的任务是检查夏天住所的乡村住所建筑物的隔热性能。 然后发现,在著名的中国网站上出现了价格合理的热成像模块。 是否可以组装奇特的,甚至有用的东西-家用热成像仪? 为什么不呢,就像Raspberry躺在某处...结果如何-我会告诉你削减的余地。

MLX90640。 这是什么


实际上,这是带有板上微控制器的热成像矩阵。 生产以前未知的Melexis公司。 热成像矩阵的尺寸为32 x 24像素。 这虽然不多,但是在对图像进行插值时,似乎足以至少分辨出一些东西。

图片

该传感器有两个版本,它们的情况在矩阵的视角不同。 更矮小的结构A以75度(垂直)的110度(水平)俯瞰外部世界。 B-分别在55下37.5度。 设备外壳只有四个输出-两个用于电源,两个用于通过I2C接口与控制设备通信。 感兴趣的数据表可以在这里下载。

那么GY-MCU90640是什么?


中国同志将MLX90640与其他微控制器(STM32F103)一同使用。 显然,这是为了简化矩阵管理。 整个农场称为GY-MCU90640。 收购时(2018年12月底)的成本约为5000卢布。 看起来像这样:

图片

如您所见,有两种类型的板,板上有窄角或广角版本的传感器。

哪个版本最适合您? 不幸的是,这是一个很好的问题,只有在订购和接收模块后才提出。 由于某种原因,在订购时,我没有注意这些细微差别。 但是徒劳。

对于自走式机器人或安全系统,更宽的版本将是一个好选择(视野会更大)。 根据数据表,它还具有更少的噪声和更高的测量精度。

图片

但是对于可视化任务,我宁愿推荐B的更“远程”版本。出于一个非常重要的原因。 将来,在拍摄时,可以将其部署(手动或在具有驱动器的平台上)并拍摄合成的“照片”,从而将适度的32像素分辨率提高24像素以上。 例如,收集64 x 96像素的热图像...好吧,将来,照片将来自广角A版本。

连接到Raspberry PI


有两种方法可以控制热成像模块:

  1. 缩短板上的“ SET”跳线,并使用I2C直接联系内部MLX90640微控制器。
  2. 不用理会跳线,并通过RS-232通过STM32F103板上安装的类似接口与模块通信。

如果使用C ++编写,则忽略额外的微控制器,使跳线短路并使用制造商的API(位于此处)可能会更方便。

谦逊的pythonists也可以走第一条路。 似乎有几个Python库( 此处此处 )。 但不幸的是,没有一个人为我工作。

高级Python专家基本上可以使用Python编写模块控制驱动程序。 数据表中详细描述了获取帧的过程。 但是随后您将必须规定所有校准程序,这似乎有点麻烦。 因此,我不得不走第二条路。 事实证明,它有点刺痛,但相当不错。

得益于中国工程师的真知灼见或偶然的巧合,这条披肩在结论中占据了很好的位置:

图片

仍然仅需放置块并将围巾插入树莓连接器中即可。 板上安装了一个5到3伏的转换器,因此似乎没有什么威胁Raspberry精致的Rx和Tx端子。

应当补充的是,根据第一种选择的连接也是可能的,但是需要更多的劳动和焊接技能。 该板必须安装在Raspberry连接器的另一侧(如本文标题照片所示)。

软体类


在一个著名的中国网站上,提供了这样的奇迹来访问GY-MCU90640:

图片

最有可能的是,应该对与板上安装的微控制器的交互协议进行一些描述,并以此软件产品为依据! 与围巾销售者简短交谈之后(尊重这些尊敬的先生们),就向我发送了这样的协议。 它以pdf和纯中文显示。

借助Google的翻译器和有效的复制粘贴功能,该协议在大约一个半小时内被解密,每个人都可以在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.

image

. , . — . 20 40 . Ctrl + C.

image

Raspberry Pi Zero W Pi 3 B+. VNC . , , powerbank' VNC . , , .

. .

, , . . , . - , .

!

UPD: . . - , , . . — .

image

. +20...+40 -10...+5.

Source: https://habr.com/ru/post/zh-CN435946/


All Articles