电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
[战疫情]如何制作一个低成本热成像仪来监测体温
分 享
扫描二维码分享
[战疫情]如何制作一个低成本热成像仪来监测体温
MLX90640
esp32
IAMLIUBO
IAMLIUBO
关注
发布时间: 2020-02-18
丨
阅读: 9005
## 我是谁,谁又是我 Hi,大家好,我是刘波,人在江湖行走的ID是:[IAMLIUBO](https://blogs.oopswow.com/)!受到电子芯吧客平台用户活跃度的感召,为了防止世界被破坏,为了保护世界的和平,于是我决定也要来电子芯吧客平台混个脸熟了(我不会承认我是为了稿费才来的!绝对不会!滑稽.gif)。 ## 前言 相信很多人现在还在家没有去公司上班吧,今年春节一场突如其来的疫情打乱了很多节奏,不过大家也不需要担心,因为我们中奖会战胜疫情,武汉加油,中国加油!本片文章就带大家制作一个简单的低成本的热成像仪,来监测自己或者家人的体温以及身体健康状态! ## 准备 ### 硬件 * [ESP32_DevKitc_V4](https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-8715811636.13.5d784a79xX5Wgx&id=542143157571) * [MLX90640红外热像仪模块](http://www.waveshare.net/shop/MLX90640-D110-Thermal-Camera.htm) * ILI9341显示屏(某宝有很多,大家自行选择就可以) * 洞洞板 * 杜邦线 ### 软件 * Arduino IDE ## 红外热成像技术简介 物体表面温度如果超过绝对零度即会辐射出电磁波,随着温度变化,电磁波的辐射强度与波长分布特性也随之改变,波长介于0.75μm到1000μm间的电磁波称为“红外线”,而人类视觉可见的“可见光”介于0.4μm到0.75μm。 其中波长为0.78~2.0微米的部分称为近红外,波长为2.0~1000微米的部分称为热红外线。红外线在地表传送时,会受到大气组成物质( 特别是H2O、CO2、CH4 、N2O、O3等)的吸收,强度明显下降,仅在中波3μ~5μm及长波8~12μm的两个波段有较好的穿透率(Transmission),通称大气窗口(Atmospheric window),大部份的红外热像仪就是针对这两个波段进行检测,计算并显示物体的表面温度分布。此外,由于红外线对极大部份的固体及液体物质的穿透能力极差,因此红外热成像检测是以测量物体表面的红外线辐射能量为主。 ----*以上内容摘抄自[网络](http://www.sohu.com/a/308727808_120103055)* 通过上面的介绍,大家可以看出来,其实红外热成像主要是测量物体**表面**的红外线辐射,这里大家注意了,是物体表面,意思就是说如果被测物体与测试设备间隔着透明的玻璃测试也是不准确的,所以非接触测温对测量方式有一定要求,但是在现在疫情传播的关键时期,非接触测温是可以很好的避免交叉感染,所以优势也是非常大的。 对了,我们常见的耳温枪或者额温枪**大部分**也属于红外测温,只不过是单点式的,这里我们使用的传感器像素为32x24。 ## 硬件介绍 MLX90640是迈来芯研发的传感器,具体特性如下: * 工作温度范围为 -40 至 85°C * 可测量的物体温度范围为 -40 至 300°C * 分辨率为 32x24 像素 * 典型目标物体温度精度为 1°,可在整个测量范围内保持高精度水平 * NETD 仅为 0.1K RMS(刷新速率为 1Hz) * 不需要根据特定温度要求进行重新校准,能够在确保更大便利性的同时降低运营费用 * 两种不同的视角 (FoV) 可供选择:标准 55°x35° 和 110°x75° 广角 * 4 引脚 TO39 封装,包含必需的光学元件 * I²C 兼容的数字接口,可简化集成 可以看出这款传感器还是非常不错的,要比AGM8833强大很多,不知道大家有没有用过AMG8833,这是一款8x8分辨率的,想比之下要比MLX90640就差很多了,这里我们就简单介绍一下MXL90640,对于ESP32和显示屏相信大家肯定也不陌生了,就不做介绍了。 ## 硬件连接 这里我们使用的是ESP32开发板然后用Arduino来开发,相信很多人都很熟悉Arduino吧,这里就不在做介绍,显示屏使用的是ILI9341 TFT显示屏,某宝很好找,上面的硬件清单里面主要元器件也给大家贴好了网址,大家可以直接点击链接查看,当然这里只是给出的我购买的链接,并没有任何广告嫌疑,大家可以自行斟酌决定。(滑稽.gif) ``` ESP32_DevKit<------>ILI9341 P15 CS P18 SCK P19 MISO P23 MOSI P2 DC P4 RST ESP32_DevKit<------->MLX90640 P22 SCL P21 SDA ``` 我这里使用洞洞板连接起来的,大家也可以自行决定连接方式。 ![ESP32-MLX90640连接方式](https://cf02.ickimg.com/bbsimages/202002/2ec5318eb7e33150e7dc1aa28a8cfa31.jpg "ESP32-MLX90640连接方式") 线都在洞洞板后面,比较凌乱,这里就不给大家看了,连接也相对比较简单,大家也可以使用杜邦线连接。 ## 代码开发 微雪也有示例代码,不过显示屏是用的HX9347D的主控,我现在手头只有ILI9341主控的显示屏,所以我在Github上也找到了大神写的代码,这里给大家贴一下主要代码: ```Arduino #include <TFT_eSPI.h> #include <SD.h> #include <SPI.h> #include <Wire.h> #include "MLX90640_API.h" #include "MLX90640_I2C_Driver.h" #define EMMISIVITY 0.95 #define INTERPOLATE false #define C_BLUE Display.color565(0,0,255) #define C_RED Display.color565(255,0,0) #define C_GREEN Display.color565(0,255,0) #define C_WHITE Display.color565(255,255,255) #define C_BLACK Display.color565(0,0,0) #define C_LTGREY Display.color565(200,200,200) #define C_DKGREY Display.color565(80,80,80) #define C_GREY Display.color565(127,127,127) #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640 #define TA_SHIFT 8 //Default shift for MLX90640 in open air paramsMLX90640 mlx90640; TFT_eSPI Display = TFT_eSPI(); // Added for measure Temp boolean measure = true; float centerTemp; unsigned long tempTime = millis(); unsigned long tempTime2 = 0; // start with some initial colors float minTemp = 20.0; float maxTemp = 40.0; // variables for interpolated colors byte red, green, blue; // variables for row/column interpolation float intPoint, val, a, b, c, d, ii; int x, y, i, j; // array for the 32 x 24 measured tempValues static float tempValues[32*24]; // Output size #define O_WIDTH 224 #define O_HEIGHT 168 #define O_RATIO O_WIDTH/32 float **interpolated = NULL; uint16_t *imageData = NULL; void setup() { Serial.begin(115200); Serial.println("Hello."); // Connect thermal sensor. Wire.begin(); Wire.setClock(400000); // Increase I2C clock speed to 400kHz Wire.beginTransmission((uint8_t)MLX90640_address); if (Wire.endTransmission() != 0) { Serial.println("MLX90640 not detected at default I2C address. Please check wiring."); } else { Serial.println("MLX90640 online!"); } // Get device parameters - We only have to do this once int status; uint16_t eeMLX90640[832]; status = MLX90640_DumpEE(MLX90640_address, eeMLX90640); if (status != 0) Serial.println("Failed to load system parameters"); status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640); if (status != 0) Serial.println("Parameter extraction failed"); // Set refresh rate MLX90640_SetRefreshRate(MLX90640_address, 0x05); // Set rate to 8Hz effective - Works at 800kHz // Once EEPROM has been read at 400kHz we can increase Wire.setClock(800000); // Set up Display. pinMode(TFT_DC, OUTPUT); SPI.begin(); SPI.setFrequency(80000000L); Display.begin(); //Display.setRotation(3); Display.fillScreen(C_BLACK); // Prepare interpolated array interpolated = (float **)malloc(O_HEIGHT * sizeof(float *)); for (int i=0; i<O_HEIGHT; i++) { interpolated[i] = (float *)malloc(O_WIDTH * sizeof(float)); } // Prepare imageData array imageData = (uint16_t *)malloc(O_WIDTH * O_HEIGHT * sizeof(uint16_t)); // get the cutoff points for the color interpolation routines // note this function called when the temp scale is changed setAbcd(); drawLegend(); } void loop() { tempTime = millis(); readTempValues(); setTempScale(); drawPicture(); drawMeasurement(); } // Read pixel data from MLX90640. void readTempValues() { for (byte x = 0 ; x < 2 ; x++) // Read both subpages { uint16_t mlx90640Frame[834]; int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame); if (status < 0) { Serial.print("GetFrame Error: "); Serial.println(status); } float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640); float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640); float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature MLX90640_CalculateTo(mlx90640Frame, &mlx90640, EMMISIVITY, tr, tempValues); } } int row; float temp, temp2; void interpolate() { for (row=0; row<24; row++) { for (x=0; x<O_WIDTH; x++) { temp = tempValues[(31 - (x/7)) + (row*32) + 1]; temp2 = tempValues[(31 - (x/7)) + (row*32)]; interpolated[row*7][x] = lerp(temp, temp2, x%7/7.0); } } for (x=0; x<O_WIDTH; x++) { for (y=0; y<O_HEIGHT; y++) { temp = interpolated[y-y%7][x]; temp2 = interpolated[min((y-y%7)+7, O_HEIGHT-7)][x]; interpolated[y][x] = lerp(temp, temp2, 1);//y%7/7.0); } } } // Linear interpolation float lerp(float v0, float v1, float t) { return v0 + t * (v1 - v0); } void drawPicture() { if (INTERPOLATE) { interpolate(); for (y=0; y<O_HEIGHT; y++) { for (x=0; x<O_WIDTH; x++) { imageData[(y*O_WIDTH) + x] = getColor(interpolated[y][x]); } } Display.pushImage(8, 8, O_WIDTH, O_HEIGHT, imageData); } else { for (y=0; y<24; y++) { for (x=0; x<32; x++) { Display.fillRect(8 + x*7, 8 + y*7, 7, 7, getColor(tempValues[(31-x) + (y*32)])); } } } } // Get color for temp value. uint16_t getColor(float val) { /@@* pass in value and figure out R G B several published ways to do this I basically graphed R G B and developed simple linear equations again a 5-6-5 color display will not need accurate temp to R G B color calculation equations based on http://web-tech.ga-usa.com/2012/05/creating-a-custom-hot-to-cold-temperature-color-gradient-for-use-with-rrdtool/index.html */ red = constrain(255.0 / (c - b) * val - ((b * 255.0) / (c - b)), 0, 255); if ((val > minTemp) & (val < a)) { green = constrain(255.0 / (a - minTemp) * val - (255.0 * minTemp) / (a - minTemp), 0, 255); } else if ((val >= a) & (val <= c)) { green = 255; } else if (val > c) { green = constrain(255.0 / (c - d) * val - (d * 255.0) / (c - d), 0, 255); } else if ((val > d) | (val < a)) { green = 0; } if (val <= b) { blue = constrain(255.0 / (a - b) * val - (255.0 * b) / (a - b), 0, 255); } else if ((val > b) & (val <= d)) { blue = 0; } else if (val > d) { blue = constrain(240.0 / (maxTemp - d) * val - (d * 240.0) / (maxTemp - d), 0, 240); } // use the displays color mapping function to get 5-6-5 color palet (R=5 bits, G=6 bits, B-5 bits) return Display.color565(red, green, blue); } void setTempScale() { minTemp = 255; maxTemp = 0; for (i = 0; i < 768; i++) { minTemp = min(minTemp, tempValues[i]); maxTemp = max(maxTemp, tempValues[i]); } setAbcd(); drawLegend(); } // Function to get the cutoff points in the temp vs RGB graph. void setAbcd() { a = minTemp + (maxTemp - minTemp) * 0.2121; b = minTemp + (maxTemp - minTemp) * 0.3182; c = minTemp + (maxTemp - minTemp) * 0.4242; d = minTemp + (maxTemp - minTemp) * 0.8182; } // Draw a legend. void drawLegend() { float inc = (maxTemp - minTemp) / 224.0; j = 0; for (ii = minTemp; ii < maxTemp; ii += inc) { Display.drawFastVLine(8+ + j++, 292, 20, getColor(ii)); } Display.setTextFont(2); Display.setTextSize(1); Display.setCursor(8, 272); Display.setTextColor(TFT_WHITE, TFT_BLACK); Display.print(String(minTemp).substring(0, 5)); Display.setCursor(192, 272); Display.setTextColor(TFT_WHITE, TFT_BLACK); Display.print(String(maxTemp).substring(0, 5)); Display.setTextFont(NULL); } // Draw a circle + measured value. void drawMeasurement() { // Mark center measurement Display.drawCircle(120, 8+84, 3, TFT_WHITE); // Measure and print center temperature centerTemp = (tempValues[383 - 16] + tempValues[383 - 15] + tempValues[384 + 15] + tempValues[384 + 16]) / 4; Display.setCursor(86, 214); Display.setTextColor(TFT_WHITE, TFT_BLACK); Display.setTextFont(2); Display.setTextSize(2); Display.print(String(centerTemp).substring(0, 5) + " °C"); } ``` 其中依赖TFT_eSPI库和MLX90640驱动代码,由于代码比较多,这里就不给大家贴出来了,大家可以点击下面链接查看下载: [MLX90640-Thermocam](https://github.com/netzbasteln/MLX90640-Thermocam) 再给大家贴一个演示视频,这是我做好后拍的一个小视频,很短,目的只是为了给大家演示一下,没法直接插入视频,大家可以点击文章最后的链接查看。 大家只要按照上面的接线方式和代码就一定能做出来,大家如果有问题可以在评论中告诉我,最后跟大家说一声,不要出去乱走动,保护好自己,在家躺着就是最大的胜利!武汉加油,中国加油! [演示视频-B站](https://www.bilibili.com/video/av88776740/)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
2
)
IAMLIUBO
擅长:工控电子 能源电源 光电显示 设计,制造及服务 智能硬件
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交