1. 这个系统到底在解决什么问题
做嵌入式这些年,停车位检测这类题目我接触过不少,本科毕设、课程设计、甚至一些创业项目里都反复出现。先说结论:基于单片机的超声波停车位自动检测系统,本质上就是用一块成本极低的MCU加一个超声波测距模块,实现对某个停车位"有车/没车"状态的自动判断,再把结果通过显示屏、指示灯或者蜂鸣器直观地告诉车主。听起来很简单,但真要在实际场地里稳定运行,门道比想象中多。
这个项目为什么值得做?因为它是典型的"小系统、全流程"训练:涉及传感器原理、信号时序、MCU定时器操作、逻辑判断、人机交互、现场调试,几乎把嵌入式开发的核心环节都过了一遍。对新手来说,它是很好的练手项目;对有一定经验的人来说,把它做扎实也能体现工程化思维——比如阈值怎么定才不会被隔壁车位干扰、探头装多高才能兼顾轿车和SUV、下雨天地面有水会不会误判。
我写这篇文章,就是想把我实际做这一类系统时踩过的坑、总结过的经验完整记录下来。无论你是准备交毕设作业,还是想给自己车库做一个简陋但够用的检测提示,下面这些内容应该都能直接帮上忙。
2. 方案选型:核心器件到底怎么挑
2.1 单片机选STC89C52还是STM32
这是所有初学者第一个纠结的问题。我的建议非常直接:如果目的是学习原理、完成课程设计,选STC89C52就足够了;如果你的系统还要做上位机通信、多车位管理、OLED显示这些复杂功能,直接上STM32。
STC89C52是增强型8051内核,51单片机里最经典的一代,资料多到看不完,几乎任何一本单片机教材都会讲到它的定时器和外部中断。做超声波测距,核心需求就是"精确计时",STC89C52有两个16位定时器,配合12MHz晶振,每计数一次恰好1微秒,计算距离非常方便。这个精度的计时能力,对HC-SR04这种模块已经绰绰有余。
STM32的优势在于资源丰富、主频高、外设库成熟,但随之而来的是学习成本和开发复杂度。用STM32做这个题目,等于拿牛刀杀鸡——不是说不行,而是很多精力会花在配置外设上,反而忽略了传感器应用本身。我的原则是:一个项目的复杂度应当刚好匹配它的核心目标。核心目标是"测距+判断+显示",51完全够用;核心目标是"联网+云平台+多车位调度",那再考虑更强大的平台。
2.2 超声波模块:HC-SR04依然是首选
市面上常见的超声波测距模块有HC-SR04、US-015、JSN-SR04T等多种。我做过对比,综合稳定性、成本、资料丰富度,HC-SR04是这类项目的最优解,没有之一。
HC-SR04的关键参数:工作电压5V,静态电流小于2mA,探测距离标称2cm到400cm,精度可以达到3mm。它内部集成了超声波发射电路、接收电路和信号处理电路,对外只暴露四个引脚:VCC、GND、Trig(触发)、Echo(回波)。MCU只需要给Trig一个10微秒以上的高电平,模块就会自动发射一串8个周期的40kHz超声波脉冲,然后Echo引脚输出一个高电平脉宽,这个脉宽对应超声波往返的时间。
这套"傻瓜式"接口设计,让单片机端的工作变得极其简单。唯一要留意的是市面上HC-SR04假货很多,我实测过一些低价模块,最典型的问题是回波信号抖动大、量程虚标(标称4米实际2米就测不准了)。挑模块的时候,优先选有品牌、有正规渠道的,别在源头上给自己埋雷。
2.3 显示与提示方案怎么搭配
显示部分的选择,直接决定了系统的直观程度和成本,常见的有三种:
| 方案 | 成本 | 显示内容 | 适用场景 |
|---|---|---|---|
| LCD1602液晶屏 | 低 | 距离数值、车位状态文字 | 室内演示、课设答辩最常用 |
| 数码管(4位) | 最低 | 距离数值、状态码 | 成本敏感、追求极简 |
| OLED屏幕 | 中 | 大字体状态、图标 | 想做得更现代、更美观 |
我个人推荐LCD1602。它显示两行16个字符,第一行显示"Distance: 0.35m",第二行显示"Car detected!"或者"Empty",非常直观。LCD1602支持4位和8位两种接线模式,用4位模式可以省4个IO口,代码稍复杂一点,但51的IO本来就不宽裕,该省则省。
提示设备里还应该有一个蜂鸣器和一个指示灯。蜂鸣器选有源蜂鸣器就行,IO口拉高就响,不需要额外驱动电路(当然用三极管扩流更稳妥)。指示灯用普通LED加限流电阻。有车时红灯亮、蜂鸣器短响,无车时绿灯常亮,这是最招牌的基础配置。
2.4 为什么排除红外和摄像头方案
很多人会问:停车位检测为什么不直接用红外对射或者摄像头识别?理由其实很实在:红外对射方案适合装在车位入口两侧,防车辆闯入,但没法判断车位内是否已有车,而且户外强光环境下误报率不低。摄像头识别方案准确率确实高,但涉及图像采集、模型推理,成本和开发量比单片机方案高一个数量级,显然不是这个项目该做的事。
超声波方案的优势恰恰在于:不受可见光干扰、夜间照常工作、成本极低、算法简单可靠。当然它也有弱点——测量结果受温度和风影响,多探头场景下有串扰风险,这些问题的应对方法我在后面调试章节会详细讲。
3. 硬件连接与安装:这一步决定你后面少踩多少坑
3.1 完整的引脚连接对照
基于STC89C52典型接法,我整理了一张表,照这个接基本不会错:
| 模块 | 引脚 | 连接目标 | 说明 |
|---|---|---|---|
| HC-SR04 | VCC | +5V | 供电必须稳定 |
| HC-SR04 | GND | GND | 共地是必须的 |
| HC-SR04 | Trig | P1.0 | 触发信号输入 |
| HC-SR04 | Echo | P1.1 | 回波信号输出 |
| LCD1602 | RS | P2.0 | 寄存器选择 |
| LCD1602 | RW | P2.1 | 读写选择,直接接地也可省IO |
| LCD1602 | EN | P2.2 | 使能信号 |
| LCD1602 | D4-D7 | P2.4-P2.7 | 4位数据线 |
| 蜂鸣器 | 正极 | P1.2(经三极管) | 有源蜂鸣器 |
| LED | 正极 | P1.3(串联1k电阻) | 状态指示灯 |
有几个细节特别提醒:Echo引脚输出的是5V高电平脉冲,51单片机IO可以直接承受,但如果你换用了3.3V的MCU,一定要加电阻分压,否则会把MCU烧掉。另外,给整个系统供电时,尽量用独立的5V稳压电源,不要和电机、继电器这类感性负载共用电源,否则Echo信号会叠满毛刺,测距值忽大忽小,这是最典型的坑之一。
3.2 供电与走线的基本功
整个系统的电流消耗不大,HC-SR04工作时峰值电流大约15mA,LCD1602背光电流在几十毫安,蜂鸣器算上蜂鸣瞬间大概20mA。也就是说,只要用一个输出能力在500mA以上的5V电源就够了,USB充电头、充电宝都能胜任。但有一点必须较真——HC-SR04的供电质量直接影响测距精度。如果供电电压掉到4.5V以下,模块发射功率会下降,回波变弱,远距离目标就可能测不到。
走线方面,Echo信号线最好短而直,避免与Trig线并行走长线。如果项目需要把探头用延长线引出,优先使用屏蔽线,屏蔽层单端接地。这些基本功看着不起眼,但在现场调试的时候,能省下一大半"明明代码没问题却不工作"的排查时间。
3.3 探头安装位置和高度是个学问
安装是整个项目里最容易被忽视、也最影响实际效果的部分。超声波探头一定要避开车位正上方的吊灯、管道这类反射物,更要避开相邻车位的探测范围。我见过一个现场,探头装在车位后墙上,结果旁边车辆倒车入库时,反射波直接串进本车位的探头,造成大面积误报。
推荐的安装方式有两种。一种是顶装下视:探头安装在车位上方约2.5米处,垂直朝下。车位空着时,超声波打到地面,回波距离约2.5米;有车时,超声波打到车顶,距离会缩短到0.5米到1米左右。这种方式的优点是探测范围集中,不太受隔壁车位影响。另一种是侧装斜视:探头装在车位侧方墙壁,斜角度照射车位中部。这种方式安装方便,但反射信号离散性大,调试难度高。
不论哪种方式,都必须在安装位置实测空旷距离和有车距离,而不是凭感觉定阈值。这个标定过程,我放到第5节专门讲。
4. 核心原理与软件实现:测距到底怎么算
4.1 超声波测距的基本原理
超声波测距的原理说白了就是回声定位,跟蝙蝠飞行的原理一模一样。模块发射一束40kHz的超声波脉冲,声波遇到障碍物后被反射回来,模块计算出从发射到接收的时间差Δt。声波在空气中的传播速度约340m/s,那障碍物距离就是:
[ D = \frac{v \times \Delta t}{2} ]
除以2是因为声波走了个来回。把速度换算成常用单位:340m/s = 0.034cm/μs。所以从Echo引脚读到的脉宽如果是T微秒,距离就是:
[ D(\text{cm}) = T \times 0.034 / 2 = T \times 0.017 ]
举个例子,如果Echo高电平持续时间是1000微秒,距离就是17厘米;持续2000微秒,距离就是34厘米。这个换算关系是写代码时反复要用的,建议直接背下来。
4.2 STC89C52的精确定时方案
既然核心是测量Echo高电平的持续时间,那计时精度就决定了测距精度。STC89C52用12MHz晶振时,机器周期正好是1微秒(12个时钟周期),所以定时器每计数一次就是1微秒。我们把定时器0配置为16位模式,Echo一拉高就开始计数,Echo一拉低就停止计数,这样计数器里的数值直接就是微秒数,不需要任何换算。
这里有个前提必须提醒:12MHz晶振下串口波特率会有误差,如果这个系统还打算做串口通信,建议换11.0592MHz晶振。用了11.0592MHz后,计数器每次是12÷11.0592≈1.085微秒,距离公式就要相应修正为T×1.085×0.017。不是不能用,只是每次计算都要多乘一步。我给的示例按12MHz来写,公式最清爽。
4.3 主程序框架与关键代码
整体逻辑是一个循环:测距→显示→判断→提示→延时→再测。为了让测量稳定,我建议在同一轮循环里连续采样5次,去掉最大值和最小值后取中间3个值的平均,这就是最简单的滑动滤波。
#include <reg52.h> sbit TRIG = P1^0; sbit ECHO = P1^1; // 10微秒延时 void delay_10us(void) { unsigned char i = 12; while (--i); } // 超声波测距,返回距离(cm),超时返回0 unsigned int get_distance_cm(void) { unsigned int cnt; TRIG = 1; delay_10us(); // 触发脉宽至少10us TRIG = 0; while (ECHO == 0); // 等待回波开始 TH0 = 0; TL0 = 0; TR0 = 1; // 启动定时器0计时 while (ECHO == 1) { // 超时保护:TH0超过0x3E大约就是16毫秒 // 对应距离约2.7米,超出范围就放弃这次测量 if (TH0 >= 0x3E) { TR0 = 0; return 0; } } TR0 = 0; // 停止计时 cnt = (TH0 << 8) | TL0; // 12MHz下1计数=1us,距离 = cnt * 0.017 (cm) return (unsigned int)(cnt * 17 / 1000); }这里有两个写代码时的关键点。第一,主循环开始前一定要初始化定时器工作在模式1(16位计数),并且关闭定时器中断,用查询方式就够,开中断反而容易把时序搞复杂。第二,超时保护必须有。如果探头前方是空的或者障碍物太远,Echo可能长时间不回来,没有超时保护程序就卡死在while循环里,整个系统直接"假死"。我在超时判断里拿TH0当粗判断,16位计数器溢出前就掐断,这样单次测量最坏等待时间可控。
4.4 车位占用判断逻辑
拿到距离值之后,怎么判断有没有车?逻辑上非常简单,就是一个区间比较:
#define CAR_DIST_MIN 10 // 低于10cm视为贴住探头的异常值 #define CAR_DIST_MAX 120 // 距离小于1.2米判定为有车 unsigned int dist = get_distance_cm(); if (dist == 0) { // 超时,说明前方空旷,判定为空车位 set_status(EMPTY); } else if (dist >= CAR_DIST_MIN && dist <= CAR_DIST_MAX) { // 距离在判定区间内,有车停入 set_status(OCCUPIED); } else { set_status(EMPTY); }为什么要设一个最小距离?因为如果车辆已经贴到探头正下方,距离值会小到失去意义;更关键的是,异常抖动可能产生一个极端小值,直接当有车处理会误报。设了最小阈值后,这段"盲区"被明确排除。
判定区间怎么定,就是我在上一节说的标定问题。比如现场实测空车位时探头到地面距离是250cm,有车时距离50cm,那阈值完全可以放到120cm甚至更宽;如果空车位时距离只到150cm(比如杂物较多),那阈值就得压缩到80cm。核心原则:先量两个状态的典型值,再取中间偏有车方向的值作为阈值。
5. 阈值标定与现场调试:没有这一步全是纸上谈兵
5.1 拿到现场第一件事:测基线
很多人在实验室把代码调通了,拿到现场就翻车,原因几乎都是没做基线测量。到现场第一件事不是改软件,而是架好设备,用串口或者显示屏把原始距离值打出来,连续记录5分钟,分别记录空车位和有车两种情况下的距离波动范围。
一个真实的标定记录可能是这样的:
| 场景 | 距离范围 | 均值 | 结论 |
|---|---|---|---|
| 空车位(无人无车) | 238-255cm | 246cm | 基线稳定 |
| 空车位(有人站立) | 130-160cm | 145cm | 人员干扰需注意 |
| 有车(轿车) | 42-55cm | 48cm | 判定区间可靠 |
| 有车(SUV) | 35-48cm | 41cm | 比轿车更近 |
| 有车(人下车后) | 42-70cm | 50cm | 人员从车旁走过有波动 |
从这张表能看出,空车位和有车的距离区间之间有接近100cm的间隔,这种情况下阈值设在110cm左右非常安全。反过来,如果测出来两个状态的间隔只有20cm,那就不是调阈值能解决的,得先检查探头安装角度或者供电质量。
5.2 人员走动和邻位干扰怎么处理
停车位检测最大的干扰源其实就是人。司机下车、行人从车位前走过,都会短暂进入探头的波束范围,产生一个"伪有车"信号。我的处理思路是时间确认机制:连续3次采样(间隔200ms)都判定为有车,才真正切换状态。这样一帧的人影晃动数据根本撑不到3次连续判断,Hysteresis也顺便解决了临界值抖动问题。
相邻车位的串扰是另一个高频问题。超声波模块的接收头比较灵敏,隔壁车位的探头如果正对着本探头,可能出现互相收到对方反射波的情况。解决办法有三个层次:第一,安装时让探头朝向尽量错开;第二,在软件上对连续两次测量的间隔做个随机化,避免两个探头的触发节奏完全同步;第三,实在躲不开,就换带编码的超声波模块,比如US-015支持多地址,那成本就上去了。
5.3 环境温度对测距的隐性影响
很多人不知道,声速并不是恒定的340m/s,实际声速随温度变化:气温每升高1℃,声速大约增加0.6m/s。在0℃的冬天和35℃的夏天,同一段真实距离,测出来的数值会差好几个厘米。对停车位检测这种"二值判断"应用来说,温差导致的厘米级偏差通常在安全余量范围内,影响不大。但如果你追求精度,可以在软件里加一个温度补偿公式:
[ v = 331.4 + 0.6 \times T ]
用一个DS18B20温度传感器读环境温度,替换掉代码里写死的340m/s。这个扩展成本不到十块钱,但能让系统在极端天气下依然保持稳定的判定边界,做工程方案的时候属于加分项。
6. 常见故障排查与避坑实录
6.1 测量值乱跳:八成不是代码问题
我调试这类系统遇到最多的现象就是:LCD上显示的距离数值毫无规律地乱跳,比如一会儿30cm一会儿200cm。新手第一反应就是改代码,加滤波、改延时,折腾半天没用。按我的排查顺序,先查供电,再查接线,最后才查代码。
供电问题看一个细节:HC-SR04测量瞬间电流会突然增大,如果电源线太长太细,电压会被拉低,模块工作不稳定。用万用表测一下模块VCC引脚在测量瞬间的电压,如果低于4.8V,果断换粗线或者就近加一个100μF电解电容。接线问题主要看Echo引脚到单片机之间有没有过长平行走线,如果有,把线分开或者换屏蔽线。代码问题反而最简单,确认有没有超时保护、触发脉宽够不够10微秒就行。
我还遇到过一种隐蔽情况:LCD1602和HC-SR04共用同一组IO口扩展板上的电源,结果LCD背光一开启,超声波电源纹波就变大,距离测量值就开始跳。解决办法是把两个模块的电源从单片机板上分开取电,各走各的支路,问题立刻消失。
6.2 现场常见问题速查表
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| 上电后LCD不亮 | 对比度电位器没调好 | 调节VO引脚电位器,直到有字符显示 |
| 显示距离一直为0 | Echo没信号/超时保护触发 | 检查Echo接线,确认模块供电正常 |
| 距离偏大且固定 | 探头对着软性材料(如海绵) | 更换安装位置,避开软质吸声物 |
| 距离在小范围抖动 | 供电纹波大/声波多径反射 | 加强滤波电容,调整探头角度 |
| 空车位误报有车 | 人正在车位旁活动 | 增加连续判断次数,调大阈值 |
| 有车时报空车位 | 车车身吸声强(某些车型) | 降低探头安装高度,或调整朝向 |
6.3 测试时的三个实用技巧
第一,在显示屏上同时显示原始距离和判定状态,调试时能一眼看出是测量问题还是判断问题。第二,在代码里加一个"布局模式"——按住按键就进入连续测距显示模式,不做过任何判断逻辑,方便现场安装时实时看距离变化来调整探头角度。第三,测试时故意制造边界场景:让车只停进半个车位、让车斜着停、让一个人站在车旁边,这些场景都比理想状态更容易暴露判定逻辑的漏洞,提前测到就能提前改。
7. 更进一步:从单车位做到多车位联网
7.1 多车位扩展的基本思路
如果做完单个车位检测还不满足,最常见的扩展方向就是做成多车位系统。方案有两种:一种是每个车位一个独立单片机,各自带显示,互不通信,这是最简单的并联方案;另一种是多个检测节点通过总线(比如RS-485)汇聚到一块主控屏上,统一显示每个车位的状态。
我做过一个多车位的实际项目,当时用的是RS-485总线方案。每个车位节点用STC89C52加一个MAX485芯片,通过差分总线接到中控端的STM32上。为什么用RS-485而不是直接用无线?因为地下车库环境里2.4G信号衰减明显,而RS-485在几十米的走线上非常可靠,成本也就几块钱一颗芯片。总线方案里要注意每台设备的地址设置——用拨码开关最直观,维护的时候一目了然。
7.2 可以替换和升级的组件
这个系统每个环节都留有升级空间。超声波模块可以换成工业级探头,探测角度更小、抗干扰更强;MCU可以换成带CAN外设的型号,直接挂车载总线;显示部分可以升级成 TFT彩屏,把整个车库的动态画在小屏上。另外,如果希望手机推送车位状态,只需要在现有单片机后面加一个串口转WiFi模块(比如ESP8266)或者串口转4G模块,数据上行到云平台——但这部分工作已经超出"单片机应用"的范畴,属于物联网联调了。
我对这个项目最深的体会是:它真正训练的不是"会接线、会写代码",而是"面对一个真实物理世界里的问题,怎么把测量、判断、交互、容错这些环节工程化地组织起来"。很多人做毕设时把注意力全花在让屏幕显示一个距离值上,其实那只是起点。把阈值标定做成流程、把干扰处理做成策略、把故障排查做成清单,这些才是这个项目最有价值的部分。如果你正在做类似的系统,希望这篇内容能帮你少走几段弯路,把力气花在真正起作用的地方。