GY-906 MLX90614 红外测温 Arduino 教程:视场、发射率与 OLED 显示

Cartoon of a small blue GY-906 MLX90614 board with a silver metal can and a loose black four-pin header beside it, a blue 0.96-inch OLED with white pixels, a plain white mini breadboard, rainbow male-to-male jumper leads and a blue Arduino Uno

GY-906 读到的是一个 90° 锥形视场内所有东西的平均温度,所以它量到的那一块面积,宽度是它到目标距离的两倍 —— 距离 2 cm 时约 4 cm 宽,距离 30 cm 时就是 60 cm 宽。接在 Uno 上是 VIN 到 5V、GND 到 GND、SCL 到 A5、SDA 到 A4,前提是你先把袋子里那条松着的四针排针焊上去。这是一颗玩家与工业用的表面测温传感器,不是医用体温计。

先看这一段。皮肤温度不等于核心体温。这里的任何内容都不是临床测量,也不是医疗建议;体温筛查需要通过医疗认证的仪器,而 Melexis 在数据手册里点名了 MLX90614 的哪些版本才够格 —— 这一颗不在其中。

MLX90614 到底在量什么?

GY-906 模块上那颗光亮的银色金属罐里装的 MLX90614,是一颗热电堆:温热物体发出的红外线落在一层薄膜上,薄膜上排布着一串串联热电偶的热端,红外线让热端比芯片衬底上的冷端高出零点几度,而这个温差产生的电压,才是真正被测到的量。温差还不等于温度,所以芯片同时也测量自己封装的温度,把它当成推算物体温度的参考点。这就是它为什么有两个输出 —— 传感器自身的封装温度,以及物体温度 —— 也是那条最要紧的规则的由来,因为窗口能看到的一切都会算进去Melexis 数据手册把话说得很白:测到的数值,是传感器视场内所有物体的平均温度(”The measured value is the average temperature of all objects in the Field Of View of the sensor”),而且只有在物体完全填满传感器视场时,标称精度才成立(”the accuracy is only valid if the object fills the FOV of the sensor completely.”)。

为什么传感器一移动,读数就变了?

GY-906 既没有激光指示点也没有取景器,所以没有任何东西告诉你那个被平均掉的圆有多大 —— 一移动它就变,而只有算式能告诉你变了多少。我们这颗模块用的是 90° 光学视场,所以锥的半角是 45°;圆的半径等于距离乘以半角的正切,而 tan 45° = 1。于是半径等于距离,直径就是距离的两倍

到目标的距离 被平均掉的圆有多大 实际上盖住了什么
2 cm ≈ 4 cm 一只马克杯的杯壁,别的什么都没有
10 cm ≈ 20 cm 一台笔记本电脑的整个底面
30 cm ≈ 60 cm 一颗头、两边肩膀,还有背后那面墙

一杯热饮,半分钟就能把这件事验完。距离 2 cm 时,那个 4 cm 的圆整个都落在杯子上,所以数字是可信的。退到 30 cm,同一只杯子 —— 就算杯壁有 8 cm 宽 —— 在一个 60 cm 的圆里占不到 2 %,剩下 98 % 全是室温的墙,于是平均值落在离室温一两度的地方。没有任何东西漂移了,是你问了一个不同的问题。有一点要补上:Melexis 定义视场的标准,是灵敏度降到 50 % 的位置,所以这个圆的边缘是软的,刚好在圈外的东西仍然会贡献一点 —— 这只会让实际规则比算式更严,绝不会更松。

卡通对比图:一块蓝色红外传感器板带着银色金属罐,近距离时投出一道窄的青色锥形光束落在白色马克杯上;远距离时锥形大幅张开,越过杯子一路铺到深蓝色的墙上
同一颗传感器、同一只杯子,答案却不一样。贴近时,被量到的圆全是杯子;伸直手臂那么远,圆里大半是墙,平均值也就跟着墙走。

选配:排针我们帮你焊

ItemPriceQty
Soldering Service for Circuit Boards & Electronics - 5 Pins Soldering ServiceSoldering Service for Circuit Boards & Electronics - 5 Pins Soldering ServiceSOLSE05RM0.80

GY-906 的四针排针是松着出货的,必须焊上去才能用。这项代焊服务涵盖五支针以内,正好就是这个活。它是人工安排的,不走购物车结账 —— 先用 WhatsApp 跟我们说一声,我们会跟你的订单一起处理。

为什么 GY-906 出货时排针没焊上?

GY-906 出货是一块光板:蓝色 PCB 下缘有四个空的镀金孔 —— VIN、GND、SCL、SDA —— 那条四针黑色排针松着躺在袋子里。这样同一块板才能选直排针、弯排针,或者把线直接焊平了塞进外壳。但这也意味着在四个焊点做好之前,什么都接不上

2.54 mm 间距的四个焊点,是很好的第一份焊接练习,技巧在我们的排针焊接指南里讲完了。有两个细节是这块板独有的:排针要从银色罐那一面插进去,让针脚从背面伸出来,传感器才会朝着背离面包板的方向;还有,烙铁 —— 以及之后你的手指 —— 都别碰那颗光亮的罐子:那个金属帽是传感器的光学窗口,也是它的温度参考,不是拿来抓的把手。不想自己动手?我们的五针代焊服务包的正好就是这条排针。如果这是你第一次焊,看一遍别人怎么润焊盘、怎么让锡流上去,胜过任何文字描述:

两个 I2C 设备怎么共用一块 Arduino Uno?

MLX90614 和这块 0.96 吋 OLED 能挂在同样那两根线上,是因为 I2C 是一条带地址的总线:所有设备都在听,只有被叫到地址的那一个才回话。传感器的出厂地址是 0x5A。OLED 是 SSD1306,地址由背面一颗跳线电阻决定,出货时短接在标着 0x78 的焊盘上 —— 那是 8 位的写地址,最低位是读写标志。右移一位就得到库要的 7 位地址:0x78 >> 1 = 0x3C。不冲突,一条总线。另一个选项用同样的算法:跳线落在 0x7A 那个焊盘上就是 0x3D,而下面 sketch 里的 OLED_ADDR 必须跟着改。

要共用这条总线,面包板才有了它存在的理由:四条网络每一条都得同时接到两块板以及 Uno,而 Uno 的一支引脚上塞不下两个杜邦线插头。170 孔迷你面包板的一列是底下连通的五个孔,于是每一列就成了一条网络分岔的汇流点。把 GY-906 的四支针插进一侧的一排,OLED 的四支插进另一侧,这样每条短跳线只需要跨过中央凹槽就好。

两块模块都是平躺着的,不是立起来 —— 它们的针从板子背面伸出,所以每块 PCB 最后都悬在面包板上方几毫米,正面朝天。摆的时候让每块板的身体探出面包板边缘,而不是压在板面上,否则它会盖住自己跳线要用的插孔;OLED 从针脚那一排往外还有大约 27 mm。而既然罐子是朝上看的,这个作品的瞄准方式,是把目标拿到传感器上方,而不是把传感器凑向目标。

⚠️ 这两块板的引脚顺序并不一样。GY-906 是 VIN、GND、SCL、SDA;OLED 是 GND、VCC、SCL、SDA。电源和地是对调的,所以照着同一个顺序抄过去,是把显示屏接反电源最容易的方式。看 OLED 的丝印要正面朝着自己看 —— 翻过来看针脚,顺序就反了。

网络 GY-906 引脚 OLED 引脚 Uno 排座
电源 VIN(第 1 支) VCC(第 2 支) 5V
GND(第 2 支) GND(第 1 支) GND
时钟 SCL(第 3 支) SCL(第 3 支) A5
数据 SDA(第 4 支) SDA(第 4 支) A4

一共八条公对公杜邦线:四条短的把两边对应的列连起来,四条长的接到 Uno 的母排座上。而 5V 确实是对的那条电源轨,尽管 MLX90614 的裸片是 3 V 级的器件,因为模块已经替你稳压了 —— 一份针对这块板的拆解找到板上有一颗 662K 稳压器,从 VIN 做出 3.3 V,而 VIN 最高可以吃到 6 V,另外还有 4.7 kΩ 的 I2C 上拉电阻 —— 这些上拉必然接在那条稳压后的电源轨上,因为 Melexis 规定 3 V 版本的 SDA 与 SCL 最高只能到它自己的供电电压。所以它同样适合 3.3 V 的 ESP32 和 5 V 的 Uno:ATmega328P 的数据手册把高电平输入门限定在 0.6 × VCC,在 5 V 板上就是 3.0 V,而 3.3 V 越过这条线还有余量。

卡通图:一块带银色金属罐的蓝色 GY-906 传感器和一块蓝色 0.96 吋 OLED 平躺在纯白色迷你面包板上,由四条短杜邦线相连,再由四条较长的杜邦线接到一块蓝色 Arduino Uno
接好的样子。每五个孔一组就是一条网络,所以四条短跳线跨过中央凹槽把两块板连起来,四条长的再把同样这四条网络送到 Uno。

用哪一段代码读它,并把数字送上 OLED?

在库管理器里装 Adafruit MLX90614 LibraryAdafruit SSD1306Adafruit GFX。这三个都依赖 Adafruit BusIO,而少了 BusIO 正是它编译不过最常见的原因。还有一个命名陷阱:方法名是 writeEmissivity(),不是好几篇第三方教程写的 setEmissivity() —— 库里随附的头文件说了算。

// GY-906(MLX90614)红外传感器 + 0.96 吋 OLED,接在 Arduino Uno 上。
// 共用一条 I2C 总线:SDA -> A4,SCL -> A5。传感器地址 0x5A,显示屏 0x3C。
// 这是一次红外表面测温的演示。
// 它不是医用体温计,这里的任何读数都不是健康测量。

#include <Wire.h>
#include <Adafruit_MLX90614.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_ADDR 0x3C   // 0x78 这个写地址右移一位的结果

Adafruit_SSD1306 display(128, 64, &Wire, -1);
Adafruit_MLX90614 mlx;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  delay(300);   // 数据手册:上电后 250 ms 起读数才有效

  // Adafruit_SSD1306::begin() 只有在帧缓冲塞不进 SRAM 时才回传 false,
  // 它从不检查显示屏有没有应答 —— 所以自己去问总线。
  Wire.beginTransmission(OLED_ADDR);
  if (Wire.endTransmission() != 0) {
    Serial.println(F("No OLED at 0x3C. Check SDA/SCL and the address jumper."));
    while (1) { }
  }
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  if (!mlx.begin()) {          // 0x5A 没有应答时,begin() 回传 false
    display.setTextSize(1);
    display.setCursor(0, 8);
    display.println(F("No MLX90614 at 0x5A"));
    display.println(F("Check the four"));
    display.println(F("solder joints."));
    display.display();
    Serial.println(F("Nothing answered at 0x5A."));
    while (1) { }
  }

  // 发射率存在传感器自己的 EEPROM 里,断电也不会丢。
  Serial.print(F("Stored emissivity: "));
  Serial.println(mlx.readEmissivity(), 2);   // 出厂值是 1.00
}

void loop() {
  double object  = mlx.readObjectTempC();    // 整个锥形视场的平均值
  double ambient = mlx.readAmbientTempC();   // 传感器封装自己的温度

  display.clearDisplay();

  if (isnan(object) || isnan(ambient)) {     // 读取失败会回传 NAN
    display.setTextSize(1);
    display.setCursor(0, 28);
    display.println(F("Read failed"));
  } else {
    display.setTextSize(1);
    display.setCursor(0, 0);
    display.print(F("SURFACE avg in FOV"));

    display.setTextSize(3);
    display.setCursor(0, 18);
    display.print(object, 1);
    display.print(F("C"));

    display.setTextSize(1);
    display.setCursor(0, 52);
    display.print(F("sensor body "));
    display.print(ambient, 1);
    display.print(F("C"));

    Serial.print(object, 2);
    Serial.print(F(" C surface / "));
    Serial.print(ambient, 2);
    Serial.println(F(" C sensor body"));
  }

  display.display();
  delay(500);
}

里头有三个刻意的选择。Adafruit_MLX90614::begin() 回传一个 bool,当 0x5A 没有任何应答时它是 false,所以 sketch 会停下来并点名是哪一颗芯片,而不是一路印零。显示屏需要它自己的探测,因为在 Adafruit_SSD1306 的源码里,begin() 只有在帧缓冲塞不进 SRAM 时才回传 false —— 它从来不问 OLED 有没有回话 —— 所以 sketch 自己用 Wire 去 ping 一下 0x3C。读取失败会回传 NAN,所以这两个值走的是 isnan(),而不是拿去做数值比较。至于那 300 ms 的等待,是因为数据手册只保证上电 250 ms 之后的结果有效。每一条字符串都留在 F() 里:Adafruit_SSD1306 会占掉 1024 字节的帧缓冲,等于 Uno 一半的 SRAM,而这还是在这段 sketch 自己那 538 字节静态占用之上。

上传之后,把马克杯那一招再做一次,这回看 OLED:贴近时,那个大字就是杯子的温度;把杯子慢慢拿远,数字会一路往下走,向着它下面那个传感器本体的温度靠拢。不过别把热的东西一直架在罐子上方 —— 那会把封装本身烤热,而封装不被烤热,正是那些精度指标成立的前提。

为什么量光亮的金属,读数会低得离谱?

因为 MLX90614 出厂时是按一个完美辐射体校准的 —— MLX90614 的标准校准,是按物体发射率为 1 来做的(”As a standard, the MLX90614 is calibrated for an object emissivity of 1″)。发射率是一个表面在同样温度下,相对于理论上完美的发射体,实际发出多少比例的红外线;Melexis 给出的参考值里,抛光铝大约是 0.18,重度氧化的铝也只有 0.30,而普通油漆是 0.85–0.95。

真正抵达传感器的,是这个表面自己发出的红外线 —— 正比于它的发射率乘以绝对温度的四次方 —— 再加上它反射回来的那份房间的红外线。一个差劲的发射体,同时就是一面很好的镜子,所以回来的大部分其实是房间的影像。把数字代进去:一块 60 °C 的抛光铝散热片,放在 30 °C 的房间里,它只发出黑体在 60 °C 时的 18 %,另外反射了 82 % 的 30 °C 房间 —— 总辐射量大约相当于 36 °C。「我的散热片读出来只比室温高一点点」,全部的解释就在这里。

解法是在你想量的那一块贴哑光胶带或刷哑光漆,而不是换一颗传感器:把同一道算式按 0.95 再算一次,那块散热片读出来大约是 58.7 °C。你也可以用一行代码把真实的发射率告诉传感器,但要清楚自己在做什么 —— mlx.writeEmissivity(0.95) 写的是芯片内部的 EEPROM,所以它熬得过断电,并且会一直影响之后的每一次读数,直到你把它改掉 —— 包括下个月你拿它去量别的东西的时候。像这段 sketch 那样在启动时把 mlx.readEmissivity() 印出来,就能让这件事一直看得见,而 mlx.writeEmissivity(1.0) 可以把出厂值写回去。

卡通对比图:一块光亮的银色带鳍散热片把光线反射回一颗蓝色小红外传感器,旁边的读数表指着很低的值;同一块散热片贴上哑光黑胶带后,读数表指着很高的值
同一块散热片,同样的温度。裸露的抛光金属大半只是把房间反射回传感器,所以读得偏冷;贴上一条哑光胶带,它才正常地发射红外线,数字也就对了。

能拿它来量体温吗?

不能 —— GY-906 上装的是不适合这件事的那个 MLX90614 版本,而且 Melexis 在数据手册里把话讲死了,没有留给人解读的空间。这一家的医疗级精度 —— 在体温附近一段很窄的区间内做到 ±0.2 °C —— 只有 MLX90614Dxx 版本才具备(”is only available for the MLX90614Dxx versions”),而符合 ASTM E1965-98(用于人体的红外体温计标准)的型号,被逐一点了名:ESF-DCC、ESF-DCH 和 ESF-DCI。它们的视场角分别是 35°、10° 和 5° —— 全都很窄,正是因为一个宽锥形视场没办法把额头,从后面的头发、墙壁和冷气分离出来。

我们这颗模块的 90° 光学视场,从设计上就把它放在另一个类别里,而同一份文件还从第二个角度佐证了这一点:Dxx 版本跑的是更高的放大倍数,代价是物体温度上限被压到 200 °C 附近,而这一颗的规格是到 380 °C。正是这个上限让它擅长它真正该做的事 —— 烙铁、电机、刹车碟、烹饪台面、3D 打印机的热端。要量人,请用通过认证的医用体温计。

我们常看到的几个误区

站得远远的,却期待一个点测的读数。这是最常见的失望来源。伸直手臂那么远,传感器平均掉的圆比一个人还宽,所以数字会往室温塌下去。

拿它去量裸露的金属。在默认发射率 1.0 之下,光亮的表面读出来会低得很夸张。在那一小块贴哑光胶带,或者把真实的发射率写进去 —— 并且记得它是会一直留着的。

用手抓着罐子,或者把它装在发热的东西旁边。物体温度是拿传感器自己封装的温度当参考推算出来的,所以手指按在金属罐上、或者旁边紧挨着一颗发烫的稳压器,都会把参考本身弄脏 —— 这也是为什么数据手册的精度指标,只在封装上下没有温差的前提下成立。

一插上电就读,尤其是在户外。把板子从冷气房拿到 33 °C 的环境里,它会有好几分钟处在远离热平衡的状态;让它先静一静。USB 电源电压塌下去也一样 —— 电源电压带来的影响大约是每伏特 0.6 °C。

常见问题

MLX90614 能量多远?

多远都行 —— 但距离越远,被平均掉的面积就越大。以这颗模块 90° 的视场角来算,那个圆的宽度是距离的两倍:2 cm 给你 4 cm,30 cm 给你 60 cm。只有物体填满这个圆时,标称精度才成立。

GY-906 可以拿来当发烧或体温计用吗?

不行。它是玩家与工业用的表面测温传感器,而且皮肤温度不等于核心体温;Melexis 把医疗级精度限定在视场很窄的 MLX90614Dxx 版本,那些才符合 ASTM E1965-98。量人请用通过认证的仪器。

为什么我的 GY-906 读出来比物体实际温度冷得多?

几乎都是发射率的问题。传感器出厂是按完美辐射体校准的,所以抛光金属 —— 铝大约 0.18 —— 大半只是把房间反射回它。在那一小块贴上哑光胶带,或者把真实的发射率写进传感器。

GY-906 一定要接 3.3V 的板子吗?5V 安全吗?

5V 没问题。模块的 VIN 后面带着自己的 3.3 V 稳压器,VIN 最高可以吃到 6 V,而它的 I2C 上拉电阻接的就是那条稳压轨。Uno 从 3.0 V 起就认得出高电平,所以 3.3 V 余量充足 —— 同一块模块也能直接用在 3.3 V 的 ESP32 上。

传感器和 OLED 能共用同样那两支引脚吗?

可以。I2C 是带地址的:MLX90614 在 0x5A 应答,这块 SSD1306 在 0x3C,所以两个一起挂在 A4 和 A5 上。我们的 DHT11 加 OLED 教程MPU6050 倾角教程用的是同一套做法。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *