资讯动态

STM32F103+AMG8833低成本红外热成像人体检测与报警实战

发布时间:2026/9/29 1:43:23 来源:尧图企业网站定制
第一次拿到 AMG8833 这个 8×8 红外阵列传感器的时候我多少有点怀疑一片不到指甲盖大的板子居然只靠 64 个测温点就能做出“热成像”效果真正把它接到 STM32F103 上读到第一帧温度数据之后结论立刻反转。这个方案虽然分辨率低但隔空看见热源、判断“有没有人”“哪里温度异常”这类需求它完全能胜任而且成本压得很低实际玩起来很有意思。这篇文章我会把整个实战过程完整讲清楚为什么选 AMG8833、怎么跟 STM32F103 接线、固件里怎么初始化传感器和读取 64 点温度、怎么做简单的人体检测和高温报警最后再聊聊我在调试中踩过的坑。适合刚学 I2C 通信、想上手红外测温项目的同学也适合需要低成本非接触测温方案、想做快速原型验证的工程师参考。1. 项目概述与方案选型1.1 为什么用 AMG8833 做人体感应很多人提到“人体感应”第一反应就是 PIR 热释电传感器。PIR 确实便宜又成熟但它的缺点是只能检测“移动”人一旦站着不动或者以很慢的速度走动输出就会消失。更麻烦的是它分不清“人”和“被晒热的沙发”只要温差变化明显什么都可能触发。AMG8833 则是一块 8×8 的热电堆阵列每个像素都能测到对应的红外辐射温度。它不像 PIR 那样只能给一个“有人/没人”的信号而是能告诉我们“热源在哪、大概有多热”哪怕人站着不动只要身体温度与环境温度有差异就能持续检测到。这个能力在 AV 联动、养殖场观察、会议签到、门禁待机唤醒等场合非常实用。当然8×8 分辨率很低只有 64 个温度点每个点覆盖的角度大概是 7.5°整体视场角 60°。这决定了它没办法做脸部识别但在“这个方向 30 厘米外有个 36°C 左右的移动热源”这种粒度上已经够了。相比 MLX90614 那种单点红外传感器AMG8833 最大的优势是多了一个空间分布信息。比如房间里来了两个人MLX90614 面对的方向不凑巧就测不到而 AMG8833 可以通过不同像素的温度差判断出“这里有一个热点那边也有一个热点”。相比更高级的 MLX90640 32×24 阵列AMG8833 虽然像素少但是价格便宜一个数量级数据量小F103 的 I2C 接口和内存都完全带得动。1.2 为什么选 STM32F103 而不是其他芯片STM32F103 这个系列到今天已经被大家玩成“国民单片机”了开发资料多到数不过来。拿最小的 F103C8T6 来说主频 72MHz、Flash 64KB、RAM 20KBI2C、USART、GPIO 全都有跑一个 AMG8833 读取 蜂鸣器报警 OLED 显示的固件资源绰绰有余。从性价比看AMG8833 模块几十块钱STM32F103C8T6 最小系统板几块钱到十几块钱整套硬件成本压得很低。相比 ESP32、树莓派这类带着无线或系统级资源的平台F103 更“纯粹”也更接近嵌入式开发的底层路径没有 SDK 帮你把 I2C 写到飞起寄存器、DMA、错误处理都得自己想清楚。做完这个项目你对 I2C 协议、传感器寄存器、嵌入式外设配置的掌握会比刷十遍教程都扎实。如果你手头已经有其他 Cortex-M 内核的板子比如 APM32、AT32移植思路也是一模一样的。AMG8833 只依赖 I2C 和少量 GPIOHAL 库的 API 基本通用把引脚配置和时钟树改一下就行没必要重新学一套东西。1.3 整体方案框架整套系统的数据流大概是这样的AMG8833 传感器上电之后持续采集 8×8 阵列的红外温度单片机通过 I2C 从寄存器 0x80 开始一次性读取 64 个字节的原始数据转换成摄氏度之后在内存里形成一个 8×8 的二维温度数组。然后程序对这个数组做两件事一是找出最高温和平均背景温度估算是否有“高温目标”二是判断这个目标是否超过报警阈值如果超过就点亮 LED、让蜂鸣器响。为了方便调试我另外用串口把几个关键温度值打印出来再用一块 OLED 把 64 点温度画成简易热图。这里有个关键点报警不只是一个“温度大于多少度”的简单比较而是要结合背景温差做判断否则夏天环境温度高的时候整个画面都是 38°C 以上会疯狂误报。这个逻辑后面单独讲。2. 硬件准备与接线2.1 核心硬件清单做这个项目下面几样东西就够了STM32F103C8T6 最小系统板一块也就是大家常说的蓝色药丸板。AMG8833 红外热像模块一个建议买自带 LDO 稳压和 PCB 天线的常见模块具体看商家标注。0.96 寸 SSD1306 OLEDI2C 版本方便可视化调试。有源蜂鸣器模块或普通蜂鸣器 S8050 三极管 1k 电阻。一个 LED 和 220Ω 限流电阻。杜邦线若干、4.7kΩ 上拉电阻两个备用。AMG8833 模块的接口一般是 VIN、GND、SCL、SDA有的板子还会把 AD0 引脚引出来甚至标成 SDO。模块工作电压典型值是 3.3V部分模块上面有稳压芯片接 5V 也能扛住但为了和 F103 逻辑电平统一我建议直接接 3.3V省去电平转换的麻烦。2.2 AMG8833 引脚定义与 I2C 接线F103 的 I2C1 默认映射在 PB6SCL和 PB7SDA这个项目就用这一组。接线表如下AMG8833 引脚STM32F103 引脚说明VIN3.3V传感器供电GNDGND共地SCLPB6I2C 时钟SDAPB7I2C 数据AD0GND 或 3.3V从机地址选择AD0 引脚的用法很关键。AMG8833 有固定 I2C 地址选择当 AD0 接 GND 时从机地址是 0x68接 3.3V 时地址是 0x69。如果你的模块没有引出 AD0一般默认地址就是 0x68直接在代码里写 0x68 就行。如果读不到数据优先怀疑地址设置是否跟硬件一致。另外I2C 总线需要上拉电阻。很多 AMG8833 模块板上已经焊了上拉直接接就能工作但如果你的模块没有或者你想把 OLED、AMG8833 放在同一条 I2C 总线上最好在 SCL 和 SDA 上各接一个 4.7kΩ 电阻到 3.3V。F103 的 I2C 外设本身对总线时序比较敏感上拉电阻缺了或者阻值太大很容易出现“能识别地址但读不到数据”的奇怪现象。2.3 显示与报警模块接法OLED 和 AMG8833 可以共用同一条 I2C 总线OLED 的地址一般是 0x3C 或 0x3D。这里需要注意如果两个设备共享总线代码里发送地址时要分清设备地址最高位要左移一位。HAL 库的HAL_I2C_Mem_Read和HAL_I2C_Mem_Write会自动帮你做地址左移但裸机读写时很多人会在这里翻车少移一位就找不到设备。报警部分我用了两个输出一个 LED一个有源蜂鸣器。LED 串一个 220Ω 电阻接到 PA1 即可。蜂鸣器如果是有源模块直接把信号引脚接到 PA2高电平触发也能工作但蜂鸣器工作时电流经常冲到 20mA 以上用单片机的 GPIO 直接驱动虽然偶尔能起来长期跑还是不放心。更稳的做法是 GPIO 接 S8050 三极管的基极基极串 1kΩ 电阻蜂鸣器接在集电极和电源之间发射极接地。这样 GPIO 只负责给基极灌电流大电流由外部电源承担整个系统可靠很多。3. 固件设计与核心代码3.1 CubeMX 初始化配置我开发时用的 STM32CubeIDE 加 HAL 库第一步先在 CubeMX 里把硬件工程搭好。配置清单如下SYS 里选择 Serial Wire方便用 SWD 调试。RCC 选择外部晶振HSEF103 蓝色药丸板上通常有 8MHz 晶振和复位电路如果你用的是别的核心板注意确认外部晶振是否焊好没有外部晶振就用内部 HSI也能跑只是时钟精度差一些。I2C1 打开模式选 I2C速度选 100kHz。AMG8833 手册里支持 100kHz 和 400kHz但第一次调我强烈建议用 100kHz稳定第一。400kHz 可以后面再试很多“偶发读不到数据”的问题其实就是速率拉满导致的。USART1 打开波特率 115200用于打印报警状态和温度最大值。PA1 配置成 GPIO_Output控制 LEDPA2 配置成 GPIO_Output控制蜂鸣器。时钟树不用太复杂用默认的 72MHz 主频就行。AMG8833 的数据量不大I2C 时钟挂在外设时钟上就算分频后是 100kHz读取一帧 64 字节加地址帧也就几毫秒的事。3.2 AMG8833 寄存器初始化AMG8833 的寄存器地址不多初始化只需要关注三个0x00 电源控制寄存器写 0x00 进入正常模式。0x01 复位寄存器写 0x3F 触发一次软件复位让传感器回到已知状态。0x02 帧率寄存器写 0x00 表示 10fps写 0x01 表示 1fps。为什么要把帧率调到 10fps因为人体感应场景里我们希望报警延迟不要太长。1fps 虽然功耗低但一个人快速走过传感器视野时可能刚好错过两帧之间的空档导致漏报。10fps 的响应速度更适合实时检测。初始化代码这样写#include main.h #define AMG8833_ADDR 0x68 // AD0 接 GND #define AMG8833_PCTL 0x00 #define AMG8833_RST 0x01 #define AMG8833_FPSC 0x02 #define AMG8833_T01L 0x80 extern I2C_HandleTypeDef hi2c1; uint8_t amg8833_init(void) { uint8_t cmd; // 正常模式 cmd 0x00; if (HAL_I2C_Mem_Write(hi2c1, AMG8833_ADDR 1, AMG8833_PCTL, I2C_MEMADD_SIZE_8BIT, cmd, 1, 100) ! HAL_OK) return 0; // 软件复位 cmd 0x3F; if (HAL_I2C_Mem_Write(hi2c1, AMG8833_ADDR 1, AMG8833_RST, I2C_MEMADD_SIZE_8BIT, cmd, 1, 100) ! HAL_OK) return 0; HAL_Delay(50); // 10fps cmd 0x00; if (HAL_I2C_Mem_Write(hi2c1, AMG8833_ADDR 1, AMG8833_FPSC, I2C_MEMADD_SIZE_8BIT, cmd, 1, 100) ! HAL_OK) return 0; return 1; }这里有个细节AMG8833_ADDR 是 7 位地址但是 I2C 通信时实际发送的是 8 位其中最低位是 R/W 位。HAL 库的HAL_I2C_Mem_Write在内部会把地址左移一位再拼上读写位所以传参时要把 0x68 左移一位AMG8833_ADDR 1别直接填 0x68。这个坑不少新手踩过一旦写错函数会返回 HAL_ERROR总线毫无响应。3.3 64 点温度读取与换算初始化完成之后读取温度就是一次普通的内存读操作。AMG8833 从寄存器 0x80 开始连续存放 64 个像素的测温结果每个像素占 1 个字节共 64 字节。代码非常直接void amg8833_read_pixels(float *dst) { uint8_t raw[64]; uint8_t reg AMG8833_T01L; // 0x80 if (HAL_I2C_Mem_Read(hi2c1, AMG8833_ADDR 1, reg, I2C_MEMADD_SIZE_8BIT, raw, 64, 100) ! HAL_OK) { // 读取失败可以顺手置一个错误标志 return; } for (int i 0; i 64; i) { dst[i] (int8_t)raw[i] * 0.25f; } }为什么乘以 0.25因为 AMG8833 内部把温度做成了带符号整数单位是 0.25°C/LSB。把这个整数强转成 int8_t再乘 0.25就能得到摄氏度。这里要特别注意如果直接把 uint8_t 拿来乘温度在零下时就会出错。比如原始字节 0xFC按 uint8_t 算是 252乘以 0.25 变成 63°C实际上它是 -4°C。必须先转成有符号数再计算。温度数据拿到之后我习惯同时读一下传感器内部热敏电阻的数值作为环境参考温度。热敏电阻寄存器在 0x0E 和 0x0F各存 1 个字节组合成一个带符号数分辨率 0.0625°C。读取方法类似但这里的寄存器地址是 16 位数据需要分开读两个字节再拼起来。环境温度可以用来辅助判断“当前室温多高”报警算法里会更稳。3.4 人体检测与温度报警逻辑这部分是整个项目的核心也是我认为比“能读数”更有意思的地方。先说最简单的思路如果画面里有某个像素温度超过 35°C就认为有人。这个逻辑在春秋天的室内环境确实能用但到了夏天墙面被晒到 38°C传感器扫过去画面全是 36°C 以上的热斑误报就成了噩梦。更合理的方法是“背景差检测”。先把这 64 个点排序找出最大值maxTemp。然后去掉最大值附近几个点计算剩下像素的平均值作为背景温度bgTemp。人体检测条件有两层maxTemp 33.0f保证目标有一定绝对热度。maxTemp - bgTemp 3.0f保证目标和周围环境有明显温差。温度报警的条件则单独设一个阈值ALARM_TEMP#define ALARM_TEMP 37.5f #define PRESENCE_MIN 33.0f #define DELTA_MIN 3.0f static int stable_count 0; void process_temperature(float *pixels, uint8_t *presence, uint8_t *alarm) { float max_temp -100.0f; float sum 0.0f; float bg_temp; int max_idx -1; // 找到最大值和最大值索引 for (int i 0; i 64; i) { if (pixels[i] max_temp) { max_temp pixels[i]; max_idx i; } } // 计算背景温度时去掉最大值本身 for (int i 0; i 64; i) { if (i ! max_idx) { sum pixels[i]; } } bg_temp sum / 63.0f; // 人体存在判断 if (max_temp PRESENCE_MIN (max_temp - bg_temp) DELTA_MIN) { if (stable_count 5) stable_count; } else { stable_count 0; } *presence (stable_count 3) ? 1 : 0; *alarm 0; if (*presence max_temp ALARM_TEMP) { *alarm 1; } }这里之所以引入stable_count是为了防抖。AMG8833 单帧的读数噪声有 ±0.5°C 左右人站在检测区域边缘时max_temp可能一会 36.2°C、一会 33.8°C。如果帧帧直接判断LED 就会疯狂闪烁。让“有人”状态连续保持至少 3 帧再激活会让输出稳定很多。报警阈值为什么选 37.5°C这里必须说明AMG8833 测出来的是物体表面的红外辐射温度不是人体的医学体温。实际皮肤发射率、距离、镜片反射都会造成偏差直接拿这个数字当发烧判定完全不可靠。我这个项目只是演示“温度超限报警”真正做医疗或工业件需要做黑体标定和距离校准。3.5 串口和 OLED 显示串口打印是最快的调试手段。我在主循环里打印最高温度、背景温度、检测状态和报警状态printf(Max:%.2f BG:%.2f Presence:%d Alarm:%d\r\n, max_temp, bg_temp, presence, alarm);需要注意STM32 标准库和 HAL 库的 printf 重定向方式不同我一般用fputc重定向到 USART1否则串口助手只能看到乱码或者什么都没有。OLED 部分我用的是 SSD1306 的 I2C 版本画一个 8×8 的伪热图。每一格矩形对应一个像素颜色深度可以用灰度表示也可以简单分成几个等级低于 28°C 显示空白28~32°C 显示浅色高于 32°C 显示最深色。这样调试时眼睛扫一眼就能看出热源在视野的哪个方向。OLED 的存在不是必须的但强烈推荐因为你调试人体检测算法时单看一串串口数字不如看热图直观位置和大小一目了然。4. 实际调试与问题排查4.1 I2C 读取失败怎么查我调试这个项目时第一轮最常见的现象是程序跑起来串口一点输出都没有HAL_I2C_Mem_Read返回 HAL_ERROR。排查顺序基本固定首先确认接线。SCL 和 SDA 不要接反F103 的 PB6、PB7 要核对清楚有些核心板的丝印标得上拉/复用引脚容易看错。然后用示波器或者逻辑分析仪看 SCL 和 SDA 波形如果根本没有波形检查 CubeMX 里 I2C1 是否初始化成功。其次确认地址。AMG8833 的地址 0x68 对应 AD0 接 GND0x69 对应 AD0 接 3.3V。有的模块默认已经把 AD0 拉到高电平那你代码里就必须用 0x69。不确定时写一个 10 行以内的 I2C 扫描程序把总线上所有能 ACK 的地址都打印出来比自己瞎猜快得多。然后是上拉电阻。AMG8833 模块如果没有板上拉SCL 和 SDA 会一直处于低电平HAL_I2C 初始化的时候总线繁忙任何通信都发起不了。这时候加上 4.7k 上拉问题立刻消失。如果你在一条总线上并联了 OLED上拉电阻算一份就够不需要每个设备都加。4.2 温度偏差大或者漂移怎么办AMG8833 的测量原理是热电堆它测的是目标与传感器之间的红外辐射差。这个值受距离影响极大。同样一个人站在 10cm 处最高温度可能 36.5°C站到 1m 处因为 60° 视场角太大热点被摊到多个像素里单点峰值会掉到 30°C 以下。这是传感器硬件性质决定的不是固件 bug。另外物体发射率也是个大问题。人体皮肤发射率接近 0.98但衣服、金属、玻璃差别巨大。铝罐、镜面这类低发射率物体即使实际温度 60°CAMG8833 也可能读出 30°C。所以这个项目做人体检测没问题做高温物体检测时需要非常小心“看起来温度不高”的反射面。如果你发现固定距离下读数一直偏高或偏低 1~2°C可以在代码里做一阶线性校准float calibrated (raw_temp - offset) * scale;offset 和 scale 需要用已知温度源标定比如恒温水浴或者黑体辐射源。没有标定条件的至少也要拿工业温度计做几次对比把大致偏差记下来。4.3 误报和漏报怎么优化误报来源最多的是阳光直射、热地板、暖气片。这些物体的绝对温度经常超过 35°C而且与周围背景温差也可能超过 3°C。应对办法有两个方向一是调整检测区域只用画面中间一部分像素做判断避开边缘的干扰源二是对时间序列做滤波比如取最近 5 帧的最高温度做移动平均单帧的异常尖峰就不会直接触发报警。漏报主要发生在目标太远、衣物太厚或者环境温度接近体表温度的时候。我在项目里加了一个“温差阈值”可配置参数默认 3°C但如果你在夏天用可以适当调低到 1.5°C。需要说明的是环境温度 36°C 时人体表面温度可能只有 34°C单纯靠热成像做人体检测的可靠性会明显下降这种情况最好配合 PIR 或毫米波雷达做多传感器融合。4.4 常见问题速查表现象可能原因处理方法串口无输出printf 未重定向检查 fputc 重定向代码I2C 读回 HAL_ERROR地址错误/接线错误/总线上拉缺失I2C 扫描、检查 AD0、补上拉温度整体偏高目标距离近、发射率高做线性校准或标定温度整体偏低目标距离远、视野摊开缩小检测范围或用最高温附近 3×3 平均报警频繁误触发环境高温/阳光干扰调整动态温差阈值、加滤波蜂鸣器一响就死机蜂鸣器电流拉低电压改用三极管驱动避免 GPIO 直连大电流5. 几个可以继续扩展的方向5.1 升级更高分辨率的传感器AMG8833 只有 8×8做原型验证够了但如果想判断更精确的位置或者识别热源的轮廓可以考虑换到MLX90640。这是一个 32×24 的红外阵列像素数是 AMG8833 的 12 倍数据量也相应变大。F103 的 I2C 吞吐量可能会比较紧张建议用 SPI 接口版本或者换更高主频的芯片。代码结构上只需要改传感器读取部分人体检测和报警逻辑基本可以复用。5.2 加无线模块做远程报警这个报警器如果固定在某一个地方意义有限。把它接上 ESP8266 或 ESP32通过 WiFi 把温度数据传到手机就能变成一个带远程通知的“热源看护”设备。做远程方案时注意电源隔离和地线共地否则无线模块发射电流的瞬间会把 AMG8833 的电源拉偏导致温度读数抖动。5.3 与上位机联动生成热力图我现在调试时主要的可视化工具还是 OLED 和串口数据量大了之后明显不够用。可以把 64 个浮点温度通过串口或者 USB 转发到 PC用 Python 的 matplotlib 画成伪彩色热力图甚至加上实时动态刷新。这样不但调试更舒服发布演示的时候效果也更好。上位机的核心就是解析串口协议把收到的 64 字节还原成 8×8 矩阵再用imshow绘制即可。5.4 从“会响”到“有用”的工程化改造报警器能响只是第一步。实际落地到养殖场、机房、仓库这类环境还需要考虑传感器安装高度、检测距离、环境温湿度补偿、边缘计算节点联网等问题。AMG8833 的低成本优势在批量场景里很明显但它的精度确实有限做最终产品前一定要对这个硬性条件有清晰认知别拿它做医疗级体温检测也别指望 8×8 分辨率能识别出人脸。我个人在实际调试中发现这个项目最值钱的部分不是硬件本身而是你开始认真处理“一坨温度数据”的过程排序、滤波、阈值判断、时序稳定、多设备协调。这些思路放到任何传感器套件里都通用。最后分享一个小技巧报警阈值不要写死在代码里做成一个全局变量再用串口协议远程调整。调试的时候你会感谢自己这个决定不用每次改一个数字就重新编译烧录一次。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑