公司动态

从零构建自动售货机:Arduino控制、传感器集成与机械设计全解析

📅 2026/8/19 3:42:18
从零构建自动售货机:Arduino控制、传感器集成与机械设计全解析
1. 从零到一为什么我要自己造一台自动售货机几年前我在一个创客空间里第一次看到有人用乐高积木和Arduino拼凑出一台能卖糖果的简易机器当时就觉得这事儿太酷了。它不像商场里那种庞然大物更像是一个会思考的玩具投币、选择、出货一套流程下来充满了机械与电子的浪漫。后来我陆续接触了一些开源硬件项目发现DIY自动售货机的门槛其实比想象中低但乐趣和收获却远超预期。这不仅仅是一个“玩具”它本质上是一个集机械设计、电子控制、软件编程和用户体验于一体的微型系统工程。自己动手造一台自动售货机到底能解决什么问题首先它是最佳的学习平台。你将被迫去理解步进电机如何精确控制旋转角度舵机如何完成推拉动作红外传感器如何检测硬币通过以及微控制器比如Arduino或树莓派如何作为大脑协调这一切。其次它具有极强的可定制性。你可以决定它卖什么——可以是零食、饮料、文具甚至是你自己做的3D打印小模型你可以设计它的交互方式——除了投币还能扫码支付、刷卡甚至人脸识别你还可以赋予它个性——一个复古的木制外壳或者一个充满赛博朋克感的亚克力骨架。最后对于小型工作室、社团活动或者家庭场景一台成本可控、功能定制的自动售货机能带来实实在在的便利和趣味。这篇文章就是把我从构思、选材、组装到编程调试的完整过程记录下来。无论你是电子爱好者、编程新手还是单纯想给生活增添点科技乐趣的动手达人都能从这里找到一条清晰的路径。我们不追求工业级的稳定但追求每一步的清晰可理解以及最终成品能可靠运行的成就感。你会发现那些看似复杂的自动化设备其核心原理往往简洁而优美。2. 核心架构拆解一台售货机由哪些部分组成在开始购买任何零件之前我们必须先在大脑里把整台机器拆解成几个功能模块。这能帮助我们在后续采购和组装时目标明确避免遗漏。一台基础的DIY自动售货机通常可以划分为四大系统控制系统、执行机构、感知系统与供电系统。2.1 大脑控制系统的选型与逻辑控制系统是整个机器的“大脑”负责接收指令如按钮按下、处理信号如计算金额、并驱动执行机构如电机转动完成出货。对于DIY项目主流选择有两个Arduino和树莓派Raspberry Pi。Arduino Uno/Nano是我的首选尤其对于初学者。它是一个微控制器擅长处理简单的、确定性的输入输出控制。它的编程环境简单社区资源极其丰富几乎所有传感器和驱动模块都有现成的库。在售货机场景中它的工作流非常直接循环检测按钮和传感器状态 - 根据状态改变电机和舵机控制引脚的电平 - 完成动作。其逻辑是“实时”和“顺序”的非常适合我们这种机械动作明确的项目。成本也低一块板子几十元。树莓派则是一台微型电脑运行完整的Linux操作系统。它的优势在于强大的计算能力和网络功能。如果你希望你的售货机拥有一个炫酷的触摸屏界面、支持网络远程管理库存、或者集成二维码扫码支付等复杂功能树莓派是更好的选择。但随之而来的是更高的复杂度你需要处理操作系统、进程管理、网络通信等。对于第一台DIY售货机我强烈建议从Arduino开始先让核心的“投币-出货”流程跑通再考虑升级。控制逻辑的核心是一个“状态机”。简单来说机器在任何时刻都处于某种特定状态比如“待机状态”、“投币计数状态”、“选择商品状态”、“出货状态”、“缺货状态”。外部事件如投入硬币、按下按钮会触发状态转移。用Arduino实现其实就是一堆if...else或者switch...case语句清晰易懂。2.2 筋骨执行机构的选择与驱动执行机构是“筋骨”负责把控制系统的电信号转化为物理动作。在售货机里最主要的动作就是“推送”或“旋转”商品使其掉落。1. 舵机Servo这是最简单直接的出货方案。你可以将舵机安装在商品货道后方舵盘上连接一个推杆。当接收到出货指令时舵机旋转一定角度如90度推杆向前推出将最前面的一个商品推落。它的优点是控制简单只需指定角度、扭矩大、价格便宜。缺点是行程固定且通常一次只能推动一个货道。适合货道不多、商品较轻如小包装零食、胶囊玩具的场景。2. 步进电机Stepper Motor这是更专业、更灵活的选择。步进电机可以精确控制旋转的圈数和角度。一种常见的应用是“螺旋货道”将一根塑料螺旋杆穿过堆叠的商品如瓶装饮料步进电机带动螺旋杆旋转每转一圈或一个固定角度就能将最底部的一个商品“拧”出来。这种方式出货稳定适合圆柱形商品。另一种是配合丝杆滑台实现直线推送适合盒装商品。步进电机需要专门的驱动板如A4988、DRV8825来控制接线和编程比舵机稍复杂但可控性极高。3. 电磁铁Solenoid这是一种“爆发式”执行器。通电时产生磁力快速吸合或弹出实现瞬间的敲击或释放动作。可以用于设计一种“挡板式”货道平时电磁铁通电挡住商品出货时断电挡板弹开商品依靠重力落下。它的动作快但冲击力大不适合易碎商品且需要较大的瞬时电流。对于我的第一台机器我选择了舵机方案。因为它最简单最容易验证整个控制流程。我为每一个货道配备了一个9g微型舵机成本可控驱动也方便Arduino可以直接驱动一两个多了需要外接电源。2.3 感官感知系统的搭建感知系统让机器有了“感官”知道发生了什么。1. 投币识别这是商业售货机的核心也是DIY的乐趣所在。有几种实现方式红外对管传感器最经济实用的方案。在硬币滑道上安装一对红外发射和接收管。硬币滚过时会遮挡红外线接收管会产生一个脉冲信号。通过测量脉冲的宽度即遮挡时间可以粗略区分不同直径的硬币。你需要先收集你所用硬币如1元、5角通过时产生的脉冲时间在代码中设置一个阈值范围来进行判断。这种方法成本极低模块几元钱但容易受到灰尘、硬币污渍以及滚动速度的影响需要仔细调试。硬币识别器模块这是“作弊”方案直接购买成品的投币器模块。它内部通常有多个红外传感器或电磁传感器能比较准确地识别常见硬币的真伪和面额并通过串口或脉冲输出信号给Arduino。价格从几十到上百元不等优点是省事、识别率高。模拟扫码支付为了更现代化可以引入一个ESP8266或ESP32WiFi模块可以单独使用也可以选用集成了这类芯片的Arduino开发板连接一个二维码生成服务。用户扫码后你的服务器甚至可以是简单的云函数收到支付成功通知再通过网络发送一个指令给售货机触发出货。这属于进阶功能涉及到网络编程和云端交互。2. 商品检测我们需要知道货道里还有没有货。最简单的方法是在货道底部安装一个红外避障传感器或微动开关。当有商品压在传感器上时状态为“有货”商品掉落后状态变为“无货”。Arduino可以实时读取这个状态并在对应货道的按钮灯熄灭或屏幕上显示“缺货”。3. 按钮输入选择商品需要输入设备。可以直接用轻触开关每个货道对应一个。也可以使用矩阵键盘来节省IO口或者用触摸传感器来提升科技感。对于有屏幕的版本甚至可以直接用屏幕上的虚拟按钮。2.4 血脉供电系统的设计与安全供电系统是“血脉”安全稳定是第一要务。DIY电子项目最大的安全隐患往往来自电源。我们的系统通常需要两种电压5V用于给Arduino、传感器、按钮灯等逻辑电路供电。6V或更高用于直接驱动舵机、步进电机等执行机构。特别是多个舵机同时动作时电流需求可能超过1A。绝对禁止将电机这类感性负载直接连接到Arduino的5V引脚上电机启动和停止时产生的反向电动势和电流冲击极易损坏Arduino脆弱的稳压芯片。正确的做法是使用独立的电源供电准备一个5V/2A以上的手机充电器或开关电源给Arduino和传感器供电。准备另一个6V/2A以上的电源或可调压的直流电源专门用于驱动舵机。确保两个电源的“地”GND连接在一起使它们有共同的参考零电位。使用一个电容如1000μF 16V并联在电机电源两端可以吸收电压波动让电机运行更平稳。如果使用步进电机驱动板务必仔细阅读数据手册正确设置驱动板的电流限制避免电机过热。一个可靠的电源方案是一个12V/3A的直流电源适配器作为总输入接一个DC-DC降压模块如LM2596输出稳定的5V给控制部分同时12V直接供给步进电机驱动板。对于舵机如果支持6V输入可以用另一个降压模块输出6V。3. 从图纸到实物机械结构与货道设计详解有了电路方案我们需要一个物理载体来容纳所有零件和商品。机械结构的设计直接决定了机器的可靠性、容量和外观。3.1 货道设计三种主流方案对比货道是商品存放和排出的通道其设计是核心。1. 弹簧螺旋货道这是饮料售货机的经典结构。商品罐装/瓶装饮料堆叠在由两侧弹簧构成的轨道上最前端的商品被一个挡板挡住。出货时挡板打开后面的商品在弹簧的推力下前移最前面的商品掉落。DIY实现挡板可以用舵机或电磁铁控制。这种货道容量大出货可靠但加工精度要求较高弹簧的弹力需要调整适中。2. 履带式货道货道底部是一条由步进电机驱动的履带。商品放在履带上。出货时履带转动将商品向前输送直至掉落。这种方案通用性强适合各种形状的盒装、袋装商品且可以通过控制履带转动时间来精确控制出货数量。缺点是结构相对复杂。3. 垂直堆积式货道我采用的方案这是最简单、最适合初学者的方案。用一个截面略大于商品的方管或PVC管作为货道商品依靠重力竖直堆积。货道底部有一个由舵机控制的“闸门”或“推杆”。出货时舵机动作将最底部的一个商品推出或放行使其掉落。我用的是亚克力板激光切割成的方形管道底部安装了一个9g舵机舵机上粘了一个3D打印的小推片。当舵机旋转时推片水平伸出将最底部的零食袋推出去。这种方案结构极其简单易于加工和调试非常适合包装规整的袋装零食。注意在设计垂直货道时货道的内壁光滑度至关重要。如果摩擦力太大商品可能会卡住无法顺利下落。我曾在货道内壁贴了普通打印纸结果商品下落不畅。后来换用了表面更光滑的亚克力板并在内壁涂抹了一层薄薄的食品级硅脂非常少的量下落就顺畅多了。另外货道的截面尺寸要比商品单边大2-3毫米预留活动空间。3.2 机身框架与外壳材料与加工选择机身需要坚固、美观且便于安装内部组件。木材多层板、松木加工最容易使用手锯、电钻和螺丝就能完成。质感温暖适合家居风格。但木材怕潮且精度一般。我最初的原型机就是用15mm厚的多层板做的用角码连接非常结实。亚克力板现代感强透明或彩色效果很棒。需要激光切割设计文件通常用CAD软件如Fusion 360或Inkscape绘制要非常精确包括所有的螺丝孔、卡槽。组装通常使用胶水亚克力专用氯仿或螺丝。优点是美观、精度高可以做出很复杂的结构。缺点是脆不耐刮且激光切割服务有一定成本。3D打印PLA/ABS灵活性最高可以打印出任何你能想象到的复杂结构包括一体成型的货道、齿轮、定制支架等。适合制作小型、精密的部件。但对于大型机身打印时间长、成本高且强度可能不如木材或亚克力。我机器上的所有传感器支架、舵机固定件、推片都是自己设计并3D打印的。铝型材如2020/2040工业风强度极高模块化程度高使用配套的连接件可以像搭积木一样快速构建框架。然后在框架上安装亚克力板或木板作为面板。这是制作大型、重型设备的优选但成本相对较高。我的建议是混合使用。主体框架用木材或铝型材保证强度内部功能件货道、支架用亚克力或3D打印保证精度前面板用亚克力增加观感。在动手切割或打印前务必在软件中进行虚拟装配检查所有零件的干涉和螺丝孔位是否对齐。3.3 出货口与收集槽商品被推出货道后需要经过一个滑道落入收集槽。这个滑道的设计要点是角度要足够陡确保商品能依靠重力顺利滑下一般建议大于45度角。内壁要光滑同样是为了减少摩擦亚克力或金属板是好选择。底部要有缓冲商品掉落时会有撞击声可以在收集槽底部粘贴一小块EVA海绵或绒布来减震降噪。防夹手设计出货口的大小应仅容商品通过防止儿童将手伸入。4. 让机器“思考”Arduino核心代码与逻辑实现硬件组装完毕接下来就是赋予它灵魂。我们将用Arduino IDE编写控制程序。这里我以最经典的“投币-选择-出货”流程为例拆解核心代码模块。4.1 引脚定义与全局变量首先我们需要规划好Arduino的引脚分配并定义程序运行所需的变量。// 引脚定义 // 投币传感器假设接在数字引脚2使用中断功能提高响应速度 #define COIN_SENSOR_PIN 2 // 三个货道的选择按钮接在数字引脚4,5,6 #define BTN_A 4 #define BTN_B 5 #define BTN_C 6 // 三个货道的出货舵机接在数字引脚9,10,11支持PWM #define SERVO_A 9 #define SERVO_B 10 #define SERVO_C 11 // 三个货道的缺货检测传感器接在模拟引脚A0,A1,A2 #define SENSOR_A A0 #define SENSOR_B A1 #define SENSOR_C A2 // 全局变量 #include Servo.h // 引入舵机库 Servo servoA, servoB, servoC; // 创建三个舵机对象 int currentCredit 0; // 当前投入的金额单位分假设1元100分 int priceA 300; // 商品A价格 3元 int priceB 200; // 商品B价格 2元 int priceC 100; // 商品C价格 1元 // 货道状态true表示有货false表示缺货 bool stockA true; bool stockB true; bool stockC true; // 投币脉冲计数相关变量 volatile unsigned long coinPulseStartTime 0; // 脉冲开始时间 volatile int coinPulseWidth 0; // 脉冲宽度 bool newCoinDetected false; // 是否有新硬币需要处理4.2 投币识别的中断服务程序为了不遗漏任何一枚快速投入的硬币我们使用中断来捕获投币传感器的信号变化。将传感器输出接到Arduino的2号或3号引脚支持外部中断。// 中断服务函数当硬币传感器引脚状态变化时触发下降沿即硬币开始遮挡 void coinPulse() { if (digitalRead(COIN_SENSOR_PIN) LOW) { // 记录脉冲开始时间 coinPulseStartTime micros(); } else { // 引脚变高硬币通过结束计算脉冲宽度 coinPulseWidth micros() - coinPulseStartTime; newCoinDetected true; // 设置标志位主循环中处理 } } void setup() { // ... 其他初始化代码 ... pinMode(COIN_SENSOR_PIN, INPUT_PULLUP); // 启用内部上拉电阻 // 在COIN_SENSOR_PIN上启用中断监听CHANGE变化从高到低和从低到高 attachInterrupt(digitalPinToInterrupt(COIN_SENSOR_PIN), coinPulse, CHANGE); // ... 其他初始化代码 ... }在loop()函数中我们检查newCoinDetected标志位并根据coinPulseWidth来判断硬币面值。这需要你事先测量让1元硬币和5角硬币分别通过传感器在串口监视器中打印出脉冲宽度取一个平均值作为判断阈值。void loop() { // 处理新投入的硬币 if (newCoinDetected) { newCoinDetected false; // 清除标志位 // 根据脉冲宽度判断硬币 if (coinPulseWidth 800 coinPulseWidth 1200) { // 假设1元硬币脉冲宽度约1000微秒 currentCredit 100; Serial.println(投入1元当前余额 String(currentCredit) 分); } else if (coinPulseWidth 400 coinPulseWidth 800) { // 假设5角硬币脉冲宽度约600微秒 currentCredit 50; Serial.println(投入5角当前余额 String(currentCredit) 分); } else { Serial.println(无法识别的硬币或干扰信号); } } // ... 其他主循环逻辑 ... }4.3 主状态机与商品选择逻辑主循环的核心是一个简单的状态机它持续做以下几件事扫描按钮状态。检查余额是否足够购买被按下的商品。检查该货道是否有货。如果条件都满足则驱动对应舵机出货并扣除余额。void loop() { // ... 投币处理代码 ... // 检查按钮A if (digitalRead(BTN_A) LOW) { // 假设按钮按下为低电平 delay(50); // 简单防抖延时 if (digitalRead(BTN_A) LOW) { // 再次确认 if (currentCredit priceA stockA) { vendProductA(); currentCredit - priceA; Serial.println(购买商品A成功剩余余额 String(currentCredit) 分); } else if (!stockA) { Serial.println(商品A缺货); } else { Serial.println(余额不足); } while(digitalRead(BTN_A) LOW); // 等待按钮释放 } } // 类似地检查按钮B和C... // checkButton(BTN_B, priceB, stockB, vendProductB); // checkButton(BTN_C, priceC, stockC, vendProductC); // 实时检测缺货状态假设传感器低电平表示有商品遮挡 stockA (analogRead(SENSOR_A) 500); // 阈值需要根据实际传感器调整 stockB (analogRead(SENSOR_B) 500); stockC (analogRead(SENSOR_C) 500); } // 出货函数示例 void vendProductA() { servoA.write(90); // 舵机转到90度位置推出去 delay(1000); // 保持1秒确保推出动作完成 servoA.write(0); // 舵机转回0度位置收回来 delay(500); // 等待收回稳定 }实操心得防抖与状态管理。上面的代码使用了最简单的delay()进行按钮防抖和动作延时这在单一任务时没问题但会导致机器在出货期间无法检测投币。更优的做法是使用非阻塞式编程利用millis()函数来管理时间。例如记录出货开始的时间然后在loop()中检查是否到达预定的结束时间而不是用delay()卡住整个程序。这样机器就能在出货动画进行的同时继续响应投币和其他事件体验更流畅。这是从新手向进阶迈进的关键一步。5. 调试、优化与那些不得不说的“坑”组装和编程完成后真正的挑战才刚刚开始。调试是一个反复试错、观察和调整的过程。5.1 机械部分的常见问题与调整问题商品卡在货道里推不出来或掉不下来。原因1货道间隙过小或内壁不光滑。用砂纸打磨内壁或涂抹极少量润滑剂如石墨粉、食品级硅脂。原因2舵机推力或行程不足。检查舵机扭矩是否足够9g舵机扭矩约1.6kg·cm。增加推杆的长度可以增大推力但会降低速度。确保舵机旋转角度servo.write()的参数能让推片完全伸入货道并推出商品。原因3商品堆叠压力太大。垂直货道中上方商品的重力全部压在底部商品上导致摩擦力巨大。可以尝试不要将货道堆得太满或者设计一个“弹簧顶杆”从货道上方轻轻顶住商品堆抵消一部分重力。问题商品掉落位置不准有时会弹到收集槽外。原因滑道角度或未端导向不好。调整滑道角度确保其直接对准收集槽中心。可以在滑道出口处增加一个柔软的“导向漏斗”用塑料片或3D打印引导商品落入槽中。5.2 电路与传感器的调试技巧问题投币识别不准经常误判或漏判。原因1传感器安装不稳固或对位不准。确保红外对管发射和接收端正对且硬币滑道能稳定地从中间穿过。可以用热熔胶或3D打印件将其牢牢固定。原因2环境光干扰。红外传感器对环境光敏感尤其是日光灯。解决方法一、给传感器套上黑色热缩管或使用遮光罩二、在代码中采用“调制解码”思路让红外发射管以特定频率闪烁接收端只识别这个频率的信号需要更复杂的电路或专用模块。原因3硬币通过速度不一致。手动投币和机器震动可能导致速度变化影响脉冲宽度。可以在滑道设计上增加一个“阻尼”部分比如用毛刷或硅胶条让硬币匀速通过。或者在代码中采用更宽容的阈值范围并取多次投币的平均值作为校准基准。问题多个舵机同时动作时Arduino重启或舵机乱转。原因电源功率不足或干扰。这是最经典的问题。务必确保电机电源与控制电源分离且电机电源功率足够单个9g舵机堵转电流可达500-700mA。在Arduino的5V和GND之间以及电机驱动电源的正负之间都并联一个100μF以上的电解电容和一个0.1μF的陶瓷电容用于滤除高频和低频噪声。如果问题依旧尝试在给舵机供电的线上串接一个大电流二极管防止反向电流冲击。5.3 软件逻辑的健壮性提升最初的代码可能只能处理“理想情况”。我们需要让它更健壮。增加“出货检测”在商品掉落的下方安装一个振动传感器或另一对红外传感器。当vendProduct()函数执行后等待一个合理的时间比如2秒然后检查振动传感器是否有信号。如果没有则判断为出货失败需要尝试重新出货一次或报警并退款将余额加回去。这能有效应对商品卡住的情况。实现“退款”功能增加一个“退款按钮”。当按下时如果余额大于零则控制一个舵机或电机将相应数量的硬币从退币口推出这需要另一个复杂的机械结构。或者对于扫码支付版本可以触发一个网络退款请求。数据持久化使用Arduino的EEPROM电可擦写存储器来保存关键数据如总销售额、各商品库存量。这样即使机器断电重启数据也不会丢失。每次成功出货后更新EEPROM中的数据。6. 进阶玩法从基础版到智能版的升级思路当你的基础版售货机稳定运行后就可以考虑给它加装“外挂”了。1. 添加显示与交互界面LCD屏幕如1602, 2004显示余额、商品价格、库存信息、欢迎语等。使用I2C接口的模块只需连接4根线编程简单。OLED屏幕比LCD更轻薄、对比度高可以显示更丰富的图形比如商品图片、动画效果。触摸屏Nextion, TFT可以实现完全图形化的点选界面取代物理按钮更加现代化。2. 联网与智能管理核心硬件将Arduino替换为NodeMCUESP8266或ESP32开发板它们自带Wi-Fi功能。远程监控让售货机连接本地Wi-Fi定时向一个你指定的服务器可以是国内云服务器也可以是内网穿透到家里的电脑发送心跳包汇报状态库存、销售额、温度等。你可以在手机或电脑上查看仪表盘。扫码支付集成这是联网后的自然延伸。你可以使用微信支付或支付宝的商户API个人可以申请当面付等产品。流程是用户在屏幕选择商品 - 售货机生成一个支付二维码调用支付平台API并显示 - 用户扫码支付 - 支付平台回调你的服务器 - 你的服务器通过网络MQTT或HTTP发送出货指令给售货机。这一步涉及服务器端编程是更大的挑战但也更有成就感。库存预警当某个商品库存低于设定值时自动发送邮件或微信消息通过Server酱等工具提醒你补货。3. 视觉识别与交互加入摄像头ESP32-CAM可以实现人脸识别启动、手势选择商品等炫酷功能。例如识别到预设的用户面孔后自动亮屏并显示该用户常买的商品推荐。机器学习边缘计算在树莓派上运行轻量级模型识别放入投币口的物体是否为目标硬币甚至识别商品的种类对于多商品混合货道。从一堆散乱的零件到一台能可靠运行的自动售货机这个过程充满了挑战但每一步问题的解决都带来巨大的满足感。我自己的第一台机器在成功卖出第一包零食时那种兴奋感至今难忘。它现在放在我的工作室里主要功能已经从“售卖”变成了“展示”和“招待”每当有朋友来访它都是一个绝佳的谈资和演示项目。记住DIY的精髓不在于一次做到完美而在于迭代。先从最简单的投币和推杆做起让它动起来。然后逐步增加传感器、屏幕、网络功能。每一个新功能的加入都会让你对系统集成有更深的理解。过程中遇到的每一个“坑”搜索引擎、开源社区和你的调试器都是最好的老师。祝你建造愉快期待看到你的作品。