1. 项目缘起:为什么选择非接触式红外测温方案?
最近在整理工作室的电子元件,翻出来几个之前做项目剩下的MLX90614红外测温传感器模块。这玩意儿精度不错,价格也亲民,但之前都是用在一些工业监测的小项目里,总觉得有点大材小用。正好手边还有一块Arduino Uno和几个超声波模块,一个想法就冒出来了:能不能做个简单、实用又有点意思的非接触式红外测温仪?
这个想法其实挺有现实意义的。传统的接触式测温,比如水银温度计或者热电偶,你得贴上去才能测,测个开水温度还行,但要是测个正在运行的电机外壳、或者想快速筛查一下物体表面温度分布,就很不方便了。非接触红外测温就没这个烦恼,它通过测量物体表面辐射出的红外能量来反推温度,完全不用碰,响应速度还快,毫秒级就能出结果。对于电子爱好者、创客,或者只是想做个智能家居小工具的人来说,用Arduino搭配MLX90614来实现这个功能,是个成本低、门槛也合适的入门级选择。
你可能在网上搜“Arduino测温”,会看到很多基于DS18B20或者LM35的方案,那些都是接触式的。而“Non-Contact Infrared Thermometer”这个组合,核心就在于“红外”和“非接触”。MLX90614就是干这个的,它内部集成了红外热电堆传感器和专用的信号处理芯片,能直接把接收到的红外辐射转换成数字温度值,通过I2C接口传给Arduino,我们几乎不用处理复杂的模拟信号。再配合一个超声波测距模块,我们还能实现“靠近自动测量”或者“测量距离补偿”,让这个小设备更智能一些。所以,这个项目不只是简单地把传感器数据读出来,更是对一种实用测量原理的动手实践。
2. 核心器件选型与工作原理深潜
工欲善其事,必先利其器。做一个非接触红外测温仪,核心就三样:负责思考的微控制器、负责“看”温度的红外传感器,以及可选但能大大提升体验的“眼睛”超声波模块。我们来逐一拆解,搞清楚为什么选它们,以及它们到底是怎么工作的。
2.1 大脑:Arduino Uno的稳定之选
市面上Arduino板子很多,从Nano到Mega,为什么首选Uno?对于这个项目,核心考量是稳定性和易用性。MLX90614采用I2C通信,只需要两根信号线(SDA, SCL)。Arduino Uno的I2C引脚(A4, A5)位置固定,连接非常直观,避免了像一些迷你板型需要飞线或额外定义的麻烦。同时,Uno的供电(5V)和IO电平与常见的MLX90614模块(工作电压3.3V或5V,兼容电平)完美匹配,不需要额外的电平转换电路。
更重要的是生态和调试便利性。Uno的USB转串口芯片非常成熟,在Arduino IDE里几乎不会遇到驱动问题,上传代码、查看串口监视器的数据都极其顺畅。项目后期如果需要增加显示(如OLED屏)或声光提示(蜂鸣器、LED),Uno丰富的数字和模拟引脚也留有充足余量。虽然ESP32等功能更强大,但对于专注于传感器数据采集和逻辑控制的红外测温仪,Uno的ATmega328P芯片性能完全够用,且因其经典,相关的库支持和社区解答也最丰富,能有效降低新手排查问题的难度。
2.2 眼睛:MLX90614如何“看见”温度?
这是本项目的绝对核心。MLX90614不是一个简单的热敏电阻,它是一个完整的红外测温系统封装。其工作原理可以分为三步:
红外能量收集:传感器前方的透镜(通常是硅透镜)负责收集目标物体发射的红外辐射。所有高于绝对零度(-273.15°C)的物体都会向外辐射红外线,辐射的强度与物体表面温度的四次方成正比(斯蒂芬-玻尔兹曼定律)。透镜将这些红外线聚焦到传感器内部的“热电堆”上。
热电堆转换:热电堆由一系列热电偶串联而成。当红外光照射到热电堆的热结点(黑色吸收面)时,结点温度升高,而冷结点(通常连接至传感器外壳)保持相对恒定。这个温差会产生一个微弱的温差电动势(电压信号)。温度差越大,产生的电压信号就越强。
信号处理与输出:这个微伏级别的电压信号极其微弱,且容易受干扰。MLX90614内部集成了一个低噪声放大器、一个17位模数转换器(ADC)和一个强大的数字信号处理器(DSP)。ADC将放大后的模拟电压信号转换为数字量,DSP则根据传感器自身的校准参数、环境温度补偿(MLX90614也内置了一个测量自身芯片温度的PTAT传感器)以及复杂的算法,最终计算出目标物体的绝对温度值。所有计算都在传感器内部完成,它通过I2C接口直接输出一个已经换算好的、以摄氏度或华氏度为单位的数字,比如
23.75,这极大简化了我们的编程工作。
这里有一个关键点需要注意:MLX90614测量的是物体表面的“辐射温度”。这个值要准确转换为真实的“表面温度”,依赖于一个叫“发射率”的参数。发射率是一个介于0(完美镜面,不辐射)和1(理想黑体,完全辐射)之间的系数。大多数有机材料(皮肤、木材)和氧化后的金属表面发射率在0.95左右,而光亮金属表面可能低至0.1。MLX90614库函数通常允许我们设置发射率(默认为0.95)。对于精度要求不高的通用场景,默认值即可;如果需要测量特定材料(如铝板、不锈钢),就必须查找该材料在相应波段的发射率并进行校正。
2.3 感知:HC-SR04超声波模块的角色
红外测温仪为什么需要超声波模块?这不是必需的,但它能实现一个非常实用的功能:触发测量或距离补偿。
MLX90614有一个固定的视场角(FOV),比如常见的版本是90°或110°。这意味着它测量的不是一个点,而是一个圆锥形区域的平均温度。当被测物体距离传感器很近时,传感器“看到”的基本就是物体表面的一小块区域;但当距离变远时,视场覆盖的面积变大,如果背景温度(比如墙壁)与目标物体温度不同,就会引入测量误差。
超声波模块HC-SR04可以精确测量传感器到物体表面的距离。我们可以利用这个距离信息做两件事:
- 自动触发:设置一个阈值(例如5-15厘米),当超声波检测到有物体进入这个范围时,自动触发一次红外温度测量并显示结果,实现“抬手即测”的体验,无需按钮。
- 距离提醒:在屏幕上实时显示当前距离,提示用户将设备保持在最佳测量距离(根据传感器数据手册,通常建议在2-5厘米内以获得最准确的“点”温度),避免因距离过远导致视场内包含非目标物体。
超声波模块的原理是发送一束40kHz的超声波脉冲,并接收从物体反射回来的回波。通过计算发射和接收的时间差,结合声速(约340m/s),就能算出距离。虽然声速受温湿度影响,但在室内短距离测量中,这个误差对于我们的触发或提示功能来说可以接受。
3. 硬件连接与电路搭建详解
理论清楚了,接下来就是动手连接。这一步看似简单,但连接的正确性和稳定性直接决定了后续程序能否顺利运行,以及测量结果的可靠性。我们按照信号流和电源流来梳理。
3.1 系统连接总图与电源考量
首先,我们需要一张清晰的接线图。以下是基于Arduino Uno、MLX90614(I2C接口)和HC-SR04超声波模块的典型连接方式:
| 元件 | 引脚 | 连接到 Arduino Uno | 说明 |
|---|---|---|---|
| MLX90614 | VIN | 5V | 供电引脚,接5V稳定电源 |
| GND | GND | 共地,至关重要! | |
| SDA | A4 | I2C数据线 | |
| SCL | A5 | I2C时钟线 | |
| HC-SR04 | VCC | 5V | 供电引脚 |
| GND | GND | 共地 | |
| Trig | 数字引脚 D2 | 触发控制引脚 | |
| Echo | 数字引脚 D3 | 回波接收引脚 | |
| 可选:OLED屏 | VCC | 5V | 供电 |
| GND | GND | 共地 | |
| SDA | A4 (与MLX90614并联) | I2C数据线,可并联 | |
| SCL | A5 (与MLX90614并联) | I2C时钟线,可并联 |
注意:多个I2C设备(如MLX90614和OLED屏)可以共享SDA和SCL线,这是I2C总线的好处。但务必确保每个设备有唯一的I2C地址。MLX90614的默认地址通常是0x5A,而常见的0.96寸OLED屏地址是0x3C,它们不会冲突。
电源是隐形的关键。整个系统从USB取电,对于Uno来说通常足够。但如果你发现测量值跳动异常大,尤其是超声波模块工作时,可能是电源纹波导致的。一个简单的改进方法是:在Arduino的5V和GND引脚之间,靠近传感器模块的位置,并联一个10μF的电解电容和一个0.1μF的陶瓷电容,用于滤除低频和高频噪声。特别是MLX90614内部的ADC对电源质量比较敏感,干净的电源能提升读数稳定性。
3.2 连接实操与常见坑点
焊接或使用杜邦线连接时,有几点需要特别留意:
- I2C上拉电阻:MLX90614模块板上通常已经集成了4.7kΩ的上拉电阻(连接在SDA和SCL到VCC之间)。如果你的模块没有,或者连接线较长(超过20厘米),信号质量可能变差。这时需要在Arduino端的A4(SDA)和A5(SCL)引脚上,分别通过一个4.7kΩ电阻连接到5V。大多数现成模块已内置,无需额外添加。
- 超声波模块的干扰:HC-SR04在发射超声波时会产生一个瞬间的较大电流。如果它与MLX90614共用一条很细或很长的电源线,可能会引起电压瞬间跌落,干扰MLX90614的测量。解决办法是使用较粗、较短的导线为它们供电,或者如前所述,在电源入口处增加滤波电容。
- GND共地:所有模块的GND引脚必须可靠地连接到Arduino的GND。这是电路正常工作的基础,任何“浮地”都会导致通信失败或读数错误。建议使用面包板或PCB的电源总线来确保所有地线连接在一起。
连接完成后,先不要急于上传复杂代码。可以分别测试各个模块。例如,先上传一个简单的I2C扫描程序,确认Arduino能正确识别到MLX90614的地址(0x5A)。再单独测试超声波模块的测距功能。分步验证能极大简化故障排查过程。
4. 软件实现:从数据读取到智能逻辑
硬件搭建好了,接下来就是赋予它灵魂的代码部分。我们将使用Arduino IDE进行开发。首先需要安装必要的库,这对于功能实现至关重要。
4.1 库文件安装与环境准备
打开Arduino IDE,进入“工具” -> “管理库...”,在库管理器中搜索并安装以下库:
- Adafruit MLX90614 Library:这是最常用、维护最好的MLX90614驱动库。搜索“Adafruit MLX90614”并安装,它通常会连带安装“Adafruit BusIO”等依赖库。
- Ultrasonic Library:用于简化HC-SR04的操作。搜索“Ultrasonic by Erick Simões”并安装。当然,你也可以不用库,直接通过
pulseIn()函数处理回声,但使用库代码更简洁。 - (可选) Adafruit SSD1306 及 GFX Library:如果你连接了I2C接口的OLED屏,需要安装这两个库来驱动屏幕和显示图形文本。
安装完成后,建议通过“文件” -> “示例”菜单找到对应库的示例程序,比如运行一下Adafruit MLX90614库中的mlxtest示例,看看串口监视器能否正确输出环境温度和物体温度,以此验证传感器和连线是否正常。
4.2 核心代码逐行解析
下面是一个整合了红外测温、超声波测距和基础串口输出的核心代码框架,并附上详细注释。
// 引入必要的库 #include <Wire.h> // Arduino内置的I2C库 #include <Adafruit_MLX90614.h> // 红外测温库 #include <Ultrasonic.h> // 超声波测距库 // 初始化传感器对象 Adafruit_MLX90614 mlx = Adafruit_MLX90614(); // 创建MLX90614对象,使用默认I2C地址0x5A // 初始化超声波对象,Trig引脚接D2,Echo引脚接D3 Ultrasonic ultrasonic(2, 3); // 定义变量 float objectTempC; // 物体温度(摄氏度) float ambientTempC; // 环境温度(摄氏度) float distanceCM; // 测量距离(厘米) const float MIN_DISTANCE = 2.0; // 最小有效测量距离(厘米) const float MAX_DISTANCE = 15.0; // 最大有效测量距离(厘米),超出此范围提示太远 void setup() { Serial.begin(9600); // 启动串口通信,用于调试输出 Serial.println("Non-Contact IR Thermometer Starting..."); // 初始化MLX90614 if (!mlx.begin()) { Serial.println("Error connecting to MLX90614. Check wiring!"); while (1); // 如果初始化失败,程序停在这里 } Serial.println("MLX90614 Found!"); // 超声波模块无需特别的初始化,库内部处理了 } void loop() { // 1. 读取超声波距离 distanceCM = ultrasonic.read(CM); // 读取距离,单位厘米 delay(50); // 短延时稳定读数 // 2. 判断距离是否在有效范围内,决定是否进行测温 if (distanceCM >= MIN_DISTANCE && distanceCM <= MAX_DISTANCE) { // 距离合适,进行红外测温 objectTempC = mlx.readObjectTempC(); // 读取物体温度(摄氏度) ambientTempC = mlx.readAmbientTempC(); // 读取环境(传感器自身)温度 // 3. 通过串口输出结果 Serial.print("Distance: "); Serial.print(distanceCM); Serial.print(" cm | "); Serial.print("Object Temp: "); Serial.print(objectTempC); Serial.print("°C | "); Serial.print("Ambient Temp: "); Serial.print(ambientTempC); Serial.println("°C"); // 这里可以添加代码,将数据显示到OLED屏上 // displayData(objectTempC, distanceCM); // 4. (可选) 简单的距离提示逻辑 if (distanceCM < 5.0) { Serial.println("提示:距离过近,请稍远离以获得更准的‘点’温度。"); } } else if (distanceCM < MIN_DISTANCE) { Serial.println("物体太近了,无法有效测量。"); } else { Serial.println("物体太远了,请靠近。"); } delay(500); // 主循环延时,控制测量频率(每秒约2次) }代码关键点解析:
mlx.begin():这个函数不仅初始化I2C通信,还会尝试与传感器握手。如果失败,最常见的原因是I2C连线错误(SDA/SCL接反)、电源问题或传感器损坏。返回的错误信息能帮我们快速定位。mlx.readObjectTempC():这是核心函数,它返回的是经过发射率校正后的物体温度。发射率默认是0.95。如果你想测量特定材料,可以使用mlx.writeEmissivity(0.90)来修改(参数范围0.1-1.0),但请注意,发射率写入传感器的EEPROM,有写入次数限制,不要频繁修改。- 距离判断逻辑:代码中设置了一个
MIN_DISTANCE和MAX_DISTANCE。太近(如<2cm)可能超出传感器最小聚焦范围,且物体可能堵塞传感器视场;太远(如>15cm)则视场过大,容易受背景干扰。这个阈值可以根据你使用的MLX90614的具体型号(视场角不同)进行调整。 - 延时控制:
loop()中的delay(500)控制了测量频率。MLX90614的转换时间很快,但过于频繁的读取可能意义不大。500ms的间隔对于手持式测温仪来说比较合适。超声波模块的测量周期也需要几十毫秒,所以主循环不宜过快。
4.3 功能扩展:添加OLED显示与声光反馈
仅有串口输出显然不够实用。接下来我们为其添加一个0.96寸OLED显示屏(I2C接口),让测量结果直观可见。
首先,确保已安装Adafruit SSD1306和GFX库。然后在代码开头添加相关库和对象初始化:
#include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);在setup()函数中初始化显示屏:
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;); // 卡住 } display.display(); // 显示Adafruit的启动logo delay(2000); display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE);然后,创建一个显示函数,在测温和测距完成后调用:
void displayData(float temp, float dist) { display.clearDisplay(); display.setCursor(0,0); display.print("Temp: "); display.print(temp, 1); // 显示一位小数 display.println(" C"); display.setCursor(0, 20); display.print("Dist: "); display.print(dist, 1); display.println(" cm"); // 距离状态提示 display.setCursor(0, 40); if(dist < 5.0) { display.print("Too Close!"); } else if (dist <= 10.0) { display.print("Good"); } else { display.print("Too Far"); } display.display(); // 将缓存内容刷到屏幕上 }在loop()函数中,在串口打印的代码附近,调用displayData(objectTempC, distanceCM);。
更进一步,可以添加一个蜂鸣器和LED。例如,当测量到温度超过某个阈值(如38.0°C)时,让蜂鸣器短鸣一声,同时红色LED闪烁,实现一个简单的发烧预警功能。这只需要在判断温度后,用digitalWrite()控制对应的数字引脚即可。硬件上,将蜂鸣器(有源)正极通过一个220Ω电阻接到一个数字引脚(如D8),负极接GND;LED同样串联一个220Ω电阻后接到另一个数字引脚(如D9)。
5. 校准、测试与精度提升实战
设备组装完成,代码也跑通了,但读出来的温度准不准?这才是检验项目成功与否的最终标准。非接触红外测温的精度受多种因素影响,我们不能指望它达到医疗级水准,但通过一些方法,可以使其在特定应用场景下足够可靠。
5.1 初始验证与交叉比对
首先,进行一个简单的“黑体”测试。找一个已知温度、表面发射率较高的物体。最方便的就是一杯恒温的水。用一支精度较好的数字温度计(最好是探针式的)插入水中,测量水的实际温度作为参考。等待几分钟让水温均匀,同时确保水面平静无蒸汽(蒸汽会影响红外测量)。将你的Arduino测温仪传感器正对水面,距离水面约3-5厘米,读取显示的温度。
比较两者读数。在室温环境下(比如25°C左右),对于一杯30-40°C的温水,你的设备读数与参考温度计的差值在±1°C以内,都可以认为是相当不错的。如果偏差较大(比如超过2°C),就需要进行以下排查和校准。
5.2 影响精度的关键因素与应对策略
发射率(Emissivity):这是最大的误差来源。MLX90614库默认发射率是0.95。对于绝大多数非金属、非光亮表面,这个值是合理的。但如果你测量的是:
- 光亮金属(如铝罐、不锈钢杯):发射率可能低至0.1-0.2。直接测量会严重偏低。解决方法是在被测表面贴一小块电工胶布或涂一层哑光黑漆,等待其温度与环境平衡后,测量胶布或漆面的温度,这时的发射率接近0.95。
- 人体皮肤:皮肤发射率约为0.98。你可以尝试在代码中将发射率设置为0.98(使用
mlx.writeEmissivity()函数,注意慎用,有写入寿命),或者在后续数据处理中软件补偿。更稳妥的方法是通过“偏移量”校准。
测量距离与视场:如前所述,距离太远,视场(FOV)内包含背景物体,会导致读数混合。最佳实践是固定一个测量距离,比如5厘米,并制作一个简单的物理卡尺或支架,确保每次测量都在这个固定距离进行。这样可以将距离因素固定化,简化校准。
环境温度补偿:MLX90614内部已经通过PTAT传感器进行了环境温度补偿。
mlx.readAmbientTempC()读出的就是传感器芯片自身的温度。确保传感器不在发热源(如Arduino板子的稳压芯片)旁边,并且有适当通风,让环境温度读数准确,这有助于提升物体温度的计算精度。传感器自身温度稳定性:刚上电时,传感器芯片本身可能未达到热平衡,前几十秒的读数可能会有漂移。建议设备上电后预热1-2分钟,再进行正式测量,读数会稳定很多。
5.3 软件校准:偏移量修正法
对于DIY项目,最实用的校准方法是“两点偏移量”法。我们不轻易改动传感器内部的发射率参数,而是在软件层面对读数进行加减补偿。
- 找一个“低温”参考点:测量一个已知低温物体,比如放在室内一段时间的一瓶矿泉水(温度接近室温,用接触式温度计测得其精确温度,例如
T_low_ref = 24.5°C)。用你的设备在固定距离下测量,得到读数T_low_read(例如24.0°C)。 - 找一个“高温”参考点:测量一个已知高温物体,比如一杯热水,同样用接触式温度计测得其温度
T_high_ref(例如45.2°C)。用你的设备测量得到T_high_read(例如44.5°C)。 - 计算偏移量:这里假设误差在整个量程内是线性的(对于小范围如20-50°C,近似成立)。可以计算一个平均偏移:
offset = ( (T_low_ref - T_low_read) + (T_high_ref - T_high_read) ) / 2。 - 应用校准:在代码中,将最终显示的温度修正为:
T_corrected = mlx.readObjectTempC() + offset。
例如,上面例子中,offset = ((24.5-24.0)+(45.2-44.5))/2 = (0.5+0.7)/2 = 0.6°C。那么所有读数都加上0.6°C。这个方法能有效消除系统性的固定偏差。
6. 外壳设计与实际应用场景拓展
一个裸露着电路板和连线的设备既不安全也不实用。3D打印一个外壳是完美的解决方案。设计外壳时需要考虑以下几点:
- 传感器开孔:为MLX90614的透镜和超声波传感器的收发头预留精确的圆形开孔。孔洞要干净,避免有毛刺遮挡。
- 显示屏开窗:为OLED屏幕开一个矩形窗口,可以用透明亚克力板覆盖保护。
- 按键与接口:预留一个按钮孔,用于切换模式(如物体/环境温度显示)或启动测量。预留USB接口的开口,方便供电和编程。
- 散热与结构:避免将Arduino板上的发热元件(如稳压芯片)紧贴外壳内壁。可以设计一些通风栅格。结构上要考虑电池仓(如果想做成便携式)的位置。
设计工具可以使用免费的Fusion 360或Tinkercad。测量好所有元件的实际尺寸,特别是安装孔位。可以在Thingiverse等网站搜索“Arduino Uno Case”、“MLX90614 case”寻找灵感或现成模型进行修改。
应用场景举例:
- 3D打印机热床温度巡检:将设备固定在打印头上,定期移动测量热床不同位置的温度,检查其加热均匀性。
- 电子设备散热检查:快速扫描电路板上芯片、MOS管等元件的温升情况,排查过热点。
- 家庭趣味应用:测量开水、咖啡的温度(注意不要有蒸汽遮挡);测量宠物窝的温度;甚至可以用来粗略比较阳光下和阴影处物体的温差。
- 教学演示工具:非常直观地展示非接触测温、红外辐射、I2C通信、超声波测距等物理和电子学概念。
最后的几点心得:在这个项目里,电源去耦电容对稳定读数真的有奇效,尤其是当你同时驱动屏幕和传感器时。MLX90614的读数偶尔跳变是正常的,软件上可以做一点平滑滤波,比如连续读5次取中位数。超声波模块在柔软表面(如布料)或复杂表面测距可能不准,这是声波反射特性决定的,在代码里要做好异常值过滤。这个项目最大的乐趣在于,它从原理到实践是一条完整的链路,你能亲手验证物理定律如何通过几块便宜的芯片和代码变成看得见摸得着的功能。