选中内容(绿色)时除了会搜索文章名,还会搜索文章内容
点击结果中的文章名进入文章界面后可以按Ctrl+F在页面内搜索
  • 版权:CC BY-SA 4.0
  • 创建:2019-10-15
  • 更新:2020-02-26
使用 K210 + MLX90640 实现热像仪功能, 实时无接触测量温度, `MaxiPy` 和 `CPython` 的简单实现


效果图:

  • K210 对图像画出了热力图,并标记几个重要点(中心店和最高温)的温度
    k210_mlx90640_hot_map2

  • PC Python 实现
    hot.jpg hot2.jpg

  • K210 与摄像头数据融合了的图
    k210_mlx90640_hot_map

传感器及参数

mlx90640
mlx90640_module

几个关键参数如下,更多看芯片手册:

分辨率: 32x24
mlx90640参数

传感器驱动协议

IIC,也有模块加了块单片机做成了串口协议

主控芯片

K210(RISC-V双核,针对AIOT领域, 能胜任本应用,而且使用MaixPy(Micropython)能快速实现看到效果

k210_sipeed_bit

实现过程

  • 获得硬件(K210MLX90640(选用串口模块可以更快上手,这里用的也是串口模块))

  • 下载MaixPy固件并测试运行通过(查看maixpy.sipeed.com获得教程)

  • 驱动MLX90640,由于是串口模块,只需要接收输出的数据即可,协议看模块文档,非常简单;如果是IIC协议会复杂一点点,多花点时间就好了. 然后我们会得到32x24的温度数据

  • 然后用得到的32x24阵列的温度数据构造Image对象(这里需要将温度数据映射到[0,255]的数值,为了对比明显,最好从得到所有像素点的极端值比如[20℃,45℃],然后按比例映射到8bit灰度值空间[0,255]), 对图像进行基本的处理,比如放大(resize,插值),然后伪彩映射可以得到一张好看的图

  • 后面这些部分就是锦上添花的了, 在图片上画十字来标记最高温度点以及可以在图片中心标记温度,这样方便读数

  • 将摄像头数据和传感器图像融合,得到有轮廓的热力图, 摄像头图像需要经过边缘标记,然后根据两个摄像头(图像摄像头和红外成像摄像头)的相对位置调整一下摄像头图片的位置,使用偏移旋转放大缩小达到与热力图吻合的效果

  • 然后显示到显示器即可

代码 (没有优化,比较粗糙,可以运行,上面的图片来自于下面程序的截图):

  1. import lcd, image, sensor
  2. from fpioa_manager import *
  3. from machine import UART
  4. import gc
  5. auto_color = True
  6. max_temp_limit = 300 # max 300
  7. min_temp_limit = 0 # min -40
  8. edge = (-1,-1,-1,-1,8,-1,-1,-1,-1)
  9. offset_x = 0
  10. offset_y = 50
  11. zoom = 1.2
  12. rotate = 0
  13. START_FLAG = 0x5A
  14. sensor_width = 32
  15. sensor_height = 24
  16. lcd_w = 320
  17. lcd_h = 240
  18. fm.register(9, fm.fpioa.UART1_TX)
  19. fm.register(10, fm.fpioa.UART1_RX)
  20. com = UART(UART.UART1, 460800, timeout=1000, read_buf_len=4096)
  21. lcd.init()
  22. sensor.reset()
  23. sensor.set_pixformat(sensor.RGB565)
  24. sensor.set_framesize(sensor.QVGA)
  25. clock = time.clock()
  26. find_frame_flag = False
  27. while 1:
  28. clock.tick()
  29. if not find_frame_flag:
  30. data = 0
  31. flag_count = 0
  32. while 1:
  33. if com.any() <= 0:
  34. continue
  35. data = com.read(1)
  36. if int.from_bytes(data, 'little') == START_FLAG:
  37. flag_count += 1
  38. if flag_count == 2:
  39. find_frame_flag = True
  40. break
  41. else:
  42. flag_count = 0
  43. else:
  44. find_frame_flag = False
  45. max_temp_pos=None
  46. data = com.read(2)
  47. data_len = int.from_bytes(data[:2], "little")
  48. sum = START_FLAG * 256 + START_FLAG + data_len
  49. if auto_color:
  50. min_temp = max_temp_limit
  51. max_temp = min_temp_limit
  52. data = com.read(data_len-2)
  53. target_temp = []
  54. for i in range(data_len//2-1):
  55. v = int.from_bytes(data[i*2:i*2+2], 'little')
  56. sum += v
  57. v = v/100.0
  58. if auto_color:
  59. if v < min_temp:
  60. if v < min_temp_limit:
  61. min_temp = min_temp_limit
  62. else:
  63. min_temp = v
  64. if v > max_temp:
  65. if v > max_temp_limit:
  66. min_temp = max_temp_limit
  67. else:
  68. max_temp = v
  69. max_temp_pos = (i%sensor_width, i//sensor_width)
  70. target_temp.append( v )
  71. data = com.read(2)
  72. v = int.from_bytes(data, 'little')
  73. sum += v
  74. machine_temp = v/100.0
  75. data = com.read(2)
  76. parity_sum = int.from_bytes(data, 'little')
  77. if len(target_temp) != sensor_height*sensor_width:
  78. print("err")
  79. continue
  80. # print("{:02x} {:02x}".format(parity_sum, sum%0xffff))
  81. # TODO: parity not correct according to the doc
  82. # if parity_sum != sum%0xffff:
  83. # print("parity sum error")
  84. # continue
  85. center_temp = target_temp[int(sensor_width/2 + sensor_height/2*sensor_width)]
  86. # print("data length:", len(target_temp))
  87. # print("machine temperature:", machine_temp)
  88. # print("center temperature:", center_temp)
  89. img = image.Image(size=(sensor_width, sensor_height))
  90. img = img.to_grayscale()
  91. if max_temp == min_temp:
  92. max_temp += 1
  93. for i in range(sensor_height):
  94. for j in range(sensor_width):
  95. color = (target_temp[i*sensor_width+j]-min_temp)/(max_temp-min_temp)*255
  96. img[sensor_width*i + j] = int(color)
  97. img = img.resize(lcd_w, lcd_h)
  98. del target_temp
  99. img = img.to_rainbow(1)
  100. img2 = sensor.snapshot()
  101. img2.conv3(edge)
  102. img2 = img2.rotation_corr(z_rotation=rotate, x_translation=offset_x, y_translation=offset_y, zoom=zoom)
  103. img = img.blend(img2)
  104. del img2
  105. img = img.draw_rectangle(lcd_w//2+4, lcd_h//2, 80, 22, color=(0xff,112,0xff), fill=True)
  106. img = img.draw_string(lcd_w//2+4, lcd_h//2, "%.2f" %(center_temp), color=(0xff,0xff,0xff), scale=2)
  107. img = img.draw_cross(lcd_w//2, lcd_h//2, color=(0xff,0xff,0xff), thickness=3)
  108. if max_temp_pos:
  109. max_temp_pos = (int(lcd_w/sensor_width*max_temp_pos[0]), int(lcd_h/sensor_height*max_temp_pos[1]))
  110. img = img.draw_rectangle(max_temp_pos[0]+4, max_temp_pos[1], 80, 22, color=(0xff,112,0xff), fill=True)
  111. img = img.draw_string(max_temp_pos[0]+4, max_temp_pos[1], "%.2f" %(max_temp), color=(0xff,0xff,0xff), scale=2)
  112. img = img.draw_cross(max_temp_pos[0], max_temp_pos[1], color=(0xff,0xff,0xff), thickness=3)
  113. fps =clock.fps()
  114. img = img.draw_string(2,2, ("%2.1ffps" %(fps)), color=(0xff,0xff,0xff), scale=2)
  115. lcd.display(img)
  116. img = com.read()
  117. del img
  118. gc.collect()
  119. # gc.mem_free()

或者在PC 使用pyserial上读取并显示:

  1. import serial
  2. import pygame
  3. from pygame.locals import QUIT, KEYDOWN, K_f, K_F11, FULLSCREEN
  4. from PIL import Image, ImageDraw
  5. import matplotlib.pyplot as plt
  6. import numpy as np
  7. import time
  8. START_FLAG = 0x5A
  9. width = 32
  10. height = 24
  11. dis_width = 320
  12. dis_height = 240
  13. auto_color = True
  14. max_temp_limit = 300
  15. min_temp_limit = -40
  16. com = serial.Serial()
  17. com.baudrate = 460800
  18. com.port = "/dev/ttyACM0"
  19. com.bytesize = 8
  20. com.stopbits = 1
  21. com.parity = "N"
  22. com.timeout = None
  23. com.rts = True
  24. com.dtr = True
  25. com.open()
  26. pygame.init()
  27. pygame.display.set_caption("pic from client")
  28. screen = pygame.display.set_mode((dis_width, dis_height), 0, 32)
  29. def get_hot_color_map():
  30. hot_map = plt.get_cmap("hot")
  31. color_map = np.zeros((256,3), np.uint8)
  32. for i in range(256):
  33. color_map[i][0] = np.int_(hot_map(i)[0]*255.0)
  34. color_map[i][1] = np.int_(hot_map(i)[1]*255.0)
  35. color_map[i][2] = np.int_(hot_map(i)[2]*255.0)
  36. return color_map
  37. def temp_list2bytes(temperature_list):
  38. ret = b''
  39. for temp in temperature_list:
  40. ret += bytes( [int(temp*10/2)] )
  41. return ret
  42. def get_dis_temp_by_target_temp(target_temp, in_size, out_size):
  43. # (in_w, in_h), (out_w, out_h)
  44. pass
  45. hot_color_map = get_hot_color_map()
  46. color_array = np.zeros((height, width, 3), np.uint8)
  47. find_frame_flag = False
  48. while 1:
  49. if not find_frame_flag:
  50. data = 0
  51. flag_count = 0
  52. while 1:
  53. data = com.read(1)
  54. if int.from_bytes(data, byteorder='little') == START_FLAG:
  55. flag_count += 1
  56. if flag_count == 2:
  57. find_frame_flag = True
  58. break
  59. else:
  60. flag_count = 0
  61. else:
  62. find_frame_flag = False
  63. max_temp_pos=None
  64. data = com.read(2)
  65. data_len = int.from_bytes(data[:2], byteorder="little")
  66. sum = START_FLAG * 256 + START_FLAG + data_len
  67. if auto_color:
  68. min_temp = max_temp_limit
  69. max_temp = min_temp_limit
  70. data = com.read(data_len-2)
  71. target_temp = []
  72. for i in range(data_len//2-1):
  73. v = int.from_bytes(data[i*2:i*2+2], byteorder='little')
  74. sum += v
  75. v /= 100.0
  76. if auto_color:
  77. if v < min_temp:
  78. if v < min_temp_limit:
  79. min_temp = min_temp_limit
  80. else:
  81. min_temp = v
  82. if v > max_temp:
  83. if v > max_temp_limit:
  84. min_temp = max_temp_limit
  85. else:
  86. max_temp = v
  87. max_temp_pos = (i%width, i//width)
  88. target_temp.append( v )
  89. data = com.read(2)
  90. v = int.from_bytes(data, byteorder='little')
  91. sum += v
  92. machine_temp = v/100.0
  93. data = com.read(2)
  94. parity_sum = int.from_bytes(data, byteorder='little')
  95. print("{:02x} {:02x}".format(parity_sum, sum%0xffff))
  96. # TODO: parity not correct according to the doc
  97. # if parity_sum != sum%0xffff:
  98. # print("parity sum error")
  99. # continue
  100. print("data length:", len(target_temp))
  101. print("machine temperature:", machine_temp)
  102. temp_bytes = temp_list2bytes(target_temp)
  103. if max_temp == min_temp:
  104. max_temp += 1
  105. # dis_temperature = get_dis_temp_by_target_temp(target_temp, (width, height), (dis_width, dis_height))
  106. try:
  107. for i in range(0, height):
  108. for j in range(0, width):
  109. color = (target_temp[i*width+j]-min_temp)/(max_temp-min_temp)*255
  110. color_array[i, j] = hot_color_map[int(color)]
  111. img = Image.fromarray(color_array)
  112. img = img.resize( (dis_width,dis_height), resample=Image.LANCZOS)
  113. draw = ImageDraw.Draw(img)
  114. draw.line((img.width/2-4, img.height/2, img.width/2+4, img.height/2), fill=0xff3ef8, width=1)
  115. draw.line((img.width/2, img.height/2-4, img.width/2, img.height/2+4), fill=0xff3ef8, width=1)
  116. draw.rectangle([(img.width/2+10, img.height/2), (img.width/2+40, img.height/2+10)], fill=0x10bd87)
  117. center_temp = target_temp[int(width/2 + height/2*width)]
  118. draw.text((img.width/2+10, img.height/2), "{}".format(center_temp), fill=0xffff)
  119. if max_temp_pos:
  120. max_temp_pos = (int(dis_width/width*max_temp_pos[0]), int(dis_height/height*max_temp_pos[1]))
  121. draw.line((max_temp_pos[0]-4, max_temp_pos[1], max_temp_pos[0]+4, max_temp_pos[1]), fill=0xff3ef8, width=1)
  122. draw.line((max_temp_pos[0], max_temp_pos[1]-4, max_temp_pos[0], max_temp_pos[1]+4), fill=0xff3ef8, width=1)
  123. draw.rectangle([(max_temp_pos[0]+10, max_temp_pos[1]), (max_temp_pos[0]+40, max_temp_pos[1]+10)], fill=0x10bd87)
  124. draw.text((max_temp_pos[0]+10, max_temp_pos[1]), "{}".format(max_temp), fill=0xffff)
  125. surface = pygame.image.fromstring(img.tobytes(), img.size, img.mode).convert()
  126. screen.blit(surface,(0, 0))
  127. pygame.display.update()
  128. print("recieve ok")
  129. except Exception as e:
  130. print(e)
  131. com.close()
文章有误?有想法想讨论?查看或者发起勘误/讨论 主题
(发起评论需要先登录 github)

/wallpaper/wallhaven-967rgd.jpg