1. 项目概述一个“社交距离伙伴”能解决什么问题最近几年大家对于公共空间里的个人距离和健康防护意识有了显著提升。无论是去超市购物、在办公室办公还是在公园散步我们都希望能有一个简单、无感的方式来提醒自己与他人保持一个合适的距离。传统的办法是靠目测但这不仅不准确还容易分心。于是一个名为“Social Distancing Buddy”社交距离伙伴的小玩意儿就进入了我的视野。本质上它就是一个便携式的电子设备核心功能是当有其他人进入你预设的安全距离范围内时它会通过振动、灯光或声音发出警报从而帮助你更轻松地维持社交距离。听起来很简单对吧但要把这个想法变成一个可靠、好用且成本可控的实物产品里面涉及到的技术选型、电路设计、功耗控制和用户体验考量每一个环节都值得深挖。这个项目非常适合电子爱好者、创客甚至是想要了解物联网边缘设备开发的朋友。通过动手制作一个“社交距离伙伴”你不仅能得到一个实用的个人工具更能系统地学习到传感器应用、微控制器编程、低功耗设计等硬核技能。接下来我就把自己从构思到实现这个项目的完整过程以及踩过的各种坑毫无保留地分享出来。2. 核心方案选型与设计思路拆解做一个距离检测装置首先得确定“眼睛”和“大脑”。市面上测距的方案很多我们需要根据使用场景来权衡。2.1 传感器选型为什么是超声波而不是红外或激光最常见的近距离测距方案有红外、超声波和激光雷达。激光雷达精度高、响应快但成本也高用在消费级小设备上有点“杀鸡用牛刀”。红外测距比如夏普的GP2Y0A系列成本适中但它对物体的颜色和材质比较敏感深色物体可能测不准在复杂光线的室外环境也不稳定。所以我最终选择了HC-SR04超声波传感器。理由很充分首先它价格极其低廉十几块钱就能买到。其次它的测距原理是发射超声波并接收回波通过时间差计算距离几乎不受光线、颜色影响在室内外都能稳定工作。最后它的有效测距范围在2cm到400cm之间对于设定1米到2米的社交距离提醒来说完全够用。当然它也有缺点比如波束角比较大约15度意味着探测的是一个圆锥形区域而不是一个点同时对于柔软吸音的材质如窗帘探测效果会打折扣。但在人流走动的场景下这个波束角反而能提供一定的探测宽度算是一个可以接受的特性。2.2 主控芯片选择ESP32如何平衡功能与功耗确定了“眼睛”接下来是“大脑”。主控芯片需要处理传感器数据、判断距离阈值、并驱动警报装置。可选的有经典的Arduino UnoATmega328P、更小巧的ATTiny85或者功能强大的ESP32。Arduino Uno简单易用生态丰富但它功耗较高且需要额外的模块才能实现无线功能比如后期想加个手机提醒。ATTiny85功耗极低但引脚和计算资源有限扩展性差。我选择了ESP32原因在于它提供了一个完美的平衡点双核处理器与低功耗模式ESP32性能强大可以轻松处理传感器数据逻辑。更重要的是它支持深度睡眠模式当没有检测到人时可以让大部分电路进入休眠仅定时唤醒检测这对依赖电池供电的便携设备至关重要。集成无线连接ESP32自带Wi-Fi和蓝牙。虽然我们这个基础版本可能用不上但这为未来功能升级留下了巨大空间比如通过手机APP调整安全距离、查看警报日志、甚至多个设备组网联动。丰富的IO与ADC足够连接传感器、警报器和电池电量检测电路。2.3 整体系统架构设计基于以上选型整个设备的架构就清晰了感知层HC-SR04超声波传感器负责持续探测前方障碍物距离。控制层ESP32开发板如ESP32 DevKit C作为核心处理器。警报层由一个小型振动马达用于贴身静默提醒、一个彩色LED用于视觉提示和一个有源蜂鸣器用于声音提醒组成。用户可以根据场合选择启用哪种警报。供电层一枚3.7V的锂聚合物电池配合一个充放电管理模块如TP4056实现充电和稳压供电。交互层两个按钮用于开关机和调整距离阈值一个开关用于切换警报模式。这个架构兼顾了功能、成本和可扩展性。所有元件都很常见总成本可以控制在百元以内。3. 硬件电路设计与组装要点电路是项目的骨架设计不合理后面编程和调试会痛苦不堪。3.1 核心电路连接详解ESP32与各元件的连接需要特别注意电平匹配和驱动能力。以下是详细的接线表元件引脚连接至 ESP32 引脚说明与注意事项HC-SR04VCC3.3V切记不可接5V虽然模块标称5V但许多版本在3.3V下也能工作。接5V可能会损坏ESP32的GPIO。HC-SR04TrigGPIO 23触发测距信号输出脚HC-SR04EchoGPIO 22回波信号输入脚。Echo脚输出是5V电平必须通过一个1kΩ和2kΩ电阻组成的分压电路将电压降至3.3V左右再接入ESP32否则必烧芯片有源蜂鸣器GPIO 18通过一个NPN三极管如8050驱动ESP32的GPIO直接驱动能力有限。振动马达GPIO 19同样建议通过三极管驱动。LED (共阳)R, G, BGPIO 12, 14, 27每个颜色通道串联一个220Ω限流电阻。按键A (设置)一端GPIO 33另一端接地。配置为内部上拉输入按下时为低电平。按键B (开关/切换)一端GPIO 32同上。电池电量检测电池正极GPIO 34 (ADC)通过两个电阻如100kΩ和220kΩ分压后接入。确保分压后电压不超过3.3V。注意超声波传感器的Echo脚电平转换是硬件设计中最容易出错的地方。我一开始偷懒直接接了结果瞬间烧掉了一个ESP32的GPIO口。教训就是面对任何非3.3V的信号源先量电压再做分压不要心存侥幸。3.2 功耗优化设计心得既然是便携设备续航是硬指标。除了依赖ESP32的深度睡眠硬件上也能做很多优化电源路径管理使用带有“负载开关”功能的充放电管理模块。当设备通过按键唤醒时才给传感器和警报器供电睡眠时彻底断开它们的电源消除静态电流消耗。传感器供电控制将HC-SR04的VCC不直接接3.3V而是通过一个MOS管连接到ESP32的一个GPIO上。需要测距时GPIO输出高电平打开MOS管给传感器供电测距完毕立即断电。实测这个操作能让整体睡眠电流从几十mA降至几百微安。警报器选型振动马达和蜂鸣器要选择低电压、低电流的型号。LED使用PWM控制亮度在非警报状态下可以设置为极低亮度或熄灭。3.3 PCB设计与外壳考虑如果只是做原型在面包板或洞洞板上搭建即可。但如果想做得精致耐用设计一块简单的PCB是值得的。使用KiCad或立创EDA这类免费工具把核心电路包括电平转换、三极管驱动集成在一块小板上可靠性会大大提升。外壳可以使用3D打印。设计时要注意为超声波传感器开一个干净的窗口避免塑料外壳对声波造成干扰或折射。蜂鸣器开口要对着外侧并且做好密封避免声音在壳内闷响。预留充电接口和按键的孔位位置要方便操作。4. 软件逻辑与核心代码实现硬件搭好了接下来就是赋予它灵魂的软件部分。代码逻辑主要围绕“测量-判断-响应-睡眠”这个循环。4.1 超声波测距的稳定化处理HC-SR04的原始读数会有波动尤其是探测边缘或移动物体时。直接使用单次测量值容易导致误报警。必须进行软件滤波。// 定义引脚 const int trigPin 23; const int echoPin 22; float getFilteredDistance() { float distances[5]; // 采样5次 float sum 0; int validCount 0; for (int i 0; i 5; i) { // 触发测距 digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // 读取回波时间 long duration pulseIn(echoPin, HIGH, 30000); // 超时设置30ms对应约5米 float distance duration * 0.0343 / 2; // 声速取343m/s除以2 // 有效性检查在有效范围内2cm - 400cm if (distance 2 distance 400) { distances[validCount] distance; sum distance; validCount; } delay(30); // 两次测量间短暂延迟防止信号干扰 } if (validCount 0) return -1; // 无效测量 // 中位值平均滤波排序后取中间几个值的平均 std::sort(distances, distances validCount); float medianAvg 0; int start validCount / 4; // 去掉最低的1/4 int end validCount * 3 / 4; // 去掉最高的1/4 for (int i start; i end; i) { medianAvg distances[i]; } medianAvg / (end - start); return medianAvg; }这段代码做了几件事多次采样、剔除明显异常值过近或过远、然后使用中位值平均滤波法能有效抑制偶然的跳动得到稳定的距离值。4.2 多模式警报状态机实现设备应该有不同工作模式以适应不同场景比如“静音振动模式”、“办公室灯光模式”、“公共场所声音模式”。我用一个简单的状态机来实现。enum AlertMode { MODE_OFF, MODE_VIBRATE, MODE_LIGHT, MODE_SOUND, MODE_ALL }; AlertMode currentMode MODE_VIBRATE; int distanceThreshold 150; // 默认1.5米 void handleAlert(float currentDistance) { if (currentDistance 0 || currentDistance distanceThreshold) { // 无目标或距离安全关闭所有警报 digitalWrite(vibratePin, LOW); setLedColor(0, 255, 0); // 绿色安全 digitalWrite(buzzerPin, LOW); return; } // 进入警报距离 float proximityLevel (distanceThreshold - currentDistance) / distanceThreshold; // 0到1越近越大 switch (currentMode) { case MODE_VIBRATE: // 振动强度随距离变近而增强PWM控制 analogWrite(vibratePin, (int)(proximityLevel * 255)); break; case MODE_LIGHT: // 灯光从绿色渐变到红色 setLedColor((int)(proximityLevel * 255), (int)((1-proximityLevel) * 255), 0); break; case MODE_SOUND: // 蜂鸣器发出间歇性警报频率随距离变近而加快 int beepInterval 1000 - (int)(proximityLevel * 900); // 从1秒到0.1秒 if (millis() % beepInterval 100) { digitalWrite(buzzerPin, HIGH); } else { digitalWrite(buzzerPin, LOW); } break; case MODE_ALL: // 组合警报 analogWrite(vibratePin, (int)(proximityLevel * 200)); setLedColor((int)(proximityLevel * 255), 0, 0); if (millis() % 500 100) digitalWrite(buzzerPin, HIGH); else digitalWrite(buzzerPin, LOW); break; } }状态机的优势是逻辑清晰易于扩展新的警报模式。4.3 低功耗睡眠与唤醒策略为了实现长续航大部分时间设备应在深度睡眠中。我设计了一个“按需唤醒周期巡检”的策略。#define uS_TO_S_FACTOR 1000000ULL // 微秒到秒转换因子 #define TIME_TO_SLEEP 10 // 深度睡眠时间秒 void enterDeepSleep() { // 1. 关闭所有外围设备电源通过GPIO控制MOS管 digitalWrite(sensorPowerPin, LOW); digitalWrite(alertPowerPin, LOW); // 2. 配置唤醒源定时唤醒 或 按键唤醒 esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); esp_sleep_enable_ext0_wakeup(GPIO_NUM_32, 0); // 按键B低电平唤醒 // 3. 进入深度睡眠 Serial.println(进入深度睡眠); delay(100); // 等待串口输出完成 esp_deep_sleep_start(); } void setup() { // 判断唤醒原因 esp_sleep_wakeup_cause_t wakeup_reason esp_sleep_get_wakeup_cause(); if(wakeup_reason ESP_SLEEP_WAKEUP_TIMER){ Serial.println(定时唤醒开始一次检测); performCheck(); // 执行一次完整的测量和判断 } else if(wakeup_reason ESP_SLEEP_WAKEUP_EXT0){ Serial.println(按键唤醒进入配置模式); enterConfigMode(); // 长按按键进入阈值设置模式 } // 无论何种唤醒执行完任务后再次进入睡眠 enterDeepSleep(); }这个策略下设备每10秒自动醒来检测一次如果发现有人靠近并触发警报则会在警报期间保持唤醒状态。警报解除后如果一段时间内无新目标则再次进入深度睡眠。按键则用于主动唤醒和配置。实测下来一枚1000mAh的电池可以轻松支撑一周以上的使用。5. 调试、优化与常见问题实录把代码烧录进去设备跑起来这才是挑战的开始。下面是我在调试过程中遇到的一些典型问题及解决方法。5.1 传感器读数不稳定或跳变极大这是最常见的问题。除了前面提到的软件滤波硬件上也要检查电源噪声确保给传感器供电的3.3V电源是干净的。可以在VCC和GND之间并联一个10uF和0.1uF的电容滤除低频和高频噪声。声学干扰超声波传感器之间会互相干扰。如果两个设备靠得很近一个发出的波可能被另一个接收到。解决方法是在代码里错开它们的触发时间或者给传感器套上一个海绵或橡胶圈稍微限制一下声波的扩散角度。物理震动如果设备戴在身上走动震动可能导致传感器松动或测量基准面变化。确保传感器被牢固固定并且探测面朝向稳定。5.2 误报警对非人目标如墙壁、桌椅也报警这是超声波传感器波束角大的固有缺点。可以通过算法优化距离持续性判断只有目标持续出现在警报距离内超过一定时间如1秒才触发警报。短暂穿过探测区域的飞虫或飘过的塑料袋会被忽略。距离变化率判断人是在移动的距离值会持续变化。而墙壁的距离值是恒定不变的。可以计算连续几次测量的距离变化率导数如果变化率几乎为零则很可能是静止物体可以忽略。安装角度调整不要水平向前安装而是稍微向下倾斜一个角度。这样它的主要探测区域是地面以上的一段空间对远处的地面或低矮物体不敏感更专注于探测行人躯干部分。5.3 续航时间远低于预期如果发现设备半天就没电了需要系统性地排查“电老虎”测量睡眠电流使用万用表uA档串联在电池和主板之间在设备进入睡眠后测量。理想的整体睡眠电流应小于500uA。如果过高依次断开外围元件传感器、警报器、指示灯看是哪个部分漏电。检查“僵尸”外设有些外设即使在软件里设置为LOW或INPUT物理上仍可能产生电流。最彻底的方法就是用MOS管或模拟开关在硬件上彻底断电。Wi-Fi/蓝牙未关闭在进入深度睡眠前确保调用WiFi.mode(WIFI_OFF)和btStop()来关闭无线模块。串口日志输出Serial.print在睡眠时虽然不工作但串口模块本身可能耗电。在发布最终版本时可以移除所有调试用的串口输出代码。5.4 警报响应不及时或漏报这通常是因为睡眠-唤醒周期设置得太长。如果设备每10秒才醒一次那么有人快速从侧面经过时很可能被漏掉。需要在功耗和响应速度间权衡在人流量可能较大的环境如超市可以将睡眠时间缩短至3-5秒。引入**运动传感器如MPU6050加速度计**作为二级唤醒源。当检测到设备本身在移动时说明佩戴者在行走自动切换到更频繁的检测模式如每秒一次静止时则恢复长睡眠周期。这样能智能地平衡响应与续航。6. 功能扩展与进阶玩法基础功能稳定后就可以玩点花样了这也是ESP32的优势所在。6.1 无线功能升级蓝牙APP配置通过蓝牙我们可以用手机方便地调整设置而不用去按那个小小的按钮。在ESP32上运行一个蓝牙低能耗服务暴露几个特征值如“距离阈值”、“警报模式”、“电池电量”。开发一个简单的手机APP可以用MIT App Inventor或Flutter快速原型连接设备后可以滑动滑块调整距离点击按钮切换模式。设备端收到新配置后保存到非易失性存储中。这样一次设置永久生效。6.2 数据记录与统计给设备加上一个小型SD卡模块或利用ESP32的SPIFFS文件系统可以记录警报事件时间、距离。这对于研究公共空间人流密度、个人社交习惯分析会很有用。数据可以定期通过Wi-Fi上传到私有服务器生成简单的统计图表。6.3 从“伙伴”到“网络”想象一下在小型办公室或工作坊里每个人都佩戴一个。设备之间通过Wi-Fi或蓝牙Mesh网络进行通信不仅可以提醒自己还能在后台进行简单的接触追踪。当两个设备长时间处于近距离时各自在本地记录一个加密的接触事件。这完全在本地完成不涉及任何中心化服务器在保护隐私的前提下为小集体提供额外的安全参考。这个项目从一个小小的想法开始到最终做出一个可靠可用的设备整个过程充满了工程上的权衡和解决问题的乐趣。它涉及了硬件设计、嵌入式编程、传感器应用、功耗优化等多个方面是一个非常好的综合性实践。最大的体会是在嵌入式开发里软件和硬件的边界非常模糊一个稳定可靠的产品一定是软硬件协同设计、反复调试的结果。比如那个Echo脚的电平转换问题软件写得再好硬件上没做防护一上电就前功尽弃。所以动手之前多查资料连接电路时多一分谨慎调试时保持耐心这些经验比任何一个具体的代码片段都更有价值。