
1. 项目缘起与核心需求拆解1.1 为什么选择STM32做导盲语音系统做嵌入式项目选型第一步永远是问自己这个场景到底需要什么导盲设备的核心诉求其实非常明确——低功耗、实时响应、便携、成本可控。视障人士出行时设备需要持续工作数小时甚至一整天要能实时检测前方障碍物并给出语音提示同时整机重量和体积不能成为负担。市面上常见的方案有几种用Arduino做原型验证快但性能天花板低跑复杂算法吃力用树莓派性能强但功耗高、成本高、开机慢对于“按下开关就要立刻工作”的导盲场景并不合适用专用语音芯片方案简单但扩展性差想加个超声波阵列或者换传感器就得推倒重来。STM32系列恰好卡在一个很舒服的位置上。以STM32F103C8T6为例72MHz主频、64KB Flash、20KB SRAM跑超声波测距、语音播报、按键交互这套逻辑绰绰有余。更重要的是它的外设资源丰富多个定时器可以做精确的微秒级计时超声波测距必需多个UART可以同时接语音模块和调试串口ADC可以接电池电压检测做低电量提醒GPIO数量也足够驱动振动马达和按键。功耗方面运行模式大概几十毫安配合合理的休眠策略一块2000mAh的锂电池撑一整天问题不大。还有一个很现实的原因STM32的开发生态太成熟了。HAL库把底层寄存器操作封装得很友好标准外设库资料遍地都是网上能找到的参考代码和项目笔记数量远超其他平台。对于个人开发者或者小团队来说这意味着开发周期可以大幅缩短踩坑成本也低得多。1.2 系统到底要解决什么问题导盲语音系统的本质是一个环境感知加信息反馈的闭环。视障人士无法用视觉获取周围环境信息系统就要替代这部分功能。具体来说需要解决三个层面的问题第一层是障碍物检测。前方有没有东西、距离多远、是静止的还是移动的这些信息必须准确获取。超声波测距是目前性价比最高的方案HC-SR04模块几块钱一个测距范围2cm到400cm精度能满足日常出行需求。如果预算充足也可以加红外或者激光测距模块做补充但超声波对玻璃、镜面等特殊材质的检测效果反而更好因为它是靠声波反射而不是光反射。第二层是信息处理与决策。检测到障碍物之后系统要判断危险等级距离大于1.5米属于安全范围可以只做低频提示或者不提示0.5米到1.5米之间属于警告范围需要语音提醒“前方有障碍物”小于0.5米属于紧急范围除了语音还要触发振动马达做触觉反馈。这个分级逻辑需要根据实际使用场景反复调试阈值定得太敏感会频繁误报定得太迟钝又起不到保护作用。第三层是人机交互。视障人士操作设备主要靠按键和语音所以按键的手感、位置、反馈方式都要特别设计。按键不能太小太密最好有凸点或者不同形状方便触摸定位。语音播报要清晰、语速适中、音量可调紧急提示音和普通提示音要有明显区分。另外设备最好支持耳机输出在嘈杂环境中用耳机听语音提示比外放更清楚也不会打扰周围的人。1.3 适合谁来参考这个项目这个项目适合几类人正在学STM32想找个完整项目练手的嵌入式新手课程设计或者毕业设计需要做实物的大学生想了解辅助设备开发思路的产品经理以及有视障亲友想自己动手做个简易辅助工具的爱好者。对于新手来说这个项目的难度曲线比较友好。它涉及的知识点包括GPIO控制、定时器配置、UART通信、中断处理、PWM输出基本覆盖了STM32开发的常用外设但又不会深到需要跑RTOS或者做复杂算法。代码量大概在800到1200行之间认真啃一周左右能理解透。对于有经验的人来说这个框架可以很方便地扩展比如加蓝牙模块做手机端配置、加GPS做定位追踪、加温湿度传感器做环境监测等等。2. 硬件选型与电路设计要点2.1 主控芯片的取舍STM32F103C8T6是这个项目的首选也就是常说的“蓝板”或者“最小系统板”。价格便宜十几块钱就能买到引脚全部引出方便接线。它的资源对于这个项目来说刚好够用3个USART2个SPI2个I2C2个12位ADC4个16位定时器37个GPIO。超声波测距用TIM做输入捕获或者直接做微秒延时都行语音模块用USART通信按键用外部中断或者轮询振动马达用PWM控制强度。如果手头有STM32F401或者F411的板子也可以用性能更强浮点运算能力更好但价格会贵一些。对于这个项目来说F103的性能已经绰绰有余没必要为了“性能冗余”多花钱。唯一需要注意的是F103的Flash只有64KB如果语音模块的音频文件要存在主控里空间会比较紧张。不过常见的语音合成模块都是自带存储的主控只需要发送文本或者指令码所以这个问题实际上不存在。2.2 超声波测距模块的选择与布局HC-SR04是最常见的选择便宜、好用、资料多。它的工作原理很简单Trig引脚给一个10微秒的高电平脉冲模块自动发射8个40kHz的超声波脉冲然后Echo引脚会输出一个高电平高电平持续的时间就是声波往返的时间。距离等于高电平时间乘以声速再除以2。计算公式是距离(cm) 高电平时间(us) × 0.034 / 2。声速在常温下约340m/s换算成每微秒0.034厘米。除以2是因为声波走了一个来回。实际使用中要注意几个问题。首先是测量角度HC-SR04的探测角度大概15度意味着它只能检测正前方一个锥形区域内的障碍物。如果要覆盖更宽的范围需要装多个模块比如左中右各一个或者用一个舵机带动模块左右扫描。其次是盲区HC-SR04的最小测量距离是2cm太近的物体反而测不到。对于导盲场景来说2cm已经非常近了基本不影响使用。第三是干扰多个超声波模块同时工作时会互相干扰需要分时复用也就是轮流触发不能同时发波。模块布局上建议装在设备正前方偏下的位置大概在腰部高度。这个高度既能检测到地面的台阶、路沿也能检测到前方的行人、车辆、墙壁。如果装在太高或者太低的位置都会影响检测效果。2.3 语音模块的选型对比语音方案有三种常见选择各有优劣方案代表模块优点缺点适用场景语音合成SYN6288、XFS5152任意文本转语音灵活价格较高20-50元需要动态播报内容语音录制播放WT588D、DY-SV17F音质好便宜只能播放预录内容提示语固定蓝牙音频蓝牙模块手机可利用手机TTS依赖手机有延迟原型验证对于导盲系统来说提示语其实是相对固定的“前方有障碍物”、“请注意”、“电量不足”、“设备已启动”等等。用WT588D或者DY-SV17F这类录制播放模块就够了成本低音质也比合成模块自然。但如果想做得更灵活比如播报当前距离数值、播报时间、播报天气那就需要SYN6288这类合成模块。我个人的建议是第一版用录制播放模块快速验证核心功能第二版再考虑升级到合成模块。因为录制播放模块的控制逻辑简单得多只需要通过串口发送指令码或者用IO口触发对应语音段就行不需要处理文本编码、波特率匹配、合成延迟这些问题。2.4 电源管理与续航估算整机功耗主要来自三部分STM32主控、超声波模块、语音模块。STM32F103运行模式大概30-50mA超声波模块工作电流15mA左右语音模块待机几毫安、播放时100-200mA。如果加振动马达振动时电流可能到100mA以上。按平均工作电流80mA估算2000mAh的锂电池理论上能撑25小时。但实际使用中要考虑电池放电曲线、升压模块效率如果用3.7V升压到5V效率大概85%、以及各种损耗实际续航大概在15-18小时。这个成绩对于日常出行来说足够了晚上回家充电就行。电源设计上建议用TP4056做锂电池充电管理用MT3608或者类似的升压模块把3.7V升到5V给超声波和语音模块供电。STM32本身可以用3.3V直接从锂电池经过LDO稳压得到。记得加一个电源开关不用的时候彻底断电避免待机耗电。3. 软件架构与核心代码实现3.1 整体软件流程设计软件的主循环逻辑其实不复杂但要把实时性和低功耗平衡好。我的做法是用定时器中断驱动超声波测距主循环处理语音播报和按键扫描。这样测距的时序精度由硬件定时器保证不会因为主循环里其他任务的执行时间波动而受影响。具体流程是这样的TIM2配置为1MHz计数频率也就是每微秒计一个数。触发超声波后在Echo引脚上升沿启动定时器下降沿读取计数值这个值就是高电平持续时间。然后根据公式算出距离存入一个全局变量。主循环每隔100ms读一次这个距离值根据距离区间决定是否触发语音播报。语音播报不能太频繁否则会一直重复“前方有障碍物”反而让人烦躁。我的策略是同一个警告级别在3秒内只播报一次除非距离突然从安全范围跳到紧急范围这种情况要立即播报。另外紧急级别的播报要打断当前正在播放的普通提示确保危险信息第一时间传达。3.2 超声波测距的定时器配置用TIM2做输入捕获或者做微秒延时都可以我倾向于用输入捕获方式因为这样测距过程完全不占用CPU时间。配置步骤如下// TIM2初始化1MHz计数频率 void TIM2_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler 71; // 72MHz/72 1MHz TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 配置PA0为输入捕获上升沿触发 TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x0; TIM_ICInit(TIM2, TIM_ICInitStructure); TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); TIM_Cmd(TIM2, ENABLE); }中断服务函数里要做状态机切换第一次捕获到上升沿时记录计数值并切换为下降沿触发第二次捕获到下降沿时计算差值并切换回上升沿触发。volatile uint32_t capture_start 0; volatile uint32_t capture_end 0; volatile uint8_t capture_flag 0; volatile float distance_cm 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_CC1) ! RESET) { if (capture_flag 0) { capture_start TIM_GetCapture1(TIM2); TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Falling); capture_flag 1; } else { capture_end TIM_GetCapture1(TIM2); uint32_t pulse_width capture_end - capture_start; distance_cm pulse_width * 0.034 / 2.0; TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Rising); capture_flag 0; } TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); } }这里有个细节要注意如果距离太远或者没有障碍物Echo引脚可能一直不拉低导致捕获不到下降沿。所以需要加一个超时机制比如在触发超声波后启动一个定时器超过50ms还没有完成捕获就强制复位状态机避免卡死。3.3 语音模块的串口通信实现以DY-SV17F为例它支持串口控制波特率默认9600。发送的指令格式是固定的起始码0xAA、指令类型、数据长度、数据内容、校验和。比如播放第1段语音的指令是AA 07 02 00 01 B4其中0x07是播放指令0x02是数据长度0x0001是语音段编号0xB4是校验和。校验和的计算方法是从起始码之后开始累加取低8位。比如07020001 0x0A校验和就是0x0A不对实际DY-SV17F的校验和是0xAA - (指令长度数据)具体要看模块手册。不同厂家的协议不一样用之前一定要仔细读手册。void Voice_Play(uint16_t index) { uint8_t cmd[6]; cmd[0] 0xAA; cmd[1] 0x07; cmd[2] 0x02; cmd[3] (index 8) 0xFF; cmd[4] index 0xFF; cmd[5] 0xAA - (cmd[1] cmd[2] cmd[3] cmd[4]); for (int i 0; i 6; i) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, cmd[i]); } }如果用的是SYN6288这类合成模块发送的就是GBK编码的文本。这里有个坑STM32的工程默认用UTF-8编码但SYN6288只认GBK。所以要么在代码里做编码转换要么直接把工程文件保存为GBK编码。我试过用转换表的方式但太占空间了最后还是把整个工程的编码改成了GBK省事。3.4 按键交互与振动反馈按键用外部中断方式配置PA1为下降沿触发内部上拉。中断服务函数里做20ms的软件消抖确认按下后再执行对应功能。按键功能可以这样分配短按切换模式室内/室外长按3秒开关机双击播报当前电量。振动马达用PWM控制TIM3的通道1输出频率2kHz左右。不同警告级别对应不同的振动强度警告级别占空比50%紧急级别占空比100%。振动和语音要同步触发让用户同时获得听觉和触觉反馈。void Vibration_Set(uint8_t level) { uint16_t duty 0; switch (level) { case 0: duty 0; break; // 关闭 case 1: duty 500; break; // 警告50% case 2: duty 1000; break; // 紧急100% } TIM_SetCompare1(TIM3, duty); }4. 系统调试与常见问题排查4.1 超声波测距不准的排查思路超声波测距不准是最常见的问题表现有几种读数跳动大、固定偏差、偶尔出现极大值或极小值。排查要按顺序来先看电源。HC-SR04对电源噪声很敏感如果和电机、语音模块共用一路电源测距读数会明显跳动。解决办法是在模块的VCC和GND之间并一个100uF的电解电容加一个0.1uF的陶瓷电容做退耦。如果还不行就给超声波模块单独用一个LDO供电。再看时序。Trig引脚的高电平脉冲宽度要在10us以上太短了模块不响应太长了也没必要。用示波器或者逻辑分析仪抓一下Trig和Echo的波形确认时序正确。Echo的高电平时间应该在几百微秒到几十毫秒之间对应几厘米到几米的距离。然后看计算公式。声速受温度影响0度时是331m/s20度时是343m/s40度时是355m/s。如果对精度要求高可以加一个温度传感器做补偿。不过对于导盲场景来说几厘米的误差完全可以接受用常温声速就够了。最后看机械安装。超声波模块的发射和接收头不能有遮挡不能装在金属壳体内否则声波会被反射和吸收。模块前方最好留出至少1cm的空间不要紧贴着外壳开孔。4.2 语音模块不发声或声音异常的解决语音模块的问题通常出在通信和供电上。如果模块完全不发声先用串口助手直接给模块发指令确认模块本身是好的。如果串口助手能控制但STM32控制不了那就是代码问题。检查几个点波特率是否匹配常见的有9600和115200TX和RX是否接反STM32的TX接模块的RXSTM32的RX接模块的TX串口是否配置正确数据位8、停止位1、无校验。如果用的是HAL库还要注意HAL_UART_Transmit的超时参数设太短了可能发不完。如果声音断断续续或者有杂音大概率是电源问题。语音模块播放时电流波动很大如果电源内阻大或者走线太细电压会被拉低导致模块复位。解决办法是给语音模块单独加一个大电容470uF以上并且电源线尽量短粗。还有一个容易忽略的问题语音模块的功放输出不能直接接耳机。模块的喇叭输出是差分信号接耳机需要加隔直电容和衰减电路。如果要用耳机最好选带耳机输出的模块或者加一个PAM8403功放板。4.3 系统功耗优化与续航提升如果实测续航明显低于预期可以从几个方面优化降低主频。STM32F103默认72MHz对于这个项目来说完全过剩。降到24MHz甚至8MHz功耗能降一半以上而测距和语音控制完全不受影响。在SystemInit里改一下PLL配置就行。用好休眠模式。主循环里如果没有任务要处理可以调用__WFI()指令让CPU进入睡眠模式等中断唤醒。这样CPU大部分时间在睡觉功耗能降到10mA以下。注意休眠前要把不用的外设时钟关掉GPIO配置成模拟输入或者推挽输出低电平避免浮空引脚漏电。优化语音播报策略。语音模块播放时功耗最大减少不必要的播报能显著延长续航。比如安全距离内不播报警告级别3秒内不重复播报夜间模式降低音量等等。选低功耗的超声波模块。HC-SR04工作电流15mA如果换成MB1000或者US-100这类低功耗模块电流能降到5mA以下。不过这些模块价格会贵一些测距范围也小一些要根据实际需求权衡。4.4 常见问题速查表现象可能原因排查方法解决方案测距读数一直为0Echo引脚没接对用万用表测Echo电压检查接线确认PA0配置为输入测距读数跳动大电源噪声示波器看VCC纹波加退耦电容单独供电语音模块不发声串口通信失败串口助手直接测试检查波特率、TX/RX接线语音播放断断续续电源电流不足测播放时VCC电压加大电容换粗电源线按键不灵敏消抖时间太短示波器看按键波形消抖时间加到20-50ms系统频繁复位看门狗溢出检查看门狗配置主循环里及时喂狗续航时间短休眠策略不当测各模式电流降主频用WFI休眠振动马达不转PWM没输出示波器看TIM3通道检查PWM配置和占空比5. 功能扩展与进阶玩法5.1 加蓝牙模块做手机端配置用HC-05或者HC-06蓝牙模块通过UART和STM32通信可以实现手机端调整参数警告距离阈值、语音音量、振动强度、播报频率等等。手机端用微信小程序或者简单的蓝牙调试APP就行不需要自己写APP。蓝牙模块接在USART2上和语音模块的USART1分开。通信协议可以自定义比如用$DIST:100#设置警告距离为100cm用$VOL:8#设置音量为8级。STM32收到指令后解析并保存到Flash里掉电不丢失。这里有个坑HC-05的默认波特率是9600但有些模块是38400买回来先用串口助手确认一下。另外蓝牙模块的VCC一般是3.6V到6V可以直接接5V但它的TX输出是3.3V电平和STM32的RX可以直接连不需要电平转换。5.2 加GPS模块做定位与轨迹记录用ATGM336H或者NEO-6M这类GPS模块通过UART输出NMEA格式的定位数据。STM32解析出经纬度后可以存在SD卡里做轨迹记录也可以通过蓝牙发送到手机。对于视障人士来说轨迹记录的意义在于如果走失了家人可以通过手机查看最后的位置。GPS模块的功耗大概30-50mA会增加不少耗电。建议做成可插拔的模块需要的时候再插上。另外GPS在室内收不到信号第一次定位可能需要几分钟这些都要在语音提示里说清楚避免用户以为设备坏了。5.3 加温湿度传感器做环境感知DHT11或者SHT30温湿度传感器通过单总线或者I2C接口和STM32通信。对于视障人士来说知道当前温度和湿度有助于判断穿衣和是否带伞。播报可以做成按键触发比如双击按键播报“当前温度25度湿度60%”。DHT11便宜但精度一般SHT30贵一些但精度和稳定性好很多。如果只是做提示用途DHT11就够了。注意DHT11的读取间隔不能小于1秒太频繁了会读失败。5.4 用FreeRTOS做任务调度当功能越来越多的时候裸机的主循环会变得很臃肿各个任务的实时性也难以保证。这时候可以上FreeRTOS把超声波测距、语音播报、按键扫描、蓝牙通信分别做成独立的任务用队列和信号量做同步。FreeRTOS在STM32F103上跑完全没问题内核占用的Flash大概6-8KBRAM大概1-2KB。任务优先级这样分配超声波测距任务最高保证时序精度语音播报次之按键扫描再次蓝牙通信最低。任务间通信用消息队列比如测距任务把距离值发到队列里语音任务从队列里取数据决定播报内容。不过对于这个项目来说FreeRTOS不是必须的。裸机的中断加主循环架构已经够用了上RTOS反而增加了调试复杂度。我的建议是第一版用裸机功能稳定之后再考虑要不要上RTOS。6. 外壳设计与佩戴方式6.1 3D打印外壳的设计要点外壳设计要考虑几个因素超声波模块的开孔位置、语音模块的出声孔、按键的触感、电池的安装方式、以及整体的佩戴舒适度。超声波模块的开孔不能太小否则会遮挡声波。建议开孔直径比模块的探头直径大2mm以上孔深尽量短最好直接开在壳壁上。如果模块是嵌在壳体内的要在探头前方留出至少5mm的空腔避免声波在腔内反射造成干扰。语音模块的出声孔可以设计成栅格状孔径2-3mm间距3-4mm。孔的总面积要足够大否则声音会被闷住。如果用的是腔体喇叭还要考虑后腔的体积太小了低音出不来声音会发尖。按键建议用硅胶按键或者机械按键加橡胶帽手感清晰盲操作容易定位。按键周围可以做一圈凸起的圆环手指摸到圆环就知道按键在哪。6.2 佩戴方式的选择导盲设备常见的佩戴方式有几种挂脖式、腰夹式、腕带式、手持式。挂脖式最方便设备垂在胸前超声波模块朝前按键在侧面手自然下垂就能摸到。腰夹式适合夏天衣服薄的时候但超声波模块容易被衣服遮挡。腕带式适合轻量化的设备但手腕的活动会影响测距方向。手持式最灵活但占一只手长时间拿着也累。我个人的建议是挂脖式为主腰夹式为辅。挂脖的绳子用可调节长度的不同身高的人都能找到合适的位置。设备本体做轻一点控制在150克以内太重了挂着不舒服。6.3 防水防尘的简易处理户外使用难免遇到下雨或者扬尘外壳的防护等级至少要做到IP54。具体做法外壳的接缝处加一圈硅胶密封条按键用防水按键或者加防水膜充电口用硅胶塞堵住超声波模块的探头前方可以加一层防水透声膜比如Gore-Tex膜既能防水又不影响声波穿透。如果不想做复杂的防水设计至少要做到电路板喷三防漆电池用热缩管包好接插件用热熔胶固定。这样即使淋到小雨也不会立刻坏回家擦干就行。7. 实测数据与效果评估7.1 测距精度实测我在室内和室外分别做了测距对比测试用卷尺量实际距离和系统读数做对比。测试结果如下实际距离(cm)系统读数(cm)误差(cm)误差率2020.30.31.5%5049.6-0.40.8%100100.80.80.8%150149.2-0.80.5%200201.51.50.75%300298.0-2.00.67%从数据看误差基本在2cm以内误差率低于1.5%完全满足导盲需求。误差主要来自声速的温度变化和计时精度但影响很小。需要注意的是这个测试是在无障碍物的开阔环境下做的如果有多个反射面读数可能会有较大偏差。7.2 语音播报响应时间从检测到障碍物到语音开始播放整个链路的延迟包括超声波测距时间约10-30ms、主循环处理时间约5ms、串口发送指令时间约2ms、语音模块响应时间约50-100ms。总延迟大概在70-140ms之间人耳基本感觉不到延迟。紧急情况下从距离小于50cm到振动马达启动的延迟更短因为振动是GPIO直接控制不需要经过语音模块。实测大概20-30ms就能感觉到振动这个响应速度足够快了。7.3 实际使用体验反馈我找了两位视障朋友做了简单的试用测试反馈主要集中在几点语音提示音量够大但在嘈杂的马路边还是有点听不清建议加耳机输出振动反馈很有用特别是紧急情况下比语音更直接设备重量还可以接受但挂绳有点勒脖子建议加宽挂绳或者用海绵垫。还有一个反馈是警告距离阈值需要根据行走速度调整。走得快的时候1.5米的警告距离有点短反应时间不够走得慢的时候1.5米又有点远频繁提示会烦。所以后来我加了蓝牙配置功能让用户自己调阈值。8. 开源资料与复现指南8.1 代码结构与工程组织整个工程用Keil MDK或者STM32CubeIDE都可以打开。代码按功能模块分文件main.c是主循环和初始化ultrasonic.c/h是超声波驱动voice.c/h是语音模块驱动key.c/h是按键处理vibration.c/h是振动马达控制bluetooth.c/h是蓝牙通信可选。每个模块的接口都做了封装比如Ultrasonic_GetDistance()返回当前距离Voice_Play(index)播放指定语音段。这样主循环的逻辑就很清晰读距离、判断级别、触发语音和振动、扫描按键、处理蓝牙指令。8.2 物料清单与成本估算物料型号数量单价(元)小计(元)主控板STM32F103C8T6最小系统板11515超声波模块HC-SR04155语音模块DY-SV17F11212振动马达扁平振动马达133锂电池18650 2000mAh11515充电模块TP4056133升压模块MT3608133按键6x6mm轻触按键30.51.5外壳3D打印12020其他导线、电阻、电容等--10合计约87.5整体成本控制在100元以内对于个人DIY来说非常友好。如果批量做成本还能更低。8.3 复现步骤与调试建议复现这个项目建议按以下顺序来先点亮LED确认开发环境正常再调通串口打印调试信息然后单独测试超声波测距接着测试语音模块播放最后把所有模块整合到一起。每步都确认无误了再进行下一步不要一上来就把所有代码烧进去出了问题很难定位。调试工具方面串口助手是必须的用来打印调试信息和直接控制模块。逻辑分析仪或者示波器有条件的话最好有一个用来抓超声波和串口的波形。万用表用来测电压和通断。这些工具加起来不到200块钱但能省下大量调试时间。代码里我留了不少调试打印用#define DEBUG控制开关。调试的时候打开正式用的时候关掉避免串口输出影响实时性。另外每个模块都有独立的测试函数可以在main.c里单独调用方便定位问题。9. 个人实操心得与避坑建议做这个项目前后花了大概三周时间踩了不少坑也积累了一些文档里不会写的经验。第一个坑是超声波模块的电源。一开始我把超声波和语音模块接在同一路5V上测距读数跳得厉害从20cm跳到200cm都有。后来在超声波模块的VCC和GND之间并了一个100uF的电解电容读数立刻就稳了。这个电容的作用是给超声波模块提供瞬态电流因为超声波发射的瞬间电流会突然增大如果电源响应不过来电压就会被拉低导致测距出错。第二个坑是语音模块的串口电平。DY-SV17F的串口是3.3V电平但有些批次的模块TX输出是5V直接接STM32的RX可能会损坏引脚。我烧了一个USART1才发现的后来加了一个电平转换电路用两个电阻分压就没事了。所以买模块的时候一定要确认电平不确定的就加转换电路几毛钱的事别省。第三个坑是按键的消抖。一开始用10ms消抖发现偶尔会误触发。后来用示波器看按键波形发现机械按键的抖动时间可能长达20ms特别是便宜的轻触按键。改成30ms消抖后就再没出现过误触发。另外按键最好加一个0.1uF的电容做硬件消抖软件消抖加硬件消抖双保险。第四个坑是电池电压检测。STM32的ADC参考电压是3.3V但锂电池的电压范围是3.0V到4.2V直接接ADC引脚会超过量程。我用两个100k电阻分压把电压降到1.5V到2.1V之间这样ADC就能正常读取了。分压电阻要用精度1%的否则电量显示会不准。第五个坑是外壳的超声波开孔。第一版外壳的超声波开孔太小只有8mm结果测距距离明显缩短只能测到1米左右。后来把开孔扩大到12mm测距距离就恢复正常了。原因是开孔太小会限制声波的发射角度声波被孔壁遮挡了一部分。所以开孔直径至少要比探头直径大2mm孔深尽量短。最后分享一个调试小技巧如果怀疑某个模块有问题先别急着改代码用串口助手或者万用表直接测模块的输入输出。比如超声波模块给Trig一个10us的高电平然后用示波器看Echo有没有输出。如果模块本身没问题再查代码。这样能快速定位问题是出在硬件还是软件上省得在代码里瞎找。