
1. 项目概述与核心思路做硬件项目尤其是涉及到机械结构和电子控制结合的时候最怕的就是想法很美好动手时却一团乱麻。这个基于Arduino的智能硬币分拣器项目就是一个典型的“从想法到实物”的绝佳练手案例。它不只是一个简单的玩具而是融合了结构设计、嵌入式编程和基础机电一体化的微型工程实践。这个分拣器的核心任务很明确用户通过面板上的按钮选择想要获取的硬币种类和数量系统接收到指令后控制对应的伺服电机动作将指定数量的硬币从储币仓中推出硬币沿滑道落入取币托盘完成一次分发。整个过程模拟了自动售货机的找零逻辑但结构更透明更适合学习和理解背后的控制原理。为什么选择硬币分拣作为项目载体首先它的需求明确输入按钮和输出电机动作、硬币掉落都非常直观反馈即时调试过程能看到实实在在的结果成就感强。其次它麻雀虽小五脏俱全你需要考虑机械结构如何稳定储币、可靠推出、传感器与输入按钮去抖、LCD显示、执行器控制伺服电机角度与速度、系统逻辑状态管理、计数以及供电脱离电脑独立运行。最后它的可扩展性很强理解了基础框架后你可以很容易地加入硬币识别传感器如红外对管或称重模块让它真正“智能”起来或者增加网络模块实现远程控制。这个教程将带你完整走一遍从零搭建的过程。我会基于常见的Arduino Uno和SparkFun Inventor‘s Kit但所有思路和代码都具备通用性你用其他兼容的开发板和元件一样可以完成。我会重点拆解那些原始教程里可能一笔带过但实际做起来最容易卡壳的细节比如伺服电机扭矩够不够、机械结构怎么防止卡币、代码里如何实现可靠的去抖和状态机。毕竟让一个想法动起来光有电路图和代码清单是不够的那些“为什么这么做”和“踩过什么坑”的经验才是关键。2. 物料清单与工具准备在开始切割纸板和焊接线缆之前一份清晰的物料清单和合适的工具能让你事半功倍。原始教程列出了所需物品这里我结合自己的经验做一次更详细的梳理和解读特别是对于一些可替代选项和关键参数的选择。2.1 电子元件详解这是项目的大脑和神经系统选择时需要考虑兼容性和驱动能力。主控板Arduino Uno R3。这是最经典的选择生态丰富资料无数。它的ATmega328P微控制器有足够的GPIO引脚来驱动本项目所需的所有外设。如果你手头是Nano、Leonardo或其他兼容板请务必对照引脚图进行映射。伺服电机SG90微型舵机 (4个)。这是本项目最核心的执行器。SG90价格低廉扭矩约为1.6kg·cm对于推动一摞硬币来说需要仔细评估。我的实测经验是在结构顺滑、硬币尤其是25美分不超过5枚的情况下SG90勉强够用但更推荐使用扭矩更大的MG90S金属齿轮扭矩约2.5kg·cm或DS3218扭矩可达20kg·cm可靠性会高很多。每个电机需要一根信号线接PWM引脚以及VCC和GND。显示模块16x2字符LCD带I2C接口。强烈建议使用带I2C转接板的LCD它只需要4根线VCC, GND, SDA, SCL就能驱动节省了宝贵的GPIO引脚也简化了布线。原始教程中提到的10k Trimpot可调电阻是用于调节非I2C LCD的对比度如果使用I2C版本则不再需要。输入设备轻触按键 (5个)。用于选择硬币种类25美分、10美分、5美分、1美分以及触发“分发”命令。选择常见的6x6mm或12x12mm四脚轻触开关即可。每个按键需要接一个10kΩ的上拉电阻到VCC另一端接地信号脚接Arduino的数字输入引脚并启用内部上拉。这是实现稳定输入的关键。电源系统开发调试期直接用USB线连接电脑供电最为方便。独立运行期需要外接电源。Arduino Uno的Vin引脚可以接受7-12V直流输入。方案一是使用4节AA电池盒6V但请注意6V略低于推荐值可能导致系统在电机动作时电压波动重启。更稳妥的方案是使用9V电池或一个输出7.5V/1A以上的直流电源适配器。伺服电机务必接在电源的输出端而不是Arduino的5V引脚上否则大电流可能损坏板载稳压芯片。连接与支撑面包板用于快速搭建和测试电路。杜邦线跳线公对公、公对母都需要用于连接Arduino、面包板和各个元件。电阻除了按键所需的4个10kΩ上拉电阻如果使用LED指示可能还需要220Ω的限流电阻。注意供电是硬件项目的“血液”。伺服电机在启动和堵转时瞬间电流很大可达500mA-1A。如果所有电机和Arduino都从一个弱电源取电会导致整体电压被拉低引起Arduino复位或程序跑飞。务必将电机的电源红、黑线直接接到外部电源的正负极仅将信号线黄/橙线接至Arduino。可以使用一个共地连接确保Arduino和电机的地电位一致。2.2 结构材料与工具机械部分是创意的实体化材料的选择直接影响最终装置的稳定性和寿命。主体结构材料瓦楞纸板成本低易加工是原型制作的绝佳材料。建议选择厚度在3mm以上的硬质纸板如快递箱材质。太薄的纸板容易变形影响结构精度。升级选择如果你希望作品更坚固、更专业可以考虑使用椴木板或亚克力板进行激光切割。这能获得极高的精度和美观度但需要相应的加工设备或服务。连接与固定热熔胶枪与胶棒纸板结构连接的主力。速度快强度对于本项目足够。注意控制胶量过多会溢出影响美观和活动部件。电工胶带用于绝缘和固定线束防止线路杂乱和短路。尼龙扎带整理线缆的神器让盒子内部整洁有序。加工工具美工刀/钩刀切割纸板的主要工具配合钢尺可以切出笔直的边缘。钢尺/直角尺确保测量和切割的准确性。“差之毫厘谬以千里”在机械装配上体现得淋漓尽致。切割垫保护桌面也方便测量。铅笔和橡皮在纸板上画线做标记。电烙铁与焊锡可选如果你希望连接更牢固可以将伺服电机的延长线、按键引线等直接焊接而不是仅靠面包板插接。在采购或清点完所有物料后建议先在桌面上将所有电子部分用面包板搭接并简单测试确保每个元件都能正常工作再进行机械部分的制作。这叫“电子先行机械后行”可以避免做好结构后发现电路有问题拆装麻烦的窘境。3. 机械结构设计与制作详解机械结构是整个装置的骨骼和肌肉它的设计直接决定了分拣动作是否可靠、流畅。原始教程给出了基本的形状和尺寸这里我将深入每个部件的设计意图、关键尺寸的考量以及制作中的技巧与避坑点。3.1 储币仓与推出机构设计这是最核心的机械模块每个币种对应一个。其核心要求是每次只允许一枚硬币处于待推出位置并且推出动作要可靠、不卡滞。储币仓Coin Holder设计思路它是一个垂直的、截面略大于单枚硬币的管道。硬币依靠重力叠放在其中。底部的开孔只能让最下面一枚硬币被推出。尺寸计算以美国25美分直径约24.26mm为例。纸板厚度假设为3mm。那么储币仓的内部宽度应为硬币直径 约1mm活动间隙即约25mm。内部深度前后距离同理。高度则取决于你想存储的硬币数量例如10枚25美分叠放厚度约17mm加上顶部余量仓体高度做到50-60mm比较合适。制作要点切割四片完全相同的长方形纸板两片做侧壁两片做前后壁用热熔胶粘成长方体管道。务必保证粘合后内部通道上下一致没有向内凸出的胶块否则硬币下落会不顺畅。可以在粘合前用一枚硬币作为“规尺”在内部比划一下。推出机构Dispenser Mechanism功能解析这个机构由固定部分连接箱体、活动部分连接伺服电机摆臂和导向部分构成。伺服电机旋转时带动摆臂我们粘在电机轴上的小纸板像铲子一样水平插入储币仓底部硬币的下方将其铲出。关键零件“Piece 2”的形状这个像山形的零件是精髓。其下部与储币仓底部开口对齐形成一个“硬币出口”。上部较高的部分用于安装伺服电机。中间的斜坡确保了当摆臂回缩时不会将硬币带回去或卡住。伺服电机安装电机必须被牢固地固定在“Piece 2”上并且其输出轴的中心高度要略高于储币仓底部硬币的厚度中心。这样摆臂在水平位置时才能刚好抵住硬币底部旋转到一定角度时才能将硬币向上铲起并推出。用热熔胶固定电机时先不要上胶手动旋转轴心模拟摆臂运动轨迹确保它在整个运动范围内不会碰到任何结构然后再点胶固定。摆臂Actuator Arm制作用结实一些的材料如雪糕棒、薄塑料片或硬卡纸裁剪一个长约15-20mm宽约5-8mm的小片。将其牢固地粘在伺服电机的舵盘上。这个摆臂就是直接接触硬币的“手指”。它的长度需要调试太短可能推不到硬币太长则在回位时可能碰到仓体或下一枚硬币。实操心得防止卡币的黄金法则。卡币是这种重力供料机构最常见的问题。除了保证内部光滑、尺寸合适外有两个小技巧非常有效第一在储币仓底部出口处的两侧用胶水贴上光滑的胶带如透明胶带减少硬币与纸板的摩擦。第二确保推出动作完成后伺服电机能回到一个让摆臂完全脱离硬币通道的位置为下一枚硬币的下落腾出空间。这需要在代码中精确设定电机的角度。3.2 主体箱体与内部滑道系统箱体是所有模块的安装平台也是硬币分拣后的汇集通道。它的设计要兼顾强度、模块安装的便利性和硬币滑动的流畅性。箱体The Box尺寸确定15x15x15英寸约38x38x38厘米是一个较大的尺寸为四个储币仓并排安装和内部滑道留出了充足空间。你可以根据自己储币仓的大小等比例缩小但务必确保内部有足够空间安装所有电机和走线。前面板加工这是人机交互界面。LCD窗口和按钮孔的定位要精确。先用铅笔和尺子在纸板上画好线。切割LCD窗口时可以切得比屏幕实际显示区域略小这样安装后能有一个边框压住屏幕边缘更美观牢固。按钮孔直径要略小于按钮帽的直径这样按钮才能卡住不掉落。结构加固大型纸板箱容易变形。可以在箱体内部的棱角处粘贴三角形的纸板加固筋能显著提升整体刚性。硬币滑道Coin Slide功能与设计它的任务是将从不同高度、不同位置推出的硬币平稳、集中地引导到前部的取币托盘中。其原理是利用重力并尽量减少硬币在滑动过程中的碰撞和跳跃。“漏斗”与“导流板”原始教程中托盘后部向上延伸的部分就是一个漏斗承接从高处掉落的硬币。箱体内部两侧倾斜的纸板矩形片是导流板。关键的角度导流板的倾斜角度至关重要。角度太小太平硬币可能滑不下去角度太大太陡硬币下滑速度过快可能会飞溅出托盘。经过测试与水平面呈30-45度的夹角是比较理想的。你可以用热熔胶临时固定放入硬币测试滑动效果调整满意后再彻底固定。滑道表面处理同样在滑道表面粘贴光滑的胶带或使用光滑的塑料片作为衬里可以极大地改善硬币的滑动性能减少卡住或翻倒的概率。3.3 总装与调试要点将所有模块组装进箱体是最后一步也是检验之前所有制作是否精准的时刻。安装顺序建议按“由内到外”的顺序。先安装内部的滑道系统然后安装四个硬币推出机构最后安装前面板已固定好Arduino和面包板。这样便于在内部进行操作和调整。伺服电机定位将推出机构粘到箱体侧壁时必须再次确认伺服电机摆臂的运动空间。用手缓慢旋转电机轴可临时接电测试观察摆臂在整个运动周期内是否会刮擦箱体壁或相邻的机构。预留至少2-3mm的安全间隙。线缆管理四个伺服电机、五个按钮、一个LCD屏线缆会非常多。在安装过程中就用扎带将同一路径的线缆捆扎在一起并沿着箱体内壁固定。混乱的线缆不仅不美观还可能被运动部件卷入造成故障。给每个伺服电机的线缆贴上标签如Q、D、N、P代表不同币种后续接线和调试时会无比轻松。动态调试机械部分初步组装完成后不要立刻封顶。先不编程直接用Arduino IDE的“舵机示例”程序Sweep分别测试每个电机观察硬币推出动作是否顺畅。可以手动在储币仓放入2-3枚硬币测试连续推出是否卡住。这个阶段发现机械问题修改起来最容易。4. 电路连接与系统集成当机械骨架搭建完毕我们就需要为其注入“神经网络”——电路连接。正确的接线是硬件项目成功的一半而清晰的接线图和规范的实操则是避免“魔法烟雾”的保障。4.1 核心电路原理图解析虽然我们使用面包板进行搭接但理解整个系统的电路原理至关重要。整个系统可以划分为几个功能模块主控与电源模块Arduino Uno是核心。外部电源如电池盒的正极接Vin引脚负极接GND。如果使用USB供电则无需连接Vin。务必确保所有模块的GND地线最终都连接到Arduino的GND形成共同的参考电位这是电路正常工作的基础。伺服电机驱动模块这是功耗最大的部分。如前所述切勿从Arduino板载的5V引脚取电。正确的接法是将外部电源的正极同时接到面包板的电源正极总线以及所有伺服电机的VCC通常为红线或棕线。外部电源的负极接到面包板的电源负极总线并连接所有伺服电机的GND通常为黑线或褐线以及Arduino的GND。每个伺服电机的信号线通常为黄线或橙线则分别接至Arduino的一个支持PWM脉宽调制的数字引脚如9, 10, 11等。LCD显示模块I2C这是最简单的部分。I2C模块通常有4个引脚VCC- Arduino5VGND- ArduinoGNDSDA- ArduinoA4或标有SDA的引脚SCL- ArduinoA5或标有SCL的引脚。按钮输入模块这是最容易出错的部分。我们需要实现“上拉电阻”电路。每个按钮有三条连接一脚接GND对角的一脚接一个10kΩ电阻该电阻的另一端接5V同时按钮接电阻的这一脚还要连接到Arduino的一个数字输入引脚如2, 3, 4, 5, 6。当按钮未按下时输入引脚通过10kΩ电阻被拉到5V高电平按下时引脚直接接通GND低电平。Arduino代码中需要启用内部上拉INPUT_PULLUP与这个外部上拉电阻协同工作确保信号稳定。4.2 面包板布局与接线实操步骤清晰的布局能让调试和排查故障效率倍增。建议按以下步骤在面包板上搭建规划区域将面包板想象成一个城市。中间凹槽分开上下两部分。通常将最上面一排作为5V总线为Arduino逻辑部分、LCD、按钮上拉供电最下面一排作为GND总线。中间区域用于放置元件和跳线。安置核心将Arduino Uno通过跳线连接到面包板从Arduino的5V引脚引线到面包板5V总线从GND引脚引线到GND总线。连接LCD将I2C LCD模块插在面包板空闲区域。用四根跳线将其VCC,GND,SDA,SCL分别连接到面包板的5V总线、GND总线以及Arduino的A4和A5。布置按钮电路以第一个按钮25美分为例将按钮跨坐在面包板中间凹槽上这样四只脚分在了上下两个区域。按钮一侧的上脚和下脚是连通的另一侧的上脚和下脚是连通的。我们利用其中一组。用一根跳线将按钮一组脚中的一只连接到面包板的GND总线。在按钮另一组脚所在列插入一个10kΩ电阻电阻的另一端连接到面包板的5V总线。最后用一根跳线从按钮与电阻相连的那只脚所在的列连接到Arduino的数字引脚2。其余4个按钮10美分、5美分、1美分、分发键如法炮制分别接到引脚3,4,5,6。连接伺服电机将面包板的5V和GND总线通过较粗的导线或并联多根延伸出作为电机的电源总线。每个伺服电机的VCC红接扩展的5V总线GND黑/褐接扩展的GND总线。四个电机的信号线黄/橙分别接Arduino的引脚9,10,11,12。最终检查接线完成后花10分钟对照原理图逐一检查所有VCC/5V是否连接正确有无短路到GND的风险所有GND是否都连通了按钮的上拉电阻和接地线是否接对伺服电机电源是否独立于Arduino板载5V4.3 上电前检查与烟雾测试在接通任何电源之前进行目视和通断检查目视检查查看有无裸露的线头可能相碰焊点是否光滑无毛刺元件引脚有无弯曲导致意外接触万用表通断测试如有将万用表调到蜂鸣档测量5V总线与GND总线之间的电阻。在未上电、所有元件连接好的情况下应该有一个较大的阻值至少几百欧姆以上。如果电阻非常小如几欧姆说明存在严重短路必须排查。首次上电先不连接伺服电机电源只给Arduino和LCD、按钮上电通过USB或外部电源。观察Arduino上的电源指示灯是否正常亮起LCD是否背光亮起可能无内容。用手逐个按下按钮观察Arduino板上对应的数字引脚旁边的LED如果有的話是否有反应这是最初步的输入测试。连接电机电源如果逻辑部分测试正常再连接伺服电机的电源。此时所有电机可能会轻微抖动一下并回到初始位置这是正常的。如果某个电机持续抖动或发出异响立即断电检查该电机的接线和机械结构是否卡死。5. 软件设计与代码实现硬件是身体软件是灵魂。这段代码需要稳健地管理用户输入、更新显示、并精确控制电机动作。我们将采用状态机的思路来构建程序这样逻辑清晰易于调试和扩展。5.1 程序框架与库引入首先我们需要包含必要的库并定义整个程序用到的常量和变量。#include Wire.h // I2C通信库 #include LiquidCrystal_I2C.h // I2C LCD库 #include Servo.h // 伺服电机库 // 引脚定义 const int BUTTON_PINS[] {2, 3, 4, 5, 6}; // 按钮引脚: 25c, 10c, 5c, 1c, 分发 const int SERVO_PINS[] {9, 10, 11, 12}; // 伺服电机引脚: 对应上述币种 const int NUM_COIN_TYPES 4; // 伺服电机对象和角度定义 Servo coinServos[NUM_COIN_TYPES]; const int SERVO_REST_ANGLE 10; // 初始位置角度避开硬币 const int SERVO_PUSH_ANGLE 60; // 推出硬币的角度 const int SERVO_PUSH_DELAY 300; // 推出动作持续时间毫秒 // 全局变量 int coinCounts[NUM_COIN_TYPES] {0}; // 存储用户当前选择的各币种数量 int lastButtonStates[NUM_COIN_TYPES 1] {HIGH}; // 按钮上一状态 (1是分发键) unsigned long lastDebounceTime 0; const unsigned long DEBOUNCE_DELAY 50; // 去抖延时 // 初始化LCD地址通常是0x27或0x3F需根据你的模块调整 LiquidCrystal_I2C lcd(0x27, 16, 2); // 币种名称显示 const char* coinNames[] {Q(25c), D(10c), N(5c), P(1c)};代码解析#include引入三个必需的库。Servo.h是Arduino内置库用于方便地控制舵机。LiquidCrystal_I2C.h需要额外安装它简化了I2C LCD的操作。const int使用常量定义引脚而不是直接在代码中写数字这样程序可读性更好修改引脚时只需改一处。Servo对象数组创建四个Servo对象便于统一管理。SERVO_REST_ANGLE和SERVO_PUSH_ANGLE这是需要根据你的机械结构实际调试的关键参数。REST_ANGLE是电机待机时的角度应确保摆臂完全脱离硬币下落路径。PUSH_ANGLE是推出硬币所需旋转的角度需要实验确定。coinCounts数组记录用户按下了几次“25美分”、“10美分”等按钮即待分发的数量。去抖相关变量机械按钮在按下和弹起时触点会产生物理抖动导致Arduino在几毫秒内读到多次快速变化的信号。我们需要通过软件“去抖”来忽略这些抖动确保一次按压只被识别一次。5.2 初始化设置setup函数在setup()函数中我们需要初始化所有硬件模块。void setup() { Serial.begin(9600); // 初始化串口用于调试输出 lcd.init(); // 初始化LCD lcd.backlight(); // 打开背光 lcd.setCursor(0, 0); lcd.print(Coin Dispenser); lcd.setCursor(0, 1); lcd.print(Ready...); // 初始化按钮引脚为输入上拉模式 for (int i 0; i NUM_COIN_TYPES 1; i) { // 1 包括分发键 pinMode(BUTTON_PINS[i], INPUT_PULLUP); lastButtonStates[i] HIGH; // 假设初始状态为高未按下 } // 初始化伺服电机 for (int i 0; i NUM_COIN_TYPES; i) { coinServos[i].attach(SERVO_PINS[i]); coinServos[i].write(SERVO_REST_ANGLE); // 移动到初始位置 delay(100); // 给电机一点时间归位 } // 显示初始界面 updateDisplay(); }关键点说明INPUT_PULLUP将按钮引脚设置为输入模式并启用内部上拉电阻。这意味着当按钮未按下时引脚通过内部电阻读到高电平(HIGH)按下时引脚接地读到低电平(LOW)。servo.attach()将Servo对象与具体的数字引脚关联起来。servo.write()立即将电机转到指定角度。updateDisplay()这是一个我们自定义的函数用于在LCD上刷新显示当前选择的硬币数量。将显示逻辑封装成函数使主循环更简洁。5.3 主循环逻辑与状态管理loop()函数以极高的频率不断运行我们需要在这里扫描按钮、更新状态、并执行分发动作。void loop() { checkButtons(); // 检查所有按钮状态 // 主循环可以添加其他任务如状态指示灯闪烁等 } void checkButtons() { for (int i 0; i NUM_COIN_TYPES 1; i) { int buttonPin BUTTON_PINS[i]; int reading digitalRead(buttonPin); // 读取当前引脚电平 // 检查信号是否发生变化从高到低或从低到高 if (reading ! lastButtonStates[i]) { lastDebounceTime millis(); // 重置去抖计时器 } // 如果信号变化后的持续时间超过了去抖延时则认为这是一个有效的状态改变 if ((millis() - lastDebounceTime) DEBOUNCE_DELAY) { // 如果当前状态是稳定的且与之前记录的状态不同 if (reading ! lastButtonStates[i]) { lastButtonStates[i] reading; // 更新状态记录 // 如果新的稳定状态是 LOW按钮被按下 if (reading LOW) { handleButtonPress(i); // 处理按钮按下事件 } } } // 更新上一次的读取状态用于下一轮循环比较 lastButtonStates[i] reading; } }代码解析软件去抖算法这是处理机械开关的经典方法。核心思想是当检测到引脚电平变化时不立即认为按钮被按下而是等待一段时间DEBOUNCE_DELAY通常20-50ms。如果这段时间后电平仍然保持在新状态才确认这是一个有效的动作。这滤除了开关触点物理抖动产生的毛刺信号。5.4 事件处理与电机控制当确认一个有效的按钮按下事件后程序会调用handleButtonPress()函数。void handleButtonPress(int buttonIndex) { // 前四个按钮对应增加硬币数量 if (buttonIndex NUM_COIN_TYPES) { coinCounts[buttonIndex]; // 对应币种数量加1 if (coinCounts[buttonIndex] 10) { // 限制最大数量例如10枚 coinCounts[buttonIndex] 10; } updateDisplay(); // 更新LCD显示 Serial.print(Added ); Serial.println(coinNames[buttonIndex]); // 串口调试输出 } else if (buttonIndex NUM_COIN_TYPES) { // 分发按钮最后一个按钮 Serial.println(Dispensing...); lcd.clear(); lcd.setCursor(0, 0); lcd.print(Dispensing...); // 遍历所有币种根据数量控制电机 for (int i 0; i NUM_COIN_TYPES; i) { if (coinCounts[i] 0) { dispenseCoin(i, coinCounts[i]); // 分发指定币种指定数量 } } // 分发完成后清零计数 memset(coinCounts, 0, sizeof(coinCounts)); delay(1000); // 显示“完成”信息一段时间 updateDisplay(); // 恢复显示主界面 lcd.setCursor(0, 1); lcd.print(Done! ); } }分发函数dispenseCoin()是控制伺服电机执行具体动作的核心void dispenseCoin(int coinType, int quantity) { Serial.print(Dispensing ); Serial.print(quantity); Serial.print( of ); Serial.println(coinNames[coinType]); for (int j 0; j quantity; j) { // 执行一次推出动作 coinServos[coinType].write(SERVO_PUSH_ANGLE); delay(SERVO_PUSH_DELAY); // 保持推出状态一段时间确保硬币完全推出 coinServos[coinType].write(SERVO_REST_ANGLE); delay(500); // 等待电机回位并为下一枚硬币下落留出时间 // 可选在LCD第二行显示分发进度 lcd.setCursor(0, 1); lcd.print(Disp: ); lcd.print(coinNames[coinType]); lcd.print( ); lcd.print(j1); lcd.print(/); lcd.print(quantity); lcd.print( ); } }关键参数调试SERVO_PUSH_DELAY这个延时决定了电机保持在推出位置的时间。时间太短硬币可能还没完全推出时间太长则影响分发速度。需要根据你的机械结构和电机速度实测调整。循环中的delay(500)在电机回位后等待500ms。这个等待非常必要它有两个作用第一让伺服电机有足够时间转动到位第二更重要的是让被推出的硬币有足够时间沿滑道落下并且让储币仓内的下一枚硬币在重力作用下落到待推出位置。如果这个间隔太短可能会发生“追尾”——上一枚硬币还没走远下一枚就被推出导致卡住。5.5 显示更新函数最后是更新LCD显示的自定义函数void updateDisplay() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(Q: D: N: P:); // 第一行显示币种缩写 lcd.setCursor(0, 1); // 移动到第二行 for (int i 0; i NUM_COIN_TYPES; i) { lcd.setCursor(i * 4, 1); // 每个数量占4个字符位置 if (coinCounts[i] 10) { lcd.print( ); // 个位数前补空格使显示对齐 } lcd.print(coinCounts[i]); } }这个函数将coinCounts数组中的数字整齐地显示在LCD的第二行对应第一行的币种标签让用户一目了然当前的选择。6. 系统调试、优化与问题排查即使按照教程一步步操作第一次上电很可能也不会完美运行。调试是硬件项目的必修课。下面记录了我实践中遇到的一些典型问题及其解决方法希望能帮你快速排雷。6.1 常见问题速查表问题现象可能原因排查步骤与解决方案LCD屏幕不亮或无显示1. 电源未接通或接反。2. I2C地址不正确。3. 对比度问题非I2C屏幕。4. 接线错误SDA/SCL接反。1. 检查VCC和GND接线用万用表测量电压。2. 使用I2C扫描程序Arduino IDE示例中有查找正确地址。3. 调节LCD模块上的电位器如果有。4. 确认SDA、SCL是否与Arduino的A4、A5或对应SDA/SCL引脚连接。按钮按下无反应1. 上拉电阻未接或接错。2. 引脚模式未设置为INPUT_PULLUP。3. 按钮损坏或接触不良。4. 代码中去抖逻辑过于严格或错误。1. 检查按钮电路一脚接地另一脚通过10k电阻接5V信号线从按钮与电阻连接点引出。2. 确认pinMode(pin, INPUT_PULLUP)。3. 用万用表通断档测试按钮按下时是否导通。4. 简化代码先不用去抖直接digitalRead并打印到串口看数值变化。伺服电机不转动或抖动1.电源功率不足最常见。2. 信号线接触不良或接错。3. 机械负载过重电机堵转。4. 代码中引脚定义错误或Servo库未正确关联。1.重点检查电机是否使用独立电源供电电源电压电流是否足够建议5V 2A以上测量电机供电电压在动作时是否跌落到4V以下。2. 确认信号线连接到了正确的PWM引脚如9,10,11。3. 断开电机与机械结构的连接空载测试电机是否能正常转动到指定角度。4. 运行Arduino内置的Sweep示例程序测试单个电机。电机转动但推不出硬币1. 摆臂角度(SERVO_PUSH_ANGLE)设置不当。2. 摆臂长度或形状不合适。3. 硬币卡在储币仓出口。4. 电机扭矩不足。1. 在代码中逐步调整SERVO_PUSH_ANGLE值如30, 45, 60, 75观察哪个角度能有效推出硬币。2. 加长摆臂或改变其形状如前端做成小钩状。3. 检查储币仓出口尺寸确保略大于单枚硬币并用砂纸或胶带打磨光滑内壁。4. 更换扭矩更大的伺服电机如MG90S。一次推出多枚硬币或卡币1. 储币仓内部过宽导致多枚硬币并排卡在底部。2. 推出动作后摆臂没有完全回位阻碍了下一枚硬币下落。3. 硬币滑道不顺畅硬币堆积。1. 修正储币仓内部尺寸确保只能容纳单列硬币。2. 检查SERVO_REST_ANGLE确保摆臂完全收回。增加推出动作后的延时(delay)给硬币下落留足时间。3. 增大滑道倾斜角度并确保表面光滑。系统运行不稳定偶尔复位1. 电机动作时引起电源电压瞬间跌落导致Arduino复位。2. 程序中有内存泄漏或逻辑死循环。3. 接线松动接触不良。1.强化电源在电机电源两端并联一个大容量电解电容如1000uF 16V可以吸收瞬间电流冲击稳定电压。这是解决复位问题的利器。2. 检查代码逻辑避免使用delay()过长导致看门狗复位可考虑改用millis()进行非阻塞计时。3. 按压和摇晃接线处观察是否会出现故障。6.2 高级优化与扩展思路当基础功能稳定后你可以尝试以下优化让项目更上一层楼非阻塞程序设计当前代码大量使用delay()在等待电机动作和硬币下落时整个程序会停止响应。可以改用基于millis()的时间戳管理实现状态机。这样在电机动作期间系统仍然可以扫描按钮用户体验更流畅。增加硬币检测传感器在储币仓底部或滑道出口安装红外对射传感器或微动开关。当传感器检测到硬币成功通过后才记录一次成功分发。这可以实现真正的“计数”避免因卡币导致计数错误。加入声音与灯光反馈在分发开始、进行中、完成时用蜂鸣器发出不同提示音或用LED显示状态。例如用一颗RGB LED蓝色代表待机黄色代表分发中绿色代表完成。系统状态监控通过串口或LCD实时显示一些系统信息如“电机A角度45°”、“当前分发第3枚”等这在调试复杂问题时非常有用。结构材料升级用激光切割的亚克力或木板替换纸板制作2.0版本。精度和耐用性会得到质的提升外观也更专业。这个项目最大的乐趣不在于一次成功而在于不断遇到问题、分析问题、解决问题的过程。每一次调试成功你对硬件、对代码、对整个系统的理解就会加深一层。当你最终看到硬币按照指令清脆地落入托盘时那种亲手创造出一个自动化系统的满足感是无可替代的。希望这份详细的教程和心得能帮你少走些弯路更顺利地享受创造的乐趣。如果在制作中遇到新的问题不妨停下来用串口打印一些数据用万用表量一量电压耐心地观察和思考答案往往就在其中。