恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

STM32红外对射人流量检测器:从传感器选型到状态机编程实战

  • 首页
  • 资讯中心
  • /
  • STM32红外对射人流量检测器:从传感器选型到状态机编程实战

相关资讯

DeepSeek Harness 源码解读(四):一次 Turn 为什么会跑多个 Step 2026/9/2 6:27:33
Python自动化信息聚合:从零搭建RSS替代方案与日推系统 2026/9/2 6:22:33
ECharts地图实战:从GeoJSON到湖南下钻交互完整指南 2026/9/2 6:22:33

最新资讯

Python网络流量异常检测:从抓包到机器学习实战指南
基于STM32与NRF24L01的无线信号模拟系统设计:从FFT分析到DDS合成实战
FPGA手工RTL实现YOLOv2加速器:算法到硅片的系统工程
Harness架构:让AI Agent稳定干活的核心执行框架
针式打印机断针补偿原理与64位系统适配方案
基于STM32F4的UVC摄像头实现:从描述符到图像传输全解析

今日推荐

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案
用Python搭建搞笑语音助手:从语音识别到语音合成全教程
ROS2阿克曼底盘仿真:从运动学原理到Nav2导航集成实践

本周热门

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析
数字电路时序基石:深入理解建立时间与保持时间
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

STM32红外对射人流量检测器:从传感器选型到状态机编程实战

发布时间:2026/9/2 6:27:33
STM32红外对射人流量检测器:从传感器选型到状态机编程实战 简介本资源是一套面向嵌入式初学者与STM32进阶开发者的综合性人流量检测实践项目包聚焦于基于ARM Cortex-M内核的STM32单片机在智能感知场景中的工程落地。压缩包共含6个核心子项目如人流量检测器、机智云物联网控制、蓝牙数据传输、麦克纳姆轮小车控制等涵盖红外/超声波传感采集、中断计数算法、PWM风扇散热、PID电机驱动、UART/蓝牙通信及云端数据上报等完整技术链路配套C语言源码、Keil工程文件及README说明文档便于理解硬件接口配置与软件逻辑分层设计。资源总计10.47MB以工程代码为主辅以仿真与控制类模块结构清晰、模块解耦适合课程设计、毕业设计或竞赛原型开发参考。目前已有201人学习下载提供从传感器信号处理到远程监控的端到端实现思路显著降低物联网终端开发门槛。1. 项目概述从零到一打造STM32人流量检测器最近在整理硬盘翻出来一个老项目——“STM32单片机人流量检测器”的完整工程包。这个项目当年是为了解决一个小型展厅或门店的客流量统计需求而做的核心就是用最便宜的单片机和常见的传感器实现一个低成本、可离线运行、且数据相对准确的计数方案。现在回头看虽然技术不算前沿但其中涉及到的传感器选型、信号处理、抗干扰设计以及嵌入式编程的实战技巧对于刚接触STM32或者想做一个实用小项目的朋友来说依然非常有参考价值。今天我就把这个项目的设计思路、代码核心以及踩过的那些坑系统地梳理分享出来。简单来说这个人流量检测器就是一个基于STM32F103C8T6也就是常说的“蓝桥杯”最小系统板核心的嵌入式系统。它通过一对红外对射管或者一个热释电红外传感器PIR来感知人体的通过然后由单片机进行判断和计数最终将数据通过OLED屏幕显示出来并且可以通过按键进行清零等操作。整个系统硬件成本可以控制在50元以内程序代码量不大但麻雀虽小五脏俱全涵盖了GPIO输入输出、外部中断、定时器、OLED驱动、按键消抖等嵌入式开发的基础知识点。无论你是学生想做课程设计、毕业设计还是工程师需要一个快速的验证原型这个方案都能提供一个清晰的实现路径。2. 核心方案选型与设计思路拆解做一个人流量检测器首先得确定“怎么检测人”。市面上主流方案无外乎几种视频分析、Wi-Fi/蓝牙探针、红外对射、区域热感应PIR。对于我们的STM32单片机场景成本和实时性、复杂度是首要考量。2.1 传感器方案对比与抉择我们当时主要对比了两种最可行的方案红外对射管方案和热释电红外传感器PIR方案。方案一红外对射管我们最终采用的核心方案这个方案原理非常简单就像电影里那种红外报警光束。在通道两侧一边安装红外发射管持续发射红外光另一边安装红外接收管。当没有人通过时接收管能稳定接收到红外信号当有人通过挡住光束时接收管信号就会发生变化。STM32通过检测这个变化来判定一次通过事件。优点方向性强计数准确只要光束足够细且安装位置合理如门框上方几乎可以做到“一人一次遮挡触发一次”误判率低。响应速度快光电转换速度极快适合人流量较大的场景。电路简单驱动和接收电路都非常成熟成本极低。缺点安装要求高需要发射和接收端严格对准否则无法工作。易受环境光干扰强烈的日光或灯光如果包含红外成分可能会干扰接收管。这就需要通过调制解调技术比如让发射管发射38kHz的调制红外光接收管只解调该频率信号来规避增加了些许电路和代码复杂度。只能检测单点通常只能检测一个“面”或“线”无法感知人的移动方向除非使用两对构成一个方向判断系统。方案二热释电红外传感器PIRPIR传感器能检测人体发出的特定波长的红外线。当人体进入其感应区域并移动时传感器输出电平就会跳变。优点非接触安装方便像一个小模块通常自带菲涅尔透镜感应一个扇形区域安装时无需严格对位。能感知存在与移动可以判断区域内是否有人活动。缺点计数准确性差这是致命伤。PIR对移动敏感一个人在感应区内来回走动可能触发多次而缓慢移动或静止不动则可能无法触发。对于严谨的“通过次数”统计它并不合适。受温度影响环境温度剧烈变化可能引起误触发。有延迟触发和恢复有一定时间不适合高频率计数。实操心得经过实测对于“通过式”人流计数如门口红外对射方案的准确性远高于PIR。因此本项目核心采用了一对调制型红外对射管发射管一体化接收头如HS0038B它内部已经集成了解调电路只响应38kHz红外信号抗干扰能力很强。为了能判断人的进出方向我们升级为了两对红外对射管安装位置一前一后间距约30-50cm。通过判断两路光束被遮挡的先后顺序就能分辨是进入还是离开。2.2 主控与外围器件选型主控MCUSTM32F103C8T6这是STM32家族中最经典的“入门神器”ARM Cortex-M3内核72MHz主频64KB Flash20KB RAM完全满足本项目需求。它拥有丰富的外设多组GPIO、定时器、外部中断EXTI、USART等价格低廉资料和社区支持极其丰富。选择它几乎不会出错。显示器件0.96寸OLEDSSD1306驱动I2C接口选择OLED是因为它无需背光、自发光、对比度高、功耗低显示效果很“酷炫”。I2C接口只需要2根数据线SCL SDA节省IO口。相比于LCD它不需要初始化复杂的GRAM驱动简单。显示内容可以包括当前总人数、进入/离开人数、系统状态等。交互与存储按键使用1-2个机械按键用于手动清零计数、切换显示模式等。必须做软件消抖处理。蜂鸣器可选。用于每次计数成功的提示音或者报警提示。EEPROMAT24C02可选但推荐。用于掉电保存累计人流量数据。STM32F103C8T6片内Flash也可模拟EEPROM但读写次数有限且操作稍复杂外挂一颗I2C接口的EEPROM芯片更稳定方便。2.3 系统整体工作流程设计整个系统的逻辑流程图在脑海里应该是清晰的上电初始化初始化系统时钟、GPIO将两路红外接收引脚设置为输入模式并开启上拉初始化OLED、按键、蜂鸣器等、外部中断将红外接收引脚连接到EXTI设置为下降沿和上升沿触发、定时器用于按键扫描、消抖计时、可能的防重复触发延时。主循环main loop通常是一个空的while(1)循环或者只执行一些低优先级的任务如刷新OLED显示、扫描按键状态。核心的计数逻辑不在主循环中而是由外部中断服务函数来高效处理。中断服务程序核心当任何一路红外光束被遮挡接收头输出低电平或恢复输出高电平时触发外部中断。在中断服务函数中需要记录当前时间戳使用定时器计数器值。判断是哪一路光束A路还是B路触发的。根据A、B两路触发的先后顺序和时间差例如在200ms内判断是“进入”还是“离开”。更新对应的计数变量。清除中断标志位。显示与响应在主循环中将更新后的计数变量显示在OLED上。当按键被按下时执行清零或其它功能。3. 硬件电路设计关键细节解析光有思路不够硬件是实现的基础。这里有几个容易踩坑的细节。3.1 红外对射模块的电路设计与抗干扰我们选用的是发射管一体化接收头的方案。发射管需要串联一个限流电阻通常工作电流在20-50mA。为了让发射管发出38kHz的调制红外光我们不能简单地将它一直通电而是需要用STM32的一个GPIO引脚通过定时器PWM功能产生一个38kHz的方波信号来驱动它。这个频率必须与接收头如HS0038B的中心频率匹配。发射部分电路示例STM32 GPIO (PWM输出) ---[电阻R1, 200Ω]---|红外发射管正极| --- GND。红外发射管负极接GND。电阻R1的值根据电源电压如3.3V和发射管正向压降约1.2V、所需工作电流如30mA计算R1 (3.3V - 1.2V) / 0.03A ≈ 70Ω。为了保险和延长寿命可以选用100-200Ω的电阻。接收部分电路 一体化接收头通常有三个引脚VCC接3.3V、GND、OUT信号输出。OUT引脚直接连接到STM32的GPIO输入引脚并且强烈建议在OUT引脚与3.3V之间连接一个上拉电阻如10kΩ。因为接收头内部是集电极开路输出不上拉的话高电平状态是不确定的。注意事项这是第一个大坑。很多新手直接连接发现信号不稳定时好时坏问题往往就出在缺少这个上拉电阻。加上之后常态下未遮挡OUT为高电平遮挡时OUT变为低电平非常干净。3.2 电源与去耦设计STM32和数字电路对电源噪声比较敏感。虽然项目简单但良好的电源习惯必须养成。在STM32的每个电源引脚VDD/VSS附近都必须放置一个0.1uF的陶瓷去耦电容并且尽可能靠近芯片引脚。在整板电源入口处放置一个10uF-100uF的电解电容或钽电容用于缓冲低频噪声。如果使用有源蜂鸣器其工作电流较大可达几十mA最好单独一路电源驱动或者在其电源两端并联一个大电容100uF防止蜂鸣器工作时产生的电压波动影响单片机复位。3.3 按键与OLED接口按键电路采用经典的上拉电阻设计。STM32的GPIO可以配置为内部上拉这样外部电路可以简化只需一个按键接地即可。但为了更高的可靠性尤其是在电磁环境复杂时外部上拉电阻如10kΩ依然是个好选择。OLED的I2C接口别忘了在两个信号线SCL和SDA上各接一个4.7kΩ的上拉电阻到3.3V。这是I2C总线规范的要求否则通信可能失败。4. 软件程序设计核心与代码实现硬件搭好软件才是灵魂。我们使用Keil MDK开发环境基于STM32标准外设库Standard Peripheral Library进行开发。HAL库当然也可以但标准库对于理解底层寄存器操作更有帮助。4.1 工程结构与初始化工程目录应清晰/Project /CMSIS // Cortex微控制器软件接口标准文件 /StdPeriph_Driver // STM32标准外设库文件 /User main.c // 主文件 stm32f10x_it.c // 中断服务函数文件 oled.c/.h // OLED驱动 key.c/.h // 按键处理 infrared.c/.h // 红外计数核心逻辑 eeprom.c/.h // EEPROM操作可选 /Doc // 说明文档在main.c的初始化部分需要按顺序完成以下关键初始化int main(void) { // 1. 初始化系统时钟通常配置为72MHz SystemInit(); // 2. 初始化延时函数基于SysTick定时器 delay_init(); // 3. 初始化OLED显示 OLED_Init(); // 4. 初始化红外传感器GPIO与外部中断 Infrared_Init(); // 5. 初始化按键GPIO Key_Init(); // 6. 初始化定时器用于精确计时和防抖 Timer_Init(); // 7. 初始化蜂鸣器GPIO可选 BEEP_Init(); // 8. 从EEPROM读取历史数据可选 total_count EEPROM_Read(); OLED_Clear(); OLED_ShowString(0, 0, People Counter); OLED_ShowString(0, 2, In:0000 Out:0000); OLED_ShowString(0, 4, Total:000000); while(1) { // 主循环任务 Key_Process(); // 扫描并处理按键 OLED_Refresh(); // 刷新显示数据 // 其他低优先级任务... } }4.2 红外计数的核心逻辑实现状态机法这是整个项目最核心的代码。直接在主中断里做复杂判断容易出问题我们采用状态机State Machine的思路来优雅地处理两路红外信号。我们定义两个红外光束Beam_A靠近外侧 Beam_B靠近内侧。定义几个状态STATE_IDLE 空闲状态无人通过。STATE_A_BLOCKED A光束被遮挡。STATE_B_BLOCKED B光束被遮挡。STATE_A_B_BLOCKED A和B都被遮挡人正在通道中间。再定义两个全局变量记录状态和时间戳typedef enum { IDLE, A_BLOCKED, B_BLOCKED, BOTH_BLOCKED } IR_State_t; volatile IR_State_t ir_state IDLE; // 当前状态 volatile uint32_t timestamp_A 0; // A路触发时间戳 volatile uint32_t timestamp_B 0; // B路触发时间戳 volatile uint8_t direction 0; // 方向0-未知1-进入2-离开在外部中断服务函数中以A路触发为例void EXTI0_IRQHandler(void) { // 假设A路接在EXTI0 if(EXTI_GetITStatus(EXTI_Line0) ! RESET) { uint32_t current_time TIM_GetCounter(TIM2); // 获取当前定时器时间 if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) 0) { // A路变为低电平被遮挡 timestamp_A current_time; switch(ir_state) { case IDLE: ir_state A_BLOCKED; break; case B_BLOCKED: // B先被挡现在A也被挡可能是进入B-A if((current_time - timestamp_B) TIME_THRESHOLD) { ir_state BOTH_BLOCKED; direction DIR_IN; // 假设B在外A在内 B先A后为进入 } break; default: break; } } else { // A路恢复高电平 switch(ir_state) { case BOTH_BLOCKED: // 两者都挡着现在A恢复了检查B是否还挡着 if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) 0) { // B仍被挡 ir_state B_BLOCKED; } else { ir_state IDLE; // 一次完整的通过事件结束根据direction更新计数 if(direction DIR_IN) { count_in; total_count; } else if(direction DIR_OUT) { count_out; total_count--; } direction 0; // 重置方向 // 可以触发蜂鸣器提示 BEEP_On(); delay_ms(50); BEEP_Off(); } break; case A_BLOCKED: ir_state IDLE; // 可能是误触发或人退回 break; default: break; } } EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志 } }B路的中断服务函数逻辑与之对称。TIME_THRESHOLD是一个时间阈值比如200ms用于判断两次遮挡是否属于同一次通过事件防止两个人先后快速通过造成的误判。实操心得状态机的引入让逻辑变得非常清晰易于调试和维护。调试时可以通过OLED实时显示当前的ir_state和direction能直观地看到系统是如何判断一次通过事件的。这是调试复杂逻辑的利器。4.3 OLED显示与按键处理OLED显示部分就是调用驱动函数将计数值格式化成字符串显示。注意频繁刷新整个屏幕可能会闪屏可以采用局部刷新策略只更新数字变化的区域。按键处理一定要做消抖。最简单的办法是在定时器中断里比如每10ms一次扫描按键引脚状态用一个变量记录稳定按下的次数连续多次如5次读到按下状态才认为按键有效。// 在定时器中断中 void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { // 按键扫描 static uint8_t key_press_cnt 0; if(KEY 0) { // 按键按下低电平有效 key_press_cnt; if(key_press_cnt 5) { // 连续50ms稳定按下 key_press_cnt 5; g_key_event KEY_EVENT_PRESS; // 设置按键事件标志 } } else { key_press_cnt 0; } TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } }在主循环中检查g_key_event标志然后执行清零等操作。这种“扫描标志位”的方式是非阻塞式的不影响主循环其他任务。5. 调试过程与常见问题排查实录做嵌入式项目调试时间往往比写代码时间长。下面记录几个典型问题和解决方法。5.1 红外计数不准乱跳数这是最常见的问题。检查硬件首先用示波器或者万用表测量红外接收头OUT引脚的信号。无人时是否为稳定的高电平接近3.3V遮挡时是否跳变为干净的低电平接近0V如果高电平不稳检查上拉电阻。如果低电平不干净可能是发射管功率不足、没对准、或环境光干扰太强。为发射管增加聚光透镜或者将发射接收模块安装在遮光筒里可以有效改善。检查软件防抖我们的状态机逻辑里已经隐含了防抖通过时间阈值判断。如果还是乱跳可以增加一个“屏蔽期”。在一次有效的计数事件完成后设置一个短暂的屏蔽时间如300ms在这段时间内即使有红外信号变化也忽略不计。这可以防止人在通道内小幅晃动造成的重复计数。检查中断优先级和冲突确保红外外部中断的优先级设置合理并且中断服务函数执行时间尽可能短。如果中断函数里做了耗时的操作如delay_ms可能会丢失其他中断信号。所有耗时操作应放到主循环中基于标志位来处理。5.2 OLED显示不正常花屏或不显示检查硬件连接首先确认VCC、GND、SCL、SDA四根线是否接对、接牢。I2C的上拉电阻是否接上。检查初始化序列OLED模块上电后需要一段初始化命令序列才能工作。不同厂商的OLED驱动芯片SSD1306/SSH1106或屏幕尺寸128x64/128x32的初始化命令可能略有差异。确保你使用的OLED_Init()函数与你的屏幕匹配。最稳妥的方法是找到卖家提供的示例程序。检查I2C时序用逻辑分析仪抓取SCL和SDA的波形看是否符合I2C协议。STM32的I2C外设有时比较“娇气”如果软件模拟I2C检查GPIO_SetBits和GPIO_ResetBits之间的延时是否足够。可以适当增加延时微秒级。5.3 系统偶尔死机或复位电源问题用示波器观察3.3V电源轨在蜂鸣器响或红外发射管工作时是否有大的电压跌落如果跌落超过芯片的容忍范围会导致复位。解决方法加强电源滤波电容或为电机/蜂鸣器等大电流器件提供独立电源。堆栈溢出如果程序中使用了较大的局部数组或者递归调用可能导致栈空间不足。检查启动文件.s文件中分配的堆栈大小适当增大。中断服务函数未清除标志位这是一个致命错误。在任何中断服务函数结束时必须清除对应的中断标志位如EXTI_ClearITPendingBitTIM_ClearITPendingBit否则会连续触发中断导致程序卡死。5.4 方向判断错误如果发现进入和离开的判断经常反了。检查安装顺序确认你代码中定义的Beam_A和Beam_B与实际安装的物理位置对应关系是否正确。调换一下两路信号在代码中的逻辑即可。调整时间阈值TIME_THRESHOLD太短可能无法捕捉到正常的通过间隔太长又可能把两个人的通过误判为一个人。需要根据实际通道宽度和人的步行速度来调整。通常100ms到300ms是一个合理的范围。可以在OLED上显示这个阈值并提供一个按键来微调它直到达到最佳效果。6. 项目优化与扩展思路基础功能实现后可以考虑让它变得更“聪明”更实用。数据存储与上传使用AT24Cxx系列EEPROM定期如每分钟保存总人数防止掉电丢失。更进一步可以增加一个ESP8266 Wi-Fi模块将实时人数数据上传到云平台如阿里云、OneNET实现远程监控和数据统计。多种工作模式通过按键切换模式。比如“累计模式”、“清空模式”、“进出独立模式”、“峰值统计模式”统计一天中最大同时在场人数。低功耗设计如果用于电池供电场景需要大幅降低功耗。可以让STM32进入休眠模式Stop Mode仅由红外传感器触发的外部中断来唤醒它。在不计数时关闭OLED屏幕、蜂鸣器等外设的电源。增加其他传感器例如增加一个DHT11温湿度传感器在OLED上同时显示环境温湿度和人流量。或者增加一个光敏电阻根据环境光自动调节OLED亮度。外壳与安装设计一个3D打印的外壳将发射管和接收管精确固定在对立两侧避免手工安装的对准难题。外壳也能起到遮光、防尘的作用让产品更美观可靠。这个STM32人流量检测器项目从传感器原理到硬件选型从状态机软件设计到调试排错完整地走完了一个小型嵌入式产品的开发流程。它涉及的知识点非常典型且都具有很强的可迁移性。希望这份超详细的拆解能帮你不仅做出这个项目更能理解背后“为什么这么做”的思考过程。在实际动手时耐心调试善用示波器和逻辑分析仪你一定能做出一个稳定可靠的作品。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号