ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于STM32与红外对管的人流量检测系统:硬件设计、状态机算法与抗干扰实践

基于STM32与红外对管的人流量检测系统:硬件设计、状态机算法与抗干扰实践 简介本资源是一套面向嵌入式初学者与STM32进阶实践者的综合性人流量检测系统开发包聚焦于真实场景下的软硬件协同设计解决从传感器数据采集、本地计数处理到多模态数据上传的完整技术链路问题。压缩包共含6个核心项目文件涵盖C语言主控程序基于HAL库、Keil工程文件、传感器驱动模块、蓝牙与机智云通信接口代码、电机控制逻辑及风扇温控仿真模型类型以.c/.h源码、.uvprojx工程、README.md说明文档为主整体大小为10.47MB。目前已有201人学习下载适合课程设计、毕业设计或电子竞赛备赛使用。读者可直接导入Keil环境编译运行获取红外/超声波双模检测逻辑、中断计数防抖策略、PWM风扇调速实现、蓝牙串口透传协议封装等关键工程细节并通过配套README快速掌握各子项目功能定位与集成方式。1. 项目概述与核心需求解析最近在做一个商场出入口的人流量统计项目客户要求成本可控、部署灵活且数据准确。市面上现成的方案要么太贵要么体积庞大安装不便。琢磨了一下决定自己动手用STM32单片机为核心搭配一对红外对管设计一个轻量级的人流量检测器。这个方案的核心思路很简单通过判断人体是否遮挡了红外光束来触发计数关键在于如何稳定、准确地识别“一个人”经过而不是被误触发比如小动物、飘过的塑料袋或者两个人紧挨着走过。最终完成的成果包括完整的程序代码、原理图、PCB设计文件等我打包成了“STM32单片机人流量检测器设计程序代码等6个.zip”。今天就来详细拆解这个项目的设计思路、硬件选型、软件逻辑以及那些调试过程中踩过的坑希望能给想做类似项目的朋友一些参考。这个项目本质上是一个数字信号的事件检测与去抖系统。它不适合需要精确识别个体特征如人脸或行进方向的场景但在统计单向通道、门口、闸机等位置的人员通过次数上具有极高的性价比和可靠性。整个系统由STM32F103C8T6俗称“蓝桥杯”或“最小系统板”那颗芯片作为主控一对红外发射接收管作为传感器一个OLED屏幕用于本地显示以及一个ESP-01S WiFi模块用于数据上传。程序代码基于STM32标准外设库开发结构清晰便于二次修改。2. 硬件系统设计与关键器件选型硬件是整个系统稳定性的基石。在预算有限的前提下每一分钱都要花在刀刃上同时要保证足够的抗干扰能力和长期运行的可靠性。2.1 主控芯片为什么是STM32F103C8T6在8位机和32位机之间我毫不犹豫地选择了STM32。对于人流量检测这种需要实时处理传感器信号、管理外设显示、通信并可能涉及简单算法的应用STM32的Cortex-M3内核提供了充足的性能余量。具体到型号F103C8T6是经典型号资源丰富72MHz主频、64KB Flash、20KB RAM、多达37个GPIO、3个定时器、2个SPI、2个I2C、3个USART、1个USB和2个ADC。最关键的是它的生态极其成熟资料遍地都是价格也打到了白菜价。注意市场上F103C8T6有国产替代如GD32和原装ST之分。对于本项目国产芯片完全够用但在采购时要注意Flash容量和功耗的细微差异最好让卖家提供测试过的例程。2.2 传感方案红外对管 vs. 热释电 vs. 摄像头这是项目的核心决策点。红外对管最终选择由红外发射管和红外接收管通常是光电三极管组成。发射管持续发出红外光接收管接收。当有人经过遮挡光束时接收管输出电平变化。优点是电路简单、成本极低一对几毛钱、响应速度快、功耗可控可调制发射。缺点是易受环境光干扰特别是太阳光中含有丰富的红外成分检测区域是一条细线安装需要精确对齐。热释电红外传感器PIR检测人体发出的特定波长红外线。优点是检测面广、不易受可见光干扰。缺点是响应速度慢适合检测静止或慢速移动、对温度变化敏感、无法区分物体移动方向除非用双元探头加菲涅尔透镜做方向判断电路复杂且成本相对较高。摄像头图像识别最强大也最复杂。优点可获取丰富信息人数、方向、甚至属性。缺点是成本高昂需要摄像头模组和较强的处理芯片、算法复杂、功耗大、涉及隐私问题。综合比较红外对管方案在成本、响应速度和实现难度上取得了最佳平衡。为了解决环境光干扰我们采用调制解调技术让STM32的PWM驱动红外发射管发出特定频率例如38kHz的红外光接收端使用一体化红外接收头如HS0038B或由运放搭建的解调电路。这样只有该频率的信号才会被识别为有效信号自然光等直流或低频干扰被极大抑制。2.3 外围电路与电源设计红外发射电路用一个NPN三极管如S8050驱动红外发射管。STM32的PWM信号通过一个限流电阻连接到三极管基极控制其通断。发射管串联一个电阻限流防止过流烧毁。计算限流电阻时要确保发射管工作在额定电流下通常20-50mA以获得足够的发射功率。红外接收电路如果使用一体化接收头如HS0038B则电路非常简单VCC、GND、OUT三根线OUT直接接STM32的GPIO。如果为了成本使用光电三极管自行解调则需要搭建一个运放电路包含带通滤波以筛选出调制频率再经过比较器整形成数字信号。后者调试更复杂但BOM成本更低。电源模块整个系统预计由5V USB或电池供电。STM32需要3.3VOLED屏和ESP-01S也多是3.3V。因此选用一颗AMS1117-3.3稳压芯片。关键点ESP-01S在发射WiFi信号时瞬时电流可能超过200mA必须确保AMS1117和前端电源能提供至少500mA的连续电流并在电源入口和芯片电源脚放置足够的滤波电容如100uF电解电容并联0.1uF陶瓷电容。显示与通信选用0.96寸OLEDSSD1306驱动I2C接口功耗低、显示清晰。WiFi模块选用ESP-01S通过串口与STM32通信实现数据上传到服务器或云平台。3. 软件架构与核心逻辑实现软件部分采用前后台超级循环架构对于本应用足够清晰高效。程序主要包含以下几个模块系统初始化、红外信号采集与处理、人数统计逻辑、OLED显示驱动、WiFi通信协议。3.1 红外信号采集与数字滤波这是确保计数准确的第一道关卡。我们使用STM32的一个外部中断引脚如PA0来接收红外接收头的输出信号。当无人遮挡时接收头输出高电平遮挡时输出低电平。// 伪代码示例外部中断服务函数 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) ! RESET) { // 读取当前引脚电平 uint8_t current_state GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); // 将事件放入一个队列或设置标志位供主循环处理 ir_event_queue[queue_tail] current_state; EXTI_ClearITPendingBit(EXTI_Line0); } }然而机械振动、瞬间的遮挡物如飞虫会导致信号的抖动产生多次快速的高低电平变化。必须在软件中进行去抖Debounce。我采用“延时确认法”在中断中处理当检测到电平变化如下降沿时不立即认为有人通过而是启动一个定时器如10ms。10ms后再次检测该引脚电平如果仍然是低电平才确认是一次有效的遮挡开始。// 主循环中处理去抖逻辑 if(ir_event_flag) { // 中断设置了标志 delay_ms(10); // 阻塞延时在实际项目中用定时器非阻塞方式更好 if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) 0) { // 确认是有效的遮挡开始 person_detected_start(); } ir_event_flag 0; }3.2 核心算法如何区分“一个人”这是本项目的灵魂。简单的“遮挡-恢复”就计数会导致人挥手、侧身、携带大件行李等情况多次计数。我设计了一个状态机来更智能地判断一次完整的通过。我们定义两个红外对管管A和管B一前一后安装间距约20-30cm根据人通过速度调整。通过判断A、B被遮挡的先后顺序不仅可以计数还能判断方向进/出。状态机有以下几个状态IDLE空闲状态A、B均无遮挡。A_TRIGGEREDA管首先被遮挡。启动一个计时器T1例如正常人走过20cm距离的时间假设为0.2-1秒。A_B_TRIGGERED在T1超时前B管也被遮挡。这很可能是一个人正常通过。启动另一个计时器T2等待B管恢复人离开B。B_TRIGGEREDB管首先被遮挡与A_TRIGGERED逻辑对称用于反方向。COUNT_PENDING当处于A_B_TRIGGERED状态且B管恢复人完全通过时进入此状态。这里加入一个“确认时间”T3如100ms如果在此期间A、B都保持未遮挡则确认一次有效计数方向为A-B进入并将计数加1。然后状态回到IDLE。这个状态机有效解决了以下问题在A处徘徊触发A后一直不触发B计时器T1超时后状态重置为IDLE不计数。反向通过B-A由状态B_TRIGGERED和对应的逻辑处理实现反向计数。两人紧贴第一个人触发A、B并计数后状态回到IDLE。此时第二个人如果已经触发了A系统会重新从A_TRIGGERED开始识别只要时间间隔大于状态机处理时间就能区分。遮挡物未完全离开通过“确认时间”T3确保光束完全恢复后再进行计数避免同一人被重复分割计数。实操心得计时器T1、T2、T3的参数需要现场实地调试。在办公室用步行速度调好的参数放到人流量大、行走速度多变的商场门口可能就不准了。最好的办法是留出通过串口或按键在线调整这些参数的功能方便现场校准。3.3 数据展示与上传本地显示使用OLED实时刷新当前总人数、进出人数、系统状态等。使用硬件I2C驱动注意初始化时的速率设置过高的速率可能导致显示异常。WiFi通信采用ESP-01SSTM32通过串口USART以AT指令与其交互。为了通信稳定我设计了简单的应用层协议心跳包每30秒发送一次维持TCP连接格式如[HEARTBEAT]\n。数据上报包当人数发生变化时或定时如每1分钟上报格式如[DATA]IN:123,OUT:122,TOTAL:245\n。指令响应ESP-01S收到服务器指令如重置计数器后转发给STM32STM32执行后回复确认。串口通信必须做好数据帧解析和超时重发。我使用一个环形缓冲区接收ESP-01S返回的数据然后逐字节解析寻找换行符\n作为帧结束标志。每个AT指令发送后等待特定时间如2秒内的响应如果超时或响应错误则重试最多3次。重试多次失败后系统应尝试重启ESP-01S通过控制其EN或RST引脚或切换到离线模式仅本地计数。// 简化的AT指令发送与等待响应函数 int8_t send_at_command_and_wait(const char* cmd, const char* expected_resp, uint32_t timeout_ms) { clear_uart_buffer(); uart_send_string(cmd); uint32_t start_tick get_tick(); while((get_tick() - start_tick) timeout_ms) { if(uart_receive_line(buffer)) { // 接收到一行数据 if(strstr(buffer, expected_resp) ! NULL) { return 0; // 成功 } else if(strstr(buffer, ERROR) ! NULL) { return -1; // AT指令错误 } } } return -2; // 超时 }4. PCB设计要点与抗干扰考量将原理图转化为可靠的PCB是产品化的关键一步。本项目PCB为双层板布局布线遵循以下原则分区布局电源区电源接口、稳压芯片、滤波电容集中放置在板子入口处。数字区STM32、晶振、下载接口放在板子中部远离模拟部分。模拟/传感区红外发射接收电路、运放如果用的话放在板子一侧特别是接收端的前级放大电路要特别敏感。射频区ESP-01S模块尽量放置在板边其天线部分下方和周围尽量净空不走线、不铺铜以减少对数字信号的干扰。电源与地处理采用“星型”或“单点”接地策略特别是模拟地AGND和数字地DGND在一点相连通常在电源芯片附近。为关键芯片STM32、运放的电源引脚就近放置去耦电容0.1uF陶瓷电容并确保回流路径最短。电源走线尽量宽减少压降。信号完整性红外接收头的输出信号线尽量短并远离时钟线、PWM线等高速数字信号线。必要时可以包地处理。晶振电路靠近STM32的OSC_IN/OSC_OUT引脚下方禁止走线并用地线包围。红外对管接口发射和接收管通过排针或导线外接。在PCB接口处为接收管信号线预留一个RC低通滤波电路的位置如1K电阻串联对地100pF电容如果现场干扰大可以焊上进一步滤除高频噪声。5. 系统调试与现场部署实录硬件焊接完成、程序烧录后真正的挑战才刚刚开始。实验室环境和实际部署环境天差地别。5.1 实验室调试步骤电源与最小系统先不焊传感器和外设只焊接STM32最小系统、电源和下载口。测试3.3V电压是否稳定能否正常烧录和运行一个简单的LED闪烁程序。红外传感电路单独测试用示波器观察红外发射管两端的电压确认PWM调制信号38kHz正常。用物体遮挡观察接收头输出引脚的电平变化是否干净利落没有毛刺。如果使用自搭建接收电路需要用示波器仔细调整运放的放大倍数和比较器阈值直到输出理想的方波。集成测试将所有模块焊接。编写测试程序让OLED显示当前A、B传感器的实时状态0/1并通过串口打印出来。用手模拟人通过观察状态变化和打印信息是否符合状态机设计。WiFi联调使用USB转TTL工具直接连接ESP-01S测试其AT指令是否正常能否连接指定的WiFi和服务器。然后再集成到系统中测试STM32与它的串口通信。5.2 现场部署常见问题与解决现场部署时我遇到了几个典型问题问题阳光直射导致误触发。现象在室内工作正常放到室外门口即使没人计数器也会偶尔跳动。排查用示波器看接收头输出发现即使未遮挡输出基线也不在稳定的高电平而是在轻微波动。强阳光包含了红外成分被接收头部分接收。解决物理屏蔽为红外对管制作一个遮光筒只允许正前方的光进入大幅减少环境光干扰。软件优化提高判断阈值。原来判断遮挡是电平变低现在改为“电平低于某个阈值如0.8 * VCC”才算。可以通过ADC读取接收头输出如果是模拟输出或者选用带模拟输出的接收器件。调制深度增加红外发射管的驱动电流在安全范围内提高发射信号强度提升信噪比。问题两人并排或推车通过只计一次数。现象两个人紧挨着同时通过系统只计为1人。分析这是红外对管方案的物理限制。光束是一条线如果两个人体在垂直于光束的方向上重叠系统无法区分。解决这属于方案局限性很难从根本上解决。可以尝试安装两套独立且水平方向有一定间隔的红外对管通过逻辑判断两套传感器触发的时间差极小来推测可能是多人但这会增加成本和复杂度。对于大多数统计场景允许一定的误差如95%准确率是可以接受的。需要在项目需求阶段明确这一点。问题WiFi频繁断线重连。现象设备运行一段时间后数据上传中断。排查查看ESP-01S的串口日志发现经常有“断开连接”或“连接失败”的信息。现场WiFi信号强度可能不稳定。解决增强软件鲁棒性在代码中增加更完善的重连机制。不仅仅是重启模块而是先尝试断开旧连接重新扫描WiFi选择信号最强的AP如果配置了多个进行连接。增加看门狗启用STM32的独立看门狗IWDG在WiFi通信卡死时复位整个系统。硬件优化检查ESP-01S的电源确保在发射瞬间电压跌落不超过0.3V。可以在其VCC引脚就近增加一个大容量钽电容如100uF。备用方案增加SD卡模块在网络异常时将数据暂存到SD卡网络恢复后补传。问题长期运行后计数漂移。现象设备运行一周后统计的总人数与人工记录或视频回放核对有少量累积误差。排查可能是状态机的计时参数T1 T2 T3不完全适应该地点所有人流速度。例如老人小孩通过慢可能导致T1超时被漏计快速跑过可能导致系统来不及反应。解决进行长时间的数据采集和对比分析。录制一段高峰期视频人工计数与设备计数对比。根据误差情况动态调整状态机的超时参数。最好能将参数做成可配置项通过串口或服务器下发。6. 程序代码结构详解与关键函数“程序代码等6个.zip”中的工程结构清晰基于STM32标准库。这里挑几个核心文件讲解Project/ ├── CMSIS/ /* ARM Cortex-M核心支持文件 */ ├── STM32F10x_StdPeriph_Driver/ /* STM32标准外设库 */ ├── User/ │ ├── main.c /* 主函数初始化与主循环 */ │ ├── stm32f10x_it.c /* 中断服务函数 */ │ ├── ir_sensor.c /* 红外传感采集与状态机处理 */ │ ├── oled.c /* OLED显示驱动 */ │ ├── wifi_esp01s.c /* ESP-01S通信模块驱动 */ │ ├── timer.c /* 定时器配置用于去抖、状态机计时 */ │ └── utils.c /* 工具函数如延时、字符串处理 */ └── … (其他配置文件)ir_sensor.c中的状态机实现简化版typedef enum { S_IDLE, S_A_FIRST, S_B_FIRST, S_BOTH_COVERED_A_FIRST, S_BOTH_COVERED_B_FIRST, S_PENDING_COUNT } DetectState_t; volatile DetectState_t g_detect_state S_IDLE; uint32_t g_timer_start_ticks 0; uint16_t g_count_in 0, g_count_out 0; void ir_sensor_state_machine_handler(uint8_t sensor_a, uint8_t sensor_b) { switch(g_detect_state) { case S_IDLE: if(sensor_a 0 sensor_b 1) { // A被遮挡B空闲 g_detect_state S_A_FIRST; g_timer_start_ticks get_system_tick(); } else if(sensor_a 1 sensor_b 0) { // B被遮挡A空闲 g_detect_state S_B_FIRST; g_timer_start_ticks get_system_tick(); } break; case S_A_FIRST: if(sensor_b 0) { // 在超时前B也被遮挡 g_detect_state S_BOTH_COVERED_A_FIRST; // 可以重置一个计时器等待B恢复 } else if(is_timeout(g_timer_start_ticks, T1_TIMEOUT)) { // A处遮挡物离开或不是人复位 g_detect_state S_IDLE; } break; case S_BOTH_COVERED_A_FIRST: if(sensor_b 1) { // B恢复人离开B g_detect_state S_PENDING_COUNT; g_timer_start_ticks get_system_tick(); } break; case S_PENDING_COUNT: if(sensor_a 1 sensor_b 1) { // A也恢复了 if(is_timeout(g_timer_start_ticks, T3_CONFIRM)) { g_count_in; // 完成一次进入计数 g_detect_state S_IDLE; // 触发数据更新和上传 data_updated 1; } } else { // 在确认期间又有遮挡可能不是一次完整通过复位到合适状态 g_detect_state S_IDLE; } break; // ... 其他状态S_B_FIRST, S_BOTH_COVERED_B_FIRST逻辑对称 default: g_detect_state S_IDLE; break; } }wifi_esp01s.c中的数据处理与发送 数据上报不宜过于频繁。我设置了一个标志位data_updated当计数变化时置位。在主循环中检查该标志并启动一个“上报延时计时器”。例如延时5秒后如果data_updated仍为1则打包数据并发送。这样可以避免网络拥堵也防止因一个人通过时光束抖动导致短时间内多次上报。7. 项目总结与扩展思考这个基于STM32的人流量检测器项目从构思到稳定运行花费了不少调试和优化的时间。它让我深刻体会到嵌入式开发不仅仅是写代码更是硬件、软件、环境、需求之间反复权衡和妥协的艺术。几个关键的体会传感器是系统的眼睛但眼睛也会“看错”。红外对管方案成本低但对环境非常敏感。调制解调是必须的物理遮光同样重要。在预算允许下可以考虑使用超声波测距模块或激光对射传感器它们的抗干扰能力更强检测距离更远但成本也更高。状态机是逻辑控制的利器。对于这种顺序事件的处理状态机比一堆if-else语句清晰、健壮得多。画好状态转移图再编码事半功倍。现场调试时间可能远超开发时间。实验室里一切完美到现场可能问题百出。务必留出充足的现场调试和参数校准时间并且最好在软件上预留调试接口如通过串口实时修改参数、查看状态。电源稳定性是一切的前提。尤其是使用WiFi、4G等无线模块时其发射瞬间的电流冲击必须考虑在内电源滤波和PCB布局布线必须认真对待。这个项目还可以从多个方向扩展低功耗设计如果采用电池供电可以使用STM32的低功耗模式让红外发射管间歇工作如发射100ms休眠900ms并选用低功耗的LoRa或NB-IoT模块替代WiFi。多区域组网在大型场所部署多个检测点通过ZigBee或LoRa自组网将数据汇总到一个网关再上传。数据融合结合简单的压电传感器或地磁传感器提高计数准确率尤其是在推车、行李箱干扰大的场景。本地存储与备份增加SPI Flash或SD卡存储长时间的历史数据即使网络中断数据也不丢失。最后所有的设计文件、原理图、PCB、源代码都打包在分享的文件里了。硬件设计使用Altium Designer软件是Keil MDK。希望这份详尽的拆解能帮你避开我踩过的那些坑更顺利地完成你自己的项目。嵌入式开发动手去做遇到问题解决问题就是最好的学习方式。本文还有配套的精品资源点击获取
返回列表