☰
ESP32+离线语音,DIY一台会听指令的智能移动垃圾桶
2026/10/6 16:35:21 网站建设 项目流程

周末下午,我坐在沙发上吃薯片,包装袋很快堆满了手边的小纸碗。垃圾桶就在两米外的墙角,可我就是不想起身。这个瞬间我意识到,一台会听指令、自己跑到你面前的垃圾桶,大概能解决不少“懒人”的真实痛点。于是我从开源社区翻了一圈现成方案,再结合自己的嵌入式底子,决定动手做一台开源DIY智能移动垃圾桶:它听得懂“过来”“回去”“开盖”,能自己绕过桌腿,还能在没电前乖乖回到墙角。这篇文章就记录下这台机器的完整设计思路、硬件选型、核心代码和调试过程中遇到的各种坑,希望能给同样想折腾的朋友一个可以直接下手的参考。

1. 先从需求说起:这台垃圾桶到底要“会”什么

1.1 “会听会走”的功能边界怎么定

做项目最忌讳一上来就想着“什么都要有”。我最初也动过加摄像头、加激光雷达、连手机APP、搞自动垃圾分类的念头,但冷静下来一算,时间、成本、调试难度全都不止翻一倍。对于一台定位在室内的移动垃圾桶,真正核心的需求其实就三条:

  • 被动值班:平时待在墙角待机,不耗电、不吵人,像个正常垃圾桶。
  • 语音召唤:用户喊“小桶过来”,它能识别指令并朝用户方向移动;喊“回墙角”,它能自动返回待机点。
  • 安全行走:移动过程中能避开椅子腿、桌腿、拖鞋这类常见障碍物,遇到台阶边缘不往前冲,快没电时会告警。

至于自动开盖、自动打包垃圾袋、识别什么垃圾能扔什么不能扔,我全归到二期或者三期再折腾。先把“听”和“走”这两个主功能做扎实,比盲目堆功能靠谱得多。这也算我给自己画的边界线:一开始只允许自己动MCU、电机驱动和几个传感器,不碰视觉、不碰导航,避免项目烂尾。

定了功能边界之后,整个系统就清晰了:一个负责语音识别和交互的模块,一套负责底层运动的差速底盘,一颗负责统筹调度和避障决策的主控,再加一块把7.4V电池电压合理分给各个模块的电源板。每一块都能独立测试,最后再拼起来联调,排查问题的时候不会像一团乱麻。

1.2 技术选型:ESP32还是STM32,语音方案怎么选

主控选型是第一个要做的决定。我在STM32F103C8T6(蓝丸)和ESP32-WROOM-32之间纠结了一阵,最后选了ESP32,核心原因是“语音”这条链路。

ESP32的优势在联网和双核处理。虽然这台垃圾桶不依赖云,但ESP32的算力做离线语音指令词解码、传感器数据融合和电机控制同时跑,非常从容。更重要的一点,ESP32自带WiFi/蓝牙,后续如果我想加手机调试、Web控制面板、甚至用蓝牙遥控,简直零成本。STM32F103当然也能做,很多开源四轴和智能小车都用它,但你要额外接一个语音识别芯片(比如SU-03T),还要处理编解码和模型文件,开发效率会低不少。下面是当时对比的两个方案:

对比项ESP32-WROOM-32STM32F103C8T6
离线语音识别支持乐鑫官方ESP-SR框架,可跑命令词识别需要外挂语音模块或自己移植算法
无线扩展原生WiFi + 蓝牙需要外接ESP8266或BLE模块
电机控制外设LEDC PWM通道足够定时器PWM充足
开发门槛Arduino框架上手快,也有ESP-IDFKeil + HAL库,偏传统嵌入式
成本约15-25元约8-15元
典型失败案例PWM引脚复用冲突、GPIO电平注意语音链路复杂,集成成本高

语音识别方案我当时列了三种:SU-03T离线语音模块、ESP32原生的ESP-SR离线识别、在线云ASR(百度/讯飞那种)。在线方案直接淘汰了——垃圾桶要的是一个“随时喊得动”的设备,依赖WiFi和云服务,网络一波动就成了哑巴,这不行。SU-03T模块最省事,模块内置了麦克风、语音识别固件和串口输出,上位机里配置好唤醒词和命令词,烧写进去就能用。ESP-SR的优点是少一块硬件,但ESP32原版跑ESP-SR中文命令词支持不如ESP32-S3好,实测识别距离也短一些,所以我最后选了“主控ESP32 + 语音模块SU-03T”这个组合,稳、准、开发量小。

电机驱动选的是TB6612FNG,不是很多人习惯用的L298N。L298N太老、压降大、发热严重,TB6612FNG体积小、管教少,最大连续输出1.2A/通道,驱动N20减速电机这种小功耗电机绰绰有余。N20电机我选的是带霍尔编码器的版本,减速比1:30,额定电压6V,空载转速约300RPM左右,配合橡胶轮,速度体感不会太疯,也不会慢得像蜗牛。

2. 硬件清单与供电设计:别让垃圾桶半路趴窝

2.1 核心部件清单与选型理由

整个项目的BOM其实不复杂,大部分模块都能在淘宝上十块钱左右买到。这里给一份我当时采购的完整清单,价格按单件估算,方便你按预算取舍:

部件型号/参数大致价格作用
主控ESP32-WROOM-32开发板20-30元负责运动控制、逻辑调度、避障决策
语音模块SU-03T 离线语音识别模块15-25元识别唤醒词和命令词,通过串口输出结果
电机 ×2N20微型减速电机带编码器,减速比1:3015-25元/个驱动左右轮
电机驱动TB6612FNG模块8-15元给电机提供正反转PWM驱动
超声波模块 ×3HC-SR043-6元/个检测前方、左前、右前障碍物距离
TOF测距模块VL53L0X15-20元检测低矮台阶和桌面边缘
电池18650电池 ×2 + 双节电池盒30-50元7.4V供电
降压模块MP1584 DC-DC降压模块 ×25-8元/个分别输出5V和3.3V
舵机SG90 9g舵机5-8元控制垃圾桶盖开合
数据线/排针若干10元供电和信号连接

这里多说一句为什么选HC-SR04而不是只用VL53L0X。HC-SR04在中近距离表现稳定,指向性虽然不算特别窄,但对桌腿、墙壁这类大目标很够用;缺点是对斜面的反射容易丢数据。VL53L0X是激光测距,适合检测前方低矮台阶或者地面突然变深的情况,但成本更高,照射面积小。我做的是三层感知:前方用三个HC-SR04覆盖左右45°和正前方,底盘边缘往下装一个VL53L0X专门防跌落,这样互补各自的盲区。

2.2 供电架构:为什么电池和降压要分开设计

供电是整个项目里最容易埋雷的地方。我见过不少人图省事,拿一个AMS1117-3.3把7.4V直接降到3.3V给主控供电,结果板子一通电就发烫,运行几分钟后自动重启。原因是AMS1117是线性稳压器,输入输出压差大时,多余的功率全部变成热量散发,电流稍微大一点就扛不住。

我的方案是7.4V电池母线走两级:第一级用MP1584 DC-DC把7.4V降到5V,给超声波模块、SU-03T语音模块和舵机供电;第二级再用一个MP1584把5V降到3.3V,给ESP32和编码器供电。DC-DC的转换效率高,发热小,纹波在电机的干扰下也能保持在可接受范围内。当然,3.3V这路也可以换成AMS1117,但输入电压最好控制在5V以内,不然热损耗仍然很难看。

电机驱动的VM脚直接接7.4V电池母线,逻辑电平脚VCC接5V,STBY接3.3V或者5V(看手册推荐)。有一点必须注意:所有模块的地线要共地,电机电源地、主控地、传感器地必须连成一个完整的参考点,否则串口通信和PWM信号会出现莫名其妙的跳变。我还在电池母线两端并联了一只1000uF电解电容和一只0.1uF瓷片电容,用来吸收电机启动瞬间的大电流冲击,实测对重启复位问题改善非常明显。

3. 底盘与运动学:决定垃圾桶“走得稳”的底层逻辑

3.1 两轮差速底盘结构与改装

底盘结构我选了最经典的两轮差速加万向轮方案,没有上四驱。原因很简单:垃圾桶不需要越野,室内地面是瓷砖或木地板,两个驱动轮加一个万向轮足够稳定,而且差速转向的控制模型比四驱简单太多。四驱要解决四个电机的速度同步问题,不然转弯的时候反而容易互相较劲。

具体布局上,两个驱动轮装在底盘中轴线靠前一点的位置,万向轮装在尾部,形成一个稳定的三角形支撑。左右轮间距我实测取220mm,底盘宽度约240mm,这样在原地旋转时不会碰到周围障碍物核心区域,而且转向半径的计算也方便。垃圾桶本体的固定也很关键:我用一块亚克力板做底盘,桶身底部用双面胶和扎带双重固定,重心尽量压低,电池和主控板都放在底盘最底层。

有个实际教训得说:垃圾桶平时是被动放置的,如果重心偏高,在承载垃圾时一旦急转弯,离心力很容易把桶掀翻。所以我把重的东西全压在底盘底部,桶身内壁的固定点要选靠近桶底1/3的位置,不要只固定桶口。这一点看起来不起眼,但直接决定了机器人是“稳稳走”还是“三步一翻”。

3.2 速度与转向控制的数学模型

两轮差速底盘的运动学模型其实高中物理就够用。设左右轮线速度分别为 vL 和 vR,轮距为 L(我这里是0.22m),则机器人的整体线速度和角速度分别为:

  • v = (vL + vR) / 2
  • ω = (vR - vL) / L

当 vL = vR 时,两个轮子速度相等,机器人直线前进;当 vL = -vR 时,机器人原地旋转;当 vL 和 vR 不相等时,机器人走弧线,弧线半径 R = L * (vL + vR) / (2 * (vR - vL))。

实现里最大的难点不是公式本身,而是怎么让实际速度尽量接近设定的 vL 和 vR。N20电机带霍尔编码器,左右轮各有一个编码器输出,可以拿来做速度闭环。比如我用外部中断对编码器脉冲计数,每100ms结算一次,得到当前轮速(单位:RPM或者m/s),然后和目标速度做PD控制,调整PWM占空比。

PD控制器的输入是速度误差 e = targetSpeed - currentSpeed,输出是PWM占空比修正量。参数整定时从kp=1.0起步,先把比例调上去,如果看到轮子抖或者有金属摩擦声,就说明PWM修正量太大了,需要减小kp或者加一点微分项kd来抑制振荡。这一步虽然枯燥,但值得耐心试:左右轮速度不一致时,直线行走会明显偏斜,这是很多新手做小车时“走着走着就拐弯”的根本原因。

4. “听”的实现:语音识别链路与指令处理

4.1 离线语音模块的选型与接入

SU-03T模块的使用体验相当“友好”,它内置了麦克风、放大器、离线识别引擎和语音提示功能,你在PC端的上位机软件里创建一个产品,上传唤醒词和命令词对应的条目,然后生成固件烧录到模块里。它的典型工作流程是:先喊唤醒词(我设置的是“小桶小桶”),唤醒后3到5秒内识别命令词,识别结果通过UART输出,同时模块自带的喇叭会播报自定义的提示音。

接线非常简单:SU-03T的VCC接5V,GND接主控GND,TXD接ESP32的RX,RXD接ESP32的TX。有一点容易踩坑:SU-03T的串口电平虽然是3.3V,但部分模块的TXD在启动瞬间会有短暂的电平跳动,如果你接的ESP32开发板RX引脚没有外部下拉,偶尔会收到一个误码字符。解决办法是在初始化时清空串口缓存,或者在主控串口配置里加一段延时再开始监听。

4.2 指令集设计与串口通信协议

语音模块能识别多少条命令词不是重点,重点是命令词和运动状态要一一对应。我设计的指令集分成三类:

指令类型唤醒后说主控行为
移动指令“过来”进入前进状态,执行避障
移动指令“回墙角”转向180度博文方向并返回
移动指令“停止”原地停车
运动控制“左转”“右转”原地左转/右转,微调方向
桶盖控制“开盖”“关盖”舵机打开/关闭垃圾桶盖
系统查询“你在哪”“嘟一声”播报提示音或短闪灯

SU-03T的上位机里设置命令词后,识别结果默认以“字符串”形式从串口输出,例如“{“cmd”:”过来”,“res”:”0”}”这类JSON格式,也可能根据固件配置输出纯字符串。我在ESP32端用字符串匹配处理,解析到“过来”就切换状态,解析到“左转”就往左原地转500ms,简单直接。如果你想更结构化,可以在模块配置里把输出格式改成固定帧头+命令ID的协议,比如“AA 01 BB”,解析起来更可靠。

4.3 代码实现:从识别结果到运动指令

ESP32端我用的是Arduino框架,主循环里同时处理串口语音数据和超声波测距,状态判断放在一个简单的if-else链里。核心逻辑大概是这样的:

#define MOTOR_LEFT_PWM 13 #define MOTOR_RIGHT_PWM 14 String voiceCmd = ""; void setup() { Serial1.begin(9600); // 接SU-03T的串口 ledcSetup(0, 10000, 8); // PWM频率10kHz,分辨率8bit ledcAttachPin(MOTOR_LEFT_PWM, 0); ledcSetup(1, 10000, 8); ledcAttachPin(MOTOR_RIGHT_PWM, 1); } void loop() { // 读取串口语音识别结果 if (Serial1.available()) { voiceCmd = Serial1.readStringUntil('\n'); voiceCmd.trim(); Serial.println("Voice CMD: " + voiceCmd); } // 根据指令或状态控制电机 if (voiceCmd == "过来") { moveForward(150); // 前进,PWM占空比150/255 state = MOVING; } else if (voiceCmd == "停止") { stopMoving(); state = IDLE; } else if (voiceCmd == "开盖") { openLid(); } else if (voiceCmd == "关盖") { closeLid(); } // 在全速移动时检测障碍物 if (state == MOVING) { float frontDist = readUltrasonic(FRONT_TRIG, FRONT_ECHO); if (frontDist < 15.0f) { avoidObstacle(); } } delay(50); // 每50ms循环一次 }

这段代码只展示了最基础的指令响应。实际工程里,我还会加入“防抖”机制:同一个指令如果在短时间内被重复识别,只执行一次,防止模块误识别连续输出导致机器人抽搐。另外,不同指令之间最好留一个500ms的“冷却窗口”,因为人说话时可能有“嗯”“啊”之类的尾音被识别成杂音,冷却窗口可以过滤掉大部分误动作。

5. “走”的实现:运动控制状态机与避障策略

5.1 速度闭环与PWM控制

让垃圾桶“走得稳”,光靠直接把PWM占空比设成固定值是不够的。不同电压下、不同地面摩擦下,同样的占空比转速并不相同。我用编码器测每个轮子的实时速度,然后做一个比例-微分控制:

volatile int leftCount = 0; volatile int rightCount = 0; // 编码器中断 void IRAM_ATTR leftEncoderISR() { leftCount++; } void IRAM_ATTR rightEncoderISR() { rightCount++; } // 每100ms调用一次,计算转速并执行PD void updateSpeedControl() { static int lastLeftCount = 0; int leftDelta = leftCount - lastLeftCount; lastLeftCount = leftCount; int rpm = leftDelta * 600 / (encoderPPR * 100 / 1000); // 粗略换算 int targetRpm = 200; int error = targetRpm - rpm; static int lastError = 0; int output = kp * error + kd * (error - lastError); lastError = error; ledcWrite(0, constrain(150 + output, 0, 255)); }

实际调参时,我把左右轮的目标RPM设为一致,然后打印两轮的实际RPM,看偏差是否稳定在5%以内。如果左轮快右轮慢,就单独给右轮加一个补偿偏置。这个小补偿值是根据恒流PWM下实测的,不同地面要微调,所以我会把补偿参数放到全局变量里,方便通过手机串口实时调。

5.2 状态机设计:待机/前来/返回/避障

整个机器人的行为用状态机管理能省掉很多逻辑混乱。我定义了四个主状态:IDLE、COME_HERE、GO_BACK、AVOIDING。

IDLE状态下,所有电机停止,只有语音模块和主控在低功耗待机;语音一旦识别到“过来”,进入COME_HERE;识别到“回墙角”,进入GO_BACK。在COME_HERE和GO_BACK状态下,如果超声波检测到前方障碍物距离小于15cm,就进入AVOIDING。

AVOIDING状态是纯自动的,不需要用户参与。它会根据三个HC-SR04的读数,决策是左转还是右转。如果只是前方近处有障碍、两侧空闲,就选择转向方向,转向完成后重新回到原来的移动状态。如果左、右两侧同时有障碍,那很可能是卡在一个窄缝里或者被围困了,就原地旋转180度,尝试反向突围。

状态机的完整代码骨架在4.3节里其实已经露出了一些,这里再补充一个关键点:状态切换时一定要驱动电机的状态彻底落定。比如从AVOIDING切回COME_HERE,如果避障时带着一个转向余量,直接继续前进很容易再撞上同一障碍物,所以我会先恢复直行0.3秒,让机器人摆脱当前转向趋势,再开始前进。

5.3 避障逻辑:超声波测距与绕行决策

超声波避障的原理不复杂:HC-SR04发射超声波,测量回波时间乘以声速除以2得到距离。Adafruit官网都给了例程,唯一要注意的是测距频率:HC-SR04每次测量需要大约30ms,三个传感器轮流测就是90ms,加上状态机判断,主循环实际频率大概10Hz左右,这在室内低速场景下完全够用。

传感器布局上,正前方一个,左右各45度角一个。这样开发决策时就能这样判断:

左前距离前方距离右前距离决策
>20cm>20cm>20cm继续直行
>15cm<15cm>15cm向空侧偏转
<15cm<15cm>15cm右转
>15cm<15cm<15cm左转
<15cm<15cm<15cm停车后原地掉头

这套决策表虽然粗糙,但我在客厅实测效果还不错:桌腿能绕过去,垃圾桶底座也能绕过去,就是遇到那种宽度极窄的椅子缝时,它可能转半天,这种场景我建议直接语音喊一声“停止”,没必要让它死磕。毕竟它只是一台垃圾桶,不是扫地机器人。

6. 实测调试记录:那些文档里不会写的坑

6.1 电压跌落导致的主控复位

第一次整机联调时,我发现只要电机一启动,ESP32就会像触电一样重启,串口打印还能看到一堆乱码。排查到根因是瞬间电流过大:N20电机启动瞬间电流接近800mA,两轮同时启动接近1.6A,电池直接输出电压瞬间被拉到6.5V以下,主控供电跟着抖。解法就是前面提到的电源架构分级:给逻辑电路用独立的DC-DC降压,并在电池母线上加大电容,同时把电机启动改成“软启动”——PWM占空比从0逐步爬升到目标值,而不是一下拉满。软启动的效果非常明显,垃圾桶起步不再有猛窜的感觉,同时电压跌落几乎消失。

6.2 语音误触发与电机噪声

调试中我遇到过一种“灵异现象”:垃圾桶在音乐放大声时,偶尔会自己执行“过来”的指令。原因很简单,SU-03T的麦克风是开放的,高音量音乐里的某段频响特征和唤醒词/命令词撞上了。解决办法是在SU-03T的上位机里开启“识别阈值”调节,把阈值调高一点;同时尽量让麦克风方向朝上,别对着喇叭或者电机方向。电机PWM频率如果设在1kHz附近,电机的高频啸叫也会干扰拾音,我把PWM频率调到20kHz,既减弱了人耳可闻的电机噪声,也减少了对语音采样的影响。

6.3 避障盲区:单传感器布局的代价

初期我图省事,只在正前方装了一个HC-SR04,结果垃圾桶老老实实撞上了斜45度角的墙。原因不是传感器坏了,而是超声波束角有限,正前方探头根本看不见侧面的障碍。后来我改成三传感器布局,把左右两个朝向斜前方45度,才算解决了这个问题。这里也提醒各位:传感器的覆盖范围一定要预留“设计余量”,室内墙角、桌腿都可能在机器人的斜前方,只盯着正前方就等于半个瞎子。

6.4 万向轮卡死与卫生死角

万向轮到现在我还是没找到完全满意的方案,便宜的小万向轮在地毯上非常容易卡住纤维,转弯时会被异物缠住,导致机器人跑偏。一种常见修法是换直径更大、支架更顺滑的万向轮,或者在轮子外部加一个防缠绕罩盖。另外,垃圾桶毕竟是装垃圾的,桶身底部和电子舱之间要留出隔离层。我用了一片亚克力板把电子舱完全封死,只留出传感器和喇叭的开孔,不然扫地时扬起的灰尘和液体撒漏,对电路板是致命的。

7. 扩展方向与个人经验

7.1 还能往哪些方向折腾

这台垃圾桶做成以后,我把硬件框图、接线方式和关键代码整理发布到了自己的开源仓库,目的就是让更多人能做出来并且改出属于自己的版本。如果按优先级来说,我觉得接下来最值得折腾的几个方向是:

  • 加入麦克风阵列:单麦克风没有方向定位,垃圾桶接到“过来”指令只能盲目直行。用ReSpeaker 2-Mic或者4-Mic阵列可以做声源定位,也能在一定程度上判断声音来自左还是右,这个对“被召唤”场景提升非常明显。
  • 加视觉识别:OpenMV、K210或者直接用ESP32-S3接摄像头模组,识别人的位置并往人方向移动,甚至可以对垃圾做简易分类(比如只看颜色)。
  • 全屋定位:在室内布置UWB锚点,或者用编码器里程计做简单的航迹推算,至少让垃圾桶知道自己大概在哪、墙角在哪。这一块工作量不小,但做完整个系统就有“回充”“归位”能力了。

7.2 做完这台机器,我的一些经验体会

整套做下来,我最大的体会是:DIY项目真正的难点不是把模块拼在一起跑通,而是让它跑得稳、跑得持久。你得为电压跌落想办法、为轮子转速不一致写补偿、为误识别调阈值,这些细节没有高深理论,但每一个都能让你多熬一个晚上。如果你也准备动手,我的建议很简单:先按这篇文章的清单把基础版本做出来,哪怕外观丑、走线乱,先把“听”和“走”的闭环打通,后面的优化都是在已有的系统上做减法和加法。别一上来想着什么都要,项目死在“过度设计”上的案例,我见得太多了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询