1. 项目概述与核心思路如果你对机械传动和嵌入式控制感兴趣那么亲手打造一个能自动装填、一键发射的投石机绝对是个能让你玩上瘾的项目。这不仅仅是把木头和电机拼在一起它融合了结构力学、运动控制和实时逻辑判断是一个典型的机电一体化实践案例。我这次做的这个“自动装填投石机”核心目标就是让这个古老而经典的攻城器械摆脱纯手动操作的繁琐通过Arduino作为大脑步进电机和伺服电机作为肌肉实现从设定、装填到发射的全流程自动化。整个系统的运作逻辑很清晰你通过一个电位器旋钮来设定投石臂的后拉角度这直接决定了发射的力度和射程然后按下黄色按钮系统就开始自动工作。步进电机会通过一个绞盘和绳索稳定而有力地将投石臂的后端配重篮一侧拉下来完成“上弦”动作。在这个过程中一个伺服电机会转动将一个物理锁销插入齿轮箱的特定齿位将拉紧的投石臂牢牢锁住等待发射。此时系统进入“已装填”状态。当你按下绿色按钮另一个伺服电机会动作将锁销拔出蓄势待发的配重篮在重力作用下急速下坠通过杠杆臂将另一端的抛射物高速抛出。任何时候觉得不对劲红色急停按钮都能立刻中断装填过程确保安全。这个项目最有价值的地方在于它把几个关键的工程模块串联了起来机械结构设计杠杆、轴承、锁止机构、精密运动控制步进电机的开环位置控制、快速响应执行伺服电机的角度控制以及上层逻辑管理Arduino处理按钮、传感器输入并协调所有执行器。下面我就把这几个月从画图、切木头、调试代码到最终让机器可靠运行的全过程包括中间踩过的坑和总结出的技巧毫无保留地分享出来。2. 核心部件选型与功能解析工欲善其事必先利其器。一个稳定可靠的自动化项目从选择合适的核心部件开始。这里的每一个选择都直接关系到最终系统的性能、可靠性和成本。2.1 控制核心为什么是Arduino Uno对于这个项目我选择了经典的Arduino Uno R3。原因很简单生态成熟、资源丰富、完全够用。Uno基于ATmega328P微控制器有14个数字I/O口其中6个可做PWM输出和6个模拟输入口对于控制两个伺服电机、一个步进电机驱动器、三个按钮和一个电位器来说绰绰有余。它的5V逻辑电平与我们将要使用的伺服电机、按钮等完美兼容。虽然像Nano体积更小但Uuno在调试阶段插拔线缆更方便也有利于初学者理解电路连接。市面上兼容板很多选择正版或口碑好的兼容板即可确保USB芯片稳定避免驱动问题。注意务必确认你使用的开发板是5V逻辑电平。有些3.3V逻辑的板子如某些ESP8266开发板虽然引脚兼容但直接驱动5V器件可能存在问题或损坏风险。2.2 动力肌肉步进电机与伺服电机的分工这是项目的核心执行机构它们的特性决定了分工。步进电机 (Stepper Motor) - 负责“装填”的精准拉力我选用的是42步进电机型号SL42STH40-1684A这是一个非常通用的型号。它的关键参数是1.8°步距角。这意味着电机旋转一圈需要360°/1.8° 200步。配合微步进驱动器可以实现更高的分辨率。我选择它的理由是开环控制位置精确无需编码器反馈只要不发生失步后面会讲如何避免控制器发出的脉冲数就严格对应电机轴转过的角度。这对于需要精确控制拉绳长度的装填过程至关重要。低速大扭矩步进电机在低速时能保持很大的扭矩非常适合这种需要缓慢、稳定施加拉力的场景。我们不需要它高速旋转但需要它能稳稳地拉起配重。保持扭矩通电但静止时步进电机能产生保持扭矩这对于在装填中途暂停或锁定前维持状态有好处。原项目提到“12V 200 steps/revolution equivalent”指的就是这类最常用的1.8°两相四线步进电机。你完全可以用其他品牌同规格电机替代。伺服电机 (Servo Motor) - 负责“锁定”与“释放”的快速动作我使用了两个SG90 9g微型伺服电机。这种舵机内部集成了电机、减速齿轮组和控制电路接收PWM信号来控制输出轴的角度通常0-180°。它的特点是闭环控制舵机内部有电位器反馈控制器会不断调整直到轴到达指令位置位置控制简单可靠。响应速度快从收到信号到转动到指定角度时间很短非常适合做“锁定”和“释放”这种需要快速、准确到位的动作。接口简单只需要一根PWM信号线以及电源和地线Arduino有现成的Servo库驱动极易上手。一个舵机用于控制齿轮箱的机械锁销在步进电机拉到位后转动一个角度将锁销插入齿轮防止回转。另一个舵机则用于控制一个释放机构如挡板或第二道锁在发射指令下达时快速移开障碍让锁销能在配重重力作用下被弹出或直接拉动锁销复位。2.3 动力桥梁TB6612FNG双路电机驱动模块Arduino的I/O口无法直接驱动步进电机需要专用的驱动模块。我选择了SparkFun的TB6612FNG双路直流电机驱动模块也可用常见的DRV8825或A4988步进电机驱动模块但TB6612FNG更通用。选择它的考虑是驱动能力每路最大连续输出电流1.2A峰值2A对于42步进电机通常额定电流在1A左右足够使用。集成度高单模块可以驱动两台直流电机或一台两相步进电机内置PWM调速和方向控制逻辑外围电路简单。保护功能具有热关断和欠压锁定保护比早期常用的L298N模块效率更高、发热更小。接口友好标准排针接口与Arduino连接方便。控制步进电机需要用到模块的AIN1/AIN2控制A相、BIN1/BIN2控制B相以及PWMA/PWMB用于微步进控制或使能本项目作为步进电机使用时通常将PWMA、PWMB接高电平通过AIN/BIN控制。实操心得TB6612FNG模块的VM电机电源和VCC逻辑电源是分开的。务必为VM提供步进电机所需的电压如12V而为VCC提供与Arduino逻辑电平匹配的电压5V。这既能保证电机有足够动力又能确保控制信号正确。2.4 人机交互输入设备的选择为了让系统可控需要简单的人机交互界面电位器 (Trimpot)用于无级设定投石臂的后拉角度。我选用了一个10kΩ的多圈精密电位器。多圈电位器比单圈的调节更精细可以更精确地设定发射力度。将其两端接5V和GND中间抽头接Arduino的模拟输入引脚如A0。Arduino读取0-1023的模拟值并映射到步进电机需要运行的步数上。按钮 (Pushbutton)三个常开式按钮分别用于“开始装填”黄、“发射”绿和“急停”红。按钮一端接GND另一端接数字引脚并启用内部上拉电阻。这样未按下时引脚读高电平按下时读低电平电路简洁可靠。RGB LED用于状态指示。例如装填中亮蓝色装填完成并锁定后亮绿色故障或急停时亮红色。这能直观反映系统状态对于调试和操作非常有用。3. 机械结构设计与制作详解电路是神经机械是骨骼。一个稳固、顺滑的机械结构是项目成功的基础。我完全采用木制结构材料易得加工方便。3.1 投石臂 (Throwing Arm) 的制作与关键尺寸投石臂是整个系统的杠杆核心。我使用了一条截面约为2.5cm x 5cm1x2英寸的松木条长度约60cm。支点孔在距离臂尾配重端约13英寸33cm的位置钻一个直径6.35mm1/4英寸的通孔。这个位置就是杠杆的支点也是力臂比的关键。支点靠近配重端意味着抛射端会有更长的力臂获得更高的线速度但需要更大的配重或更长的拉程。你需要根据你的配重和期望射程来微调这个点。我用台钻确保孔垂直于木条这样轴转动会更顺滑。抛射物篮挂钩在臂头抛射端钻一个小孔用于系挂抛射物篮通常用绳子编个小网兜。臂头我粘了一段切开的PVC管作为导向槽让抛射绳不会滑脱。配重篮连接点在臂尾端钻另一个6.35mm孔用于连接配重篮。我用了一根长约10英寸的1/4英寸全螺纹螺杆穿过这个孔两端用螺母锁紧螺杆本身作为悬挂配重篮的横轴。平衡与加固投石臂在反复受力下容易弯曲或开裂。我在支点孔附近和臂尾连接处用环氧树脂胶粘合了额外的木片进行加强。确保整个臂是笔直的否则发射时会产生不可预测的偏转。3.2 A型支架 (A-Frame) 与底座的搭建A型支架用于支撑投石臂的支点轴必须非常稳固。支架腿我用了两条厚约2cm、宽5cm、长40cm的木板。在每条木板顶端距离顶端约2.5cm处钻出6.35mm的孔。然后将两条木板呈A字形摆放底端分开约28cm以保持稳定用横撑固定。关键点两条腿上的孔必须严格对齐。我是将两条腿夹在一起后一次性钻通的保证了同心度。支点轴用一根光滑的钢轴如一根长的1/4英寸螺栓磨掉螺纹穿过投石臂的支点孔和A型支架的对应孔。在轴的两端使用垫片和开口销或锁紧螺母固定确保投石臂可以自由旋转但轴向窜动很小。在轴与木孔的接触面涂上一点润滑脂如白色锂基脂能显著减少摩擦。底座平台用木板制作一个坚实的底座将A型支架、齿轮箱平台、Arduino控制盒都固定在上面。底座要足够重或者在四角钻孔以便用螺栓固定在工作台上防止发射时整机倾覆。3.3 齿轮箱与绞盘系统这是实现自动装填的“变速箱”将步进电机的高速低扭矩旋转转换为绞盘所需的低速高扭矩旋转。齿轮箱设计原项目提供了3D打印的齿轮箱文件。其核心是一个减速齿轮组。步进电机输出轴连接小齿轮带动一个大齿轮实现一次减速。大齿轮的同轴再带动第二个小齿轮驱动最终输出轴上的另一个大齿轮实现二次减速。总减速比例如20:1或更高大大增加了输出扭矩使得步进电机能轻松拉动绳索。齿轮箱外壳保证了齿轮啮合准确并提供了安装孔。绞盘输出轴上安装一个直径约3/4英寸的线轴作为绞盘。缠绕其上的绳子我用了结实的尼龙绳末端系在投石臂的配重端。当步进电机通过齿轮箱带动绞盘旋转时绳子被收紧从而将投石臂拉下。机械锁销机构这是安全锁定的关键。在最终输出齿轮或绞盘轴上设计一个带缺口的“锁止盘”。一个舵机带动一根金属杆如自行车辐条或打印的锁销。当绞盘将投石臂拉到设定位置时舵机转动将锁销推入锁止盘的缺口从而物理上阻止齿轮反转即使步进电机断电配重也不会下落。装配要点齿轮啮合间隙要适中太紧阻力大太松会打齿、噪音大且可能跳齿。可以在齿轮轴上加微小垫片来调整。所有旋转轴处都要考虑润滑。我使用了特氟龙垫片和少量润滑脂。绞盘上的绳子要缠绕整齐避免叠压防止发射时快速释放卡住。3.4 配重篮与释放机构配重篮我用木条做了一个小篮子用铁链悬挂在投石臂尾端的螺杆上。篮子要结实方便增减配重块我用的是铸铁秤砣。重要安全提示配重篮下方和运动轨迹内绝对不要站人或放置易碎物品。发射时它下落的力量非常大。释放机构最简单的释放方式就是“拔锁”。控制锁销的舵机在收到发射指令时反向转动将锁销从锁止盘中抽出。此时配重重力瞬间克服绳索和轴承的微小摩擦力拉动投石臂快速回转完成发射。另一种更复杂的方案是使用第二个舵机控制一个“挡板”先移开挡板再让锁销被重力拉出这样对锁销舵机的扭矩要求更低。4. 电路连接与布线实战清晰的电路连接是稳定运行的保障。下面是根据原理图进行的实际接线指南和注意事项。4.1 Arduino引脚分配与接线表为了清晰我定义了以下引脚分配你可以根据自己的板子调整但在代码中要同步修改。元件引脚/连接连接到 Arduino说明电位器两端5V, GND提供参考电压中间抽头A0读取模拟值 (0-1023)按钮 (黄)一端GND另一端D2启用内部上拉检测低电平触发按钮 (绿)一端GND另一端D3启用内部上拉按钮 (红)一端GND另一端D4启用内部上拉RGB LED共阴极GND确认你的LED是共阴还是共阳红色引脚D5 (PWM)通过220Ω限流电阻绿色引脚D6 (PWM)通过220Ω限流电阻蓝色引脚D7 (PWM)通过220Ω限流电阻伺服电机1 (锁销)信号线 (橙/黄)D9标准PWM引脚电源线 (红)5V注意电流见下文地线 (棕/黑)GND伺服电机2 (释放)信号线D10标准PWM引脚电源线5V注意电流地线GNDTB6612FNG 驱动VM12V 电源正极电机电源独立供电VCCArduino 5V逻辑电源与Arduino共地GNDArduino GND必须共地AIN1D11控制步进电机A相AIN2D12控制步进电机A相BIN1D13控制步进电机B相BIN2D8控制步进电机B相PWMA, PWMB5V (或接Arduino PWM引脚)接高电平使能或接PWM做微步进AO1, AO2接步进电机A相两线顺序可调只影响转向BO1, BO2接步进电机B相两线顺序可调只影响转向步进电机4根线对应接驱动A、B两路输出需用万用表辨别绕组见下文12V电源正极TB6612FNG VM, 及12V输入正负极TB6612FNG GND, 及Arduino GND所有GND必须连接在一起4.2 关键接线细节与避坑指南电源管理 - 重中之重绝对不要只用USB给整个系统供电USB只能提供约500mA电流而步进电机和两个舵机同时工作可能超过1.5A。必须使用外部电源。我使用了一个12V 2A的直流电源适配器。电源分离12V电源正极接TB6612FNG的VM电机电源同时可以接一个12V风扇等如果需要。12V电源的负极GND必须与Arduino的GND、TB6612FNG的GND、以及所有传感器/舵机的GND连接在一起形成共同的参考地。舵机电源两个SG90舵机如果同时动作峰值电流可能达到1A以上。虽然可以接在Arduino的5V引脚上但强烈建议为舵机提供独立的5V电源例如一个5V 2A的降压模块从12V总电源降压得到或者至少从外部电源的5V输出取电避免大电流冲击导致Arduino复位或损坏。如果坚持使用Arduino的5V务必在电源正极线上加一个470μF或更大的电解电容以缓冲电流冲击。步进电机绕组辨别四线步进电机有两组线圈。用万用表电阻档测量相通的两根线是一组线圈。将这两根线接到驱动器的A相AO1, AO2另外两根接到B相。如果接反电机可能不转或力矩很小。交换同一组线圈的两根线会改变该相的旋转方向交换A、B两相的顺序也会改变整体转向。通过测试确定正确的接线顺序使绞盘收绳方向对应步进电机正转。信号线抗干扰驱动模块的控制线AIN1, AIN2等虽然电流小但步进电机运行时会产生电磁噪声。如果条件允许使用双绞线或屏蔽线连接这些信号线。按钮和电位器的连线不要太长并远离电机和电源线。急停按钮的接法急停按钮红色建议接成常闭触点并连接到Arduino的中断引脚如D2或D3。在代码中设置为下降沿触发中断。这样即使程序跑飞按下按钮断开电路也能立即触发中断执行急停函数切断电机使能可靠性最高。本示例为简化采用与其它按钮相同的常开接法通过主循环查询。5. 控制系统软件设计与代码剖析软件是项目的大脑负责协调所有硬件。代码不仅要实现功能更要健壮、安全。5.1 主程序逻辑与状态机我采用状态机 (State Machine)的编程模型来管理整个系统这是控制类项目的经典方法。系统可以处于以下几种状态IDLE (空闲)系统上电后的初始状态。RGB LED显示待机颜色如白色或蓝色呼吸。等待用户输入。LOADING (装填中)当黄色按钮被按下系统进入此状态。读取电位器值计算出所需步进电机步数。然后启动步进电机缓慢拉动绞盘。RGB LED显示运行中如蓝色常亮。在此状态下持续监控急停按钮和步进电机步数。LOCKED (已锁定)当步进电机走到预定步数后停止电机。然后控制“锁销舵机”转动到锁定位置插入锁止盘。RGB LED变为绿色常亮表示装填完成准备就绪。等待发射指令。FIRING (发射中)当绿色按钮在LOCKED状态下被按下系统进入此状态。首先控制“释放舵机”动作如果有然后控制“锁销舵机”反转拔出锁销。配重下落完成发射。RGB LED快速闪烁红色或发射特效。短暂延时后系统自动复位回IDLE状态。ESTOP (急停)在任何时候按下红色按钮系统立即进入此状态。立即停止步进电机切断驱动使能所有舵机回到安全位置。RGB LED显示红色常亮。需要手动复位如重启或长按某个按钮才能回到IDLE状态。这种状态机结构清晰通过一个全局状态变量如enum State来切换在loop()函数中用switch-case语句根据当前状态执行相应操作和状态转移判断避免了复杂的嵌套if-else逻辑。5.2 核心代码模块解析以下是基于Arduino框架的关键代码片段和解释。完整代码需包含引脚定义、状态枚举、变量声明、setup()和loop()函数。#include Servo.h #include AccelStepper.h // 使用AccelStepper库比Stepper库更强大 // 引脚定义 (根据你的实际接线修改) const int potPin A0; const int btnLoad 2, btnFire 3, btnStop 4; const int ledR 5, ledG 6, ledB 7; const int servoLockPin 9, servoReleasePin 10; // TB6612FNG引脚连接步进电机 const int motorA1 11, motorA2 12, motorB1 13, motorB2 8; // 状态定义 enum State { IDLE, LOADING, LOCKED, FIRING, ESTOP }; State currentState IDLE; // 对象初始化 Servo servoLock, servoRelease; // 定义步进电机驱动方式使用4线双极驱动模式 AccelStepper stepper(AccelStepper::FULL4WIRE, motorA1, motorA2, motorB1, motorB2); // 关键参数 const int maxSteps 2000; // 对应电位器最大值时步进电机最大步数需校准 const int lockAngle 90; // 锁销舵机锁定角度 const int unlockAngle 0; // 锁销舵机解锁角度 const int releaseAngle 180; // 释放舵机动作角度 int targetSteps 0; // 本次装填目标步数 bool loadingAborted false; void setup() { Serial.begin(9600); // 初始化引脚模式 pinMode(btnLoad, INPUT_PULLUP); pinMode(btnFire, INPUT_PULLUP); pinMode(btnStop, INPUT_PULLUP); pinMode(ledR, OUTPUT); pinMode(ledG, OUTPUT); pinMode(ledB, OUTPUT); // 初始化舵机 servoLock.attach(servoLockPin); servoRelease.attach(servoReleasePin); servoLock.write(unlockAngle); // 初始位置解锁 servoRelease.write(0); // 初始位置收回 // 初始化步进电机 stepper.setMaxSpeed(500.0); // 最大速度 (步/秒)不宜过快 stepper.setAcceleration(200.0); // 加速度 (步/秒^2)使启动停止平滑 stepper.setCurrentPosition(0); // 重置位置为0 // 初始状态指示 setColor(0, 0, 255); // 蓝色表示就绪 Serial.println(System Ready. Turn pot and press YELLOW to load.); } void loop() { // 急停按钮具有最高优先级在任何状态都检查 if (digitalRead(btnStop) LOW) { emergencyStop(); return; // 跳出本次loop停留在ESTOP状态 } switch (currentState) { case IDLE: setColor(0, 0, 255); // 蓝色 // 检测装填按钮 if (digitalRead(btnLoad) LOW) { delay(50); // 简单防抖 if (digitalRead(btnLoad) LOW) { startLoadingSequence(); } } break; case LOADING: setColor(0, 255, 255); // 青色表示运行中 // 驱动步进电机向目标位置运动 stepper.run(); // 检查是否到达目标位置 if (stepper.distanceToGo() 0) { completeLoading(); } // 在loading状态下急停按钮已在上方检查 break; case LOCKED: setColor(0, 255, 0); // 绿色表示已锁定 // 检测发射按钮 if (digitalRead(btnFire) LOW) { delay(50); if (digitalRead(btnFire) LOW) { fireSequence(); } } break; case FIRING: // 发射动画或指示短暂延时后回到IDLE setColor(255, 0, 0); delay(100); setColor(0, 0, 0); delay(100); // 发射动作在fireSequence()中已完成这里只是指示 // 等待一段时间让抛射完成然后复位 delay(1000); // 给物理过程留出时间 resetToIdle(); break; case ESTOP: // 急停状态红灯常亮等待外部复位如重启 setColor(255, 0, 0); // 这里可以加入等待复位按钮的逻辑 break; } } void startLoadingSequence() { // 1. 读取电位器值映射到目标步数 int potValue analogRead(potPin); targetSteps map(potValue, 0, 1023, 0, maxSteps); Serial.print(Start Loading. Target Steps: ); Serial.println(targetSteps); // 2. 确保锁销处于解锁位置 servoLock.write(unlockAngle); delay(500); // 等待舵机到位 // 3. 设置步进电机目标位置相对当前位置 stepper.moveTo(targetSteps); // moveTo是绝对位置move是相对位置 // 如果用相对位置stepper.move(targetSteps); // 4. 切换状态 currentState LOADING; } void completeLoading() { Serial.println(Loading Complete. Locking...); // 1. 停止步进电机AccelStepper在到达位置后自动停止 // 2. 执行锁定 servoLock.write(lockAngle); delay(500); // 等待锁定完成 // 3. 切换状态 currentState LOCKED; Serial.println(Locked and Ready to Fire.); } void fireSequence() { Serial.println(Firing!); // 1. 执行释放动作如果有第二个舵机 servoRelease.write(releaseAngle); delay(300); // 2. 解锁 servoLock.write(unlockAngle); delay(300); // 给锁销收回留出时间此时配重应已开始下落 // 3. 切换状态进入发射指示阶段 currentState FIRING; } void emergencyStop() { Serial.println(EMERGENCY STOP!); // 1. 立即停止步进电机设置目标位置为当前位置 stepper.stop(); // AccelStepper的stop()函数 stepper.disableOutputs(); // 关闭电机输出节省电力/减少发热 // 2. 舵机回到安全位置解锁 servoLock.write(unlockAngle); servoRelease.write(0); // 3. 状态切换 currentState ESTOP; setColor(255, 0, 0); // 红色常亮 // 4. 这里可以加入蜂鸣器报警 } void resetToIdle() { // 复位步进电机位置可选物理位置可能已变 // stepper.setCurrentPosition(0); // 谨慎使用需配合机械回零 servoLock.write(unlockAngle); servoRelease.write(0); currentState IDLE; Serial.println(System Reset to IDLE.); } // 辅助函数设置RGB LED颜色 void setColor(int red, int green, int blue) { analogWrite(ledR, 255 - red); // 如果是共阳极则不需要255- analogWrite(ledG, 255 - green); analogWrite(ledB, 255 - blue); }5.3 关键参数校准与调试技巧代码中的一些参数需要根据你的具体硬件进行校准maxSteps(最大步数)这是最关键的一个参数。它表示电位器旋到最大时步进电机需要走多少步才能将投石臂拉到安全范围内的最大角度。校准方法先将电位器置于中间位置上传一个简单的测试程序让电机走一个较小的步数如200步。观察绞盘收绳长度和投石臂角度变化。手动测量从初始位置到你希望的最大发射角度注意不要超过结构承受极限步进电机总共需要的步数。这个值就是maxSteps。务必留有余量防止过度拉拽损坏结构。步进电机速度与加速度 (setMaxSpeed,setAcceleration)setMaxSpeed速度太快可能导致步进电机失步发出的脉冲数多于实际走的步数表现为拉不到位或位置不准。从较低值如200步/秒开始测试逐步增加直到找到在负载下稳定运行的最大速度。setAcceleration加速度设置使电机启动和停止更平滑减少对机械结构的冲击。值太小则动作缓慢太大可能引起振动或失步。200-400是一个不错的起始范围。舵机角度 (lockAngle,unlockAngle)这需要根据你实际安装的锁销机构来确定。上传一个测试程序让舵机在0-180度间扫描观察锁销的运动轨迹找到能可靠插入锁止盘和完全退出的两个角度。务必确保在解锁位置锁销完全脱离齿轮运动路径否则发射时会卡住。防抖动处理代码中使用了简单的delay(50)进行按钮防抖。对于要求更高的场合可以使用状态检测法记录按下时间只有持续低电平超过一定时间才视为有效或中断配合计时器的方式避免误触发。6. 系统集成、测试与安全规范当所有硬件组装完毕代码也上传后就到了最激动人心也最需要谨慎的集成测试阶段。6.1 分阶段测试流程绝对不要一上来就挂配重测试遵循以下顺序步步为营通电前目视检查所有电线连接是否正确、牢固特别是电源正负极有没有接反螺丝、螺母是否拧紧结构件有无明显松动运动部件投石臂、齿轮、绞盘的转动是否顺畅有无干涉空载上电测试不挂配重不装抛射物仅连接Arduino和驱动板电源不接步进电机和舵机。测试按钮、电位器、LED功能是否正常。通过串口监视器查看打印的状态和电位器读数。接上舵机测试锁定和释放动作是否平滑、到位。用手轻轻推动锁销机构感受舵机扭矩是否足够。接上步进电机但将绳子从绞盘上取下。测试步进电机是否能正反转响应电位器控制。听电机声音是否平稳有无尖锐噪音或堵转声。带载测试装绳子不挂重物将绳子系在投石臂上但配重篮空载。进行完整的“装填-锁定”循环。观察步进电机拉绳是否平稳绞盘收绳是否整齐锁销能否可靠锁入。测试“发射”指令观察锁销能否顺利拔出投石臂在空载下是否能回弹。轻载测试小配重在配重篮中放入少量重物如一本厚书。重复测试流程。这是检验机械结构和电机扭矩的关键一步。注意听齿轮箱和电机的声音如果出现异常噪音或电机停转立即急停。测量不同电位器位置对应的投石臂角度验证控制线性度。全载测试与校准逐步增加配重至设计值。每次增加重量后都重复测试。进行发射测试放入一个轻软的安全抛射物如海绵球。所有人员站在侧面或后方绝对不要在发射轨迹前方根据射程微调电位器映射关系map函数或机械结构如绳子悬挂点。6.2 常见故障排查速查表现象可能原因排查步骤上电无反应电源未接通Arduino损坏短路保护。1. 检查电源开关、插头。2. 用万用表测量Arduino Vin/5V引脚电压。3. 检查有无短路点特别是电源线。步进电机不转驱动模块未使能接线错误电机绕组接错电流太小。1. 检查TB6612FNG的PWMA/PWMB是否接高。2. 用万用表确认电机绕组。3. 调整驱动模块上的电流调节电位器如果有。4. 用手转动电机轴如果很紧可能是电流过大。步进电机失步拉不到位电机扭矩不足速度或加速度设置过高机械阻力过大电源电压不足。1. 降低setMaxSpeed和setAcceleration。2. 检查12V电源是否足额测量带载时的电压。3. 检查齿轮箱、轴承是否润滑良好有无卡滞。4. 尝试增大驱动电流注意散热。舵机不动作或抖动电源功率不足信号线接触不良舵机损坏角度指令超出范围。1.首要检查用独立5V电源给舵机供电。2. 检查信号线连接。3. 用servo.write()测试0-180度内多个角度。4. 单独测试舵机。锁销无法锁住或锁不紧舵机扭矩不足锁销与锁止盘未对准机械结构有间隙。1. 检查舵机在锁定位置是否被外力推动断电状态下用手试。2. 调整舵机安装位置或锁销形状确保垂直插入。3. 使用扭矩更大的舵机如MG90S。按钮或电位器读数不稳定接触不良内部上拉电阻未启用或失效干扰。1. 检查接线和焊点。2. 确认代码中使用了INPUT_PULLUP。3. 在模拟输入引脚对地加一个0.1uF电容滤波。4. 在代码中加入软件滤波如多次采样取平均。发射后系统不复位急停按钮被意外触发状态机逻辑错误代码跑飞。1. 检查急停按钮接线是否松动是否为常闭接法。2. 在loop()中每个状态分支后添加串口打印跟踪状态流。3. 检查是否有数组越界、除零等可能导致崩溃的操作。6.3 至关重要的安全规范这是一个动力机械项目安全必须放在首位。个人防护在加工木材、金属特别是钻孔、切割时务必佩戴护目镜。调试电机和机械结构时不要穿宽松的衣服长发要扎起。测试区域清场发射测试时确保前方及可能的反弹区域没有任何人、宠物或易碎物品。最好在户外空旷场地进行。逐步增加能量始终从空载、轻载开始测试逐步增加配重。永远不要一开始就使用最大设计配重。急停功能确保急停按钮随时可及且功能100%可靠。定期测试它。结构检查每次测试前后检查所有木制接合处、螺丝、轴销是否有松动、开裂迹象。疲劳是金属和木材的敌人。电力安全注意12V电源线不要被运动部件磨损。所有裸露的电气连接点应用热缩管或绝缘胶带包好。不要在系统通电时进行接线操作。抛射物永远不要使用坚硬、尖锐或过重的物体作为抛射物进行测试。初期使用海绵球、网球等安全物体。7. 项目优化与扩展思路当基础功能稳定实现后你可以考虑以下优化和扩展让项目更智能、更强大。7.1 硬件层面的优化增加位置反馈目前步进电机是开环控制。可以在绞盘轴或投石臂上安装一个旋转编码器或电位器实时反馈实际位置形成闭环控制。这样即使偶尔失步系统也能感知并纠正精度和可靠性大幅提升。升级动力系统如果发现扭矩不足可以升级步进电机如更大型号的57电机或使用减速比更大的齿轮箱。对于锁销舵机如果感觉力度不够可以换用金属齿轮的舵机如MG996R。加入限位开关在投石臂运动的起点和终点安装微动开关或光电传感器作为“归零”或“极限位置”保护防止过度运行损坏机构。改进人机交互用一个小型OLED显示屏替代RGB LED可以显示当前角度、设定角度、系统状态、电池电压等信息。加入旋钮编码器来替代电位器操作更精准。7.2 软件层面的增强实现闭环控制如果加了编码器可以使用PID控制算法来让步进电机更平滑、精确地到达目标位置并能抵抗外力干扰。增加发射模式除了手动设定角度可以编程实现“固定角度连发”、“角度递增连发”等模式。数据记录与分析通过蓝牙或Wi-Fi模块如ESP-01s或HC-05将每次发射的角度、电机电流等数据发送到电脑或手机用于分析性能和优化参数。加入安全自检上电时系统自动执行一系列自检舵机是否回位、限位开关是否正常、电位器读数是否在合理范围等。7.3 机械与功能扩展自动装弹机构在抛射端增加一个由小型舵机或直流电机驱动的“弹仓”和“推弹杆”实现发射后自动装载下一个抛射物实现全自动连发。方向旋转平台将整个投石机安装在一个由另一个步进电机驱动的转台上实现水平方向的自动瞄准。弹道计算与自动瞄准结合测距传感器如超声波或激光测距和倾角传感器输入目标距离后Arduino自动计算所需的发射角度考虑抛物线弹道并控制步进电机拉到对应位置。这就真正迈向智能化了。这个项目从一块木头、一块电路板开始到最终成为一个能够听从指令、自动完成复杂机械动作的系统整个过程充满了挑战和乐趣。它深刻地展示了如何将软件逻辑与硬件实体结合起来去解决一个具体的物理问题。最大的收获不是最终能抛多远而是在调试过程中对电机特性、机械公差、控制时序、安全规范这些工程细节的切身理解。当你按下按钮听到齿轮啮合的声音看到锁销精准到位最后配重落下、抛射物划出弧线的那一刻所有的调试和折腾都值了。希望这份详细的记录能帮你绕过我踩过的那些坑更顺利地打造出属于你自己的自动投石机。记住安全第一享受创造的过程。
基于Arduino与步进电机的自动投石机:机电一体化实践全解析
1. 项目概述与核心思路如果你对机械传动和嵌入式控制感兴趣那么亲手打造一个能自动装填、一键发射的投石机绝对是个能让你玩上瘾的项目。这不仅仅是把木头和电机拼在一起它融合了结构力学、运动控制和实时逻辑判断是一个典型的机电一体化实践案例。我这次做的这个“自动装填投石机”核心目标就是让这个古老而经典的攻城器械摆脱纯手动操作的繁琐通过Arduino作为大脑步进电机和伺服电机作为肌肉实现从设定、装填到发射的全流程自动化。整个系统的运作逻辑很清晰你通过一个电位器旋钮来设定投石臂的后拉角度这直接决定了发射的力度和射程然后按下黄色按钮系统就开始自动工作。步进电机会通过一个绞盘和绳索稳定而有力地将投石臂的后端配重篮一侧拉下来完成“上弦”动作。在这个过程中一个伺服电机会转动将一个物理锁销插入齿轮箱的特定齿位将拉紧的投石臂牢牢锁住等待发射。此时系统进入“已装填”状态。当你按下绿色按钮另一个伺服电机会动作将锁销拔出蓄势待发的配重篮在重力作用下急速下坠通过杠杆臂将另一端的抛射物高速抛出。任何时候觉得不对劲红色急停按钮都能立刻中断装填过程确保安全。这个项目最有价值的地方在于它把几个关键的工程模块串联了起来机械结构设计杠杆、轴承、锁止机构、精密运动控制步进电机的开环位置控制、快速响应执行伺服电机的角度控制以及上层逻辑管理Arduino处理按钮、传感器输入并协调所有执行器。下面我就把这几个月从画图、切木头、调试代码到最终让机器可靠运行的全过程包括中间踩过的坑和总结出的技巧毫无保留地分享出来。2. 核心部件选型与功能解析工欲善其事必先利其器。一个稳定可靠的自动化项目从选择合适的核心部件开始。这里的每一个选择都直接关系到最终系统的性能、可靠性和成本。2.1 控制核心为什么是Arduino Uno对于这个项目我选择了经典的Arduino Uno R3。原因很简单生态成熟、资源丰富、完全够用。Uno基于ATmega328P微控制器有14个数字I/O口其中6个可做PWM输出和6个模拟输入口对于控制两个伺服电机、一个步进电机驱动器、三个按钮和一个电位器来说绰绰有余。它的5V逻辑电平与我们将要使用的伺服电机、按钮等完美兼容。虽然像Nano体积更小但Uuno在调试阶段插拔线缆更方便也有利于初学者理解电路连接。市面上兼容板很多选择正版或口碑好的兼容板即可确保USB芯片稳定避免驱动问题。注意务必确认你使用的开发板是5V逻辑电平。有些3.3V逻辑的板子如某些ESP8266开发板虽然引脚兼容但直接驱动5V器件可能存在问题或损坏风险。2.2 动力肌肉步进电机与伺服电机的分工这是项目的核心执行机构它们的特性决定了分工。步进电机 (Stepper Motor) - 负责“装填”的精准拉力我选用的是42步进电机型号SL42STH40-1684A这是一个非常通用的型号。它的关键参数是1.8°步距角。这意味着电机旋转一圈需要360°/1.8° 200步。配合微步进驱动器可以实现更高的分辨率。我选择它的理由是开环控制位置精确无需编码器反馈只要不发生失步后面会讲如何避免控制器发出的脉冲数就严格对应电机轴转过的角度。这对于需要精确控制拉绳长度的装填过程至关重要。低速大扭矩步进电机在低速时能保持很大的扭矩非常适合这种需要缓慢、稳定施加拉力的场景。我们不需要它高速旋转但需要它能稳稳地拉起配重。保持扭矩通电但静止时步进电机能产生保持扭矩这对于在装填中途暂停或锁定前维持状态有好处。原项目提到“12V 200 steps/revolution equivalent”指的就是这类最常用的1.8°两相四线步进电机。你完全可以用其他品牌同规格电机替代。伺服电机 (Servo Motor) - 负责“锁定”与“释放”的快速动作我使用了两个SG90 9g微型伺服电机。这种舵机内部集成了电机、减速齿轮组和控制电路接收PWM信号来控制输出轴的角度通常0-180°。它的特点是闭环控制舵机内部有电位器反馈控制器会不断调整直到轴到达指令位置位置控制简单可靠。响应速度快从收到信号到转动到指定角度时间很短非常适合做“锁定”和“释放”这种需要快速、准确到位的动作。接口简单只需要一根PWM信号线以及电源和地线Arduino有现成的Servo库驱动极易上手。一个舵机用于控制齿轮箱的机械锁销在步进电机拉到位后转动一个角度将锁销插入齿轮防止回转。另一个舵机则用于控制一个释放机构如挡板或第二道锁在发射指令下达时快速移开障碍让锁销能在配重重力作用下被弹出或直接拉动锁销复位。2.3 动力桥梁TB6612FNG双路电机驱动模块Arduino的I/O口无法直接驱动步进电机需要专用的驱动模块。我选择了SparkFun的TB6612FNG双路直流电机驱动模块也可用常见的DRV8825或A4988步进电机驱动模块但TB6612FNG更通用。选择它的考虑是驱动能力每路最大连续输出电流1.2A峰值2A对于42步进电机通常额定电流在1A左右足够使用。集成度高单模块可以驱动两台直流电机或一台两相步进电机内置PWM调速和方向控制逻辑外围电路简单。保护功能具有热关断和欠压锁定保护比早期常用的L298N模块效率更高、发热更小。接口友好标准排针接口与Arduino连接方便。控制步进电机需要用到模块的AIN1/AIN2控制A相、BIN1/BIN2控制B相以及PWMA/PWMB用于微步进控制或使能本项目作为步进电机使用时通常将PWMA、PWMB接高电平通过AIN/BIN控制。实操心得TB6612FNG模块的VM电机电源和VCC逻辑电源是分开的。务必为VM提供步进电机所需的电压如12V而为VCC提供与Arduino逻辑电平匹配的电压5V。这既能保证电机有足够动力又能确保控制信号正确。2.4 人机交互输入设备的选择为了让系统可控需要简单的人机交互界面电位器 (Trimpot)用于无级设定投石臂的后拉角度。我选用了一个10kΩ的多圈精密电位器。多圈电位器比单圈的调节更精细可以更精确地设定发射力度。将其两端接5V和GND中间抽头接Arduino的模拟输入引脚如A0。Arduino读取0-1023的模拟值并映射到步进电机需要运行的步数上。按钮 (Pushbutton)三个常开式按钮分别用于“开始装填”黄、“发射”绿和“急停”红。按钮一端接GND另一端接数字引脚并启用内部上拉电阻。这样未按下时引脚读高电平按下时读低电平电路简洁可靠。RGB LED用于状态指示。例如装填中亮蓝色装填完成并锁定后亮绿色故障或急停时亮红色。这能直观反映系统状态对于调试和操作非常有用。3. 机械结构设计与制作详解电路是神经机械是骨骼。一个稳固、顺滑的机械结构是项目成功的基础。我完全采用木制结构材料易得加工方便。3.1 投石臂 (Throwing Arm) 的制作与关键尺寸投石臂是整个系统的杠杆核心。我使用了一条截面约为2.5cm x 5cm1x2英寸的松木条长度约60cm。支点孔在距离臂尾配重端约13英寸33cm的位置钻一个直径6.35mm1/4英寸的通孔。这个位置就是杠杆的支点也是力臂比的关键。支点靠近配重端意味着抛射端会有更长的力臂获得更高的线速度但需要更大的配重或更长的拉程。你需要根据你的配重和期望射程来微调这个点。我用台钻确保孔垂直于木条这样轴转动会更顺滑。抛射物篮挂钩在臂头抛射端钻一个小孔用于系挂抛射物篮通常用绳子编个小网兜。臂头我粘了一段切开的PVC管作为导向槽让抛射绳不会滑脱。配重篮连接点在臂尾端钻另一个6.35mm孔用于连接配重篮。我用了一根长约10英寸的1/4英寸全螺纹螺杆穿过这个孔两端用螺母锁紧螺杆本身作为悬挂配重篮的横轴。平衡与加固投石臂在反复受力下容易弯曲或开裂。我在支点孔附近和臂尾连接处用环氧树脂胶粘合了额外的木片进行加强。确保整个臂是笔直的否则发射时会产生不可预测的偏转。3.2 A型支架 (A-Frame) 与底座的搭建A型支架用于支撑投石臂的支点轴必须非常稳固。支架腿我用了两条厚约2cm、宽5cm、长40cm的木板。在每条木板顶端距离顶端约2.5cm处钻出6.35mm的孔。然后将两条木板呈A字形摆放底端分开约28cm以保持稳定用横撑固定。关键点两条腿上的孔必须严格对齐。我是将两条腿夹在一起后一次性钻通的保证了同心度。支点轴用一根光滑的钢轴如一根长的1/4英寸螺栓磨掉螺纹穿过投石臂的支点孔和A型支架的对应孔。在轴的两端使用垫片和开口销或锁紧螺母固定确保投石臂可以自由旋转但轴向窜动很小。在轴与木孔的接触面涂上一点润滑脂如白色锂基脂能显著减少摩擦。底座平台用木板制作一个坚实的底座将A型支架、齿轮箱平台、Arduino控制盒都固定在上面。底座要足够重或者在四角钻孔以便用螺栓固定在工作台上防止发射时整机倾覆。3.3 齿轮箱与绞盘系统这是实现自动装填的“变速箱”将步进电机的高速低扭矩旋转转换为绞盘所需的低速高扭矩旋转。齿轮箱设计原项目提供了3D打印的齿轮箱文件。其核心是一个减速齿轮组。步进电机输出轴连接小齿轮带动一个大齿轮实现一次减速。大齿轮的同轴再带动第二个小齿轮驱动最终输出轴上的另一个大齿轮实现二次减速。总减速比例如20:1或更高大大增加了输出扭矩使得步进电机能轻松拉动绳索。齿轮箱外壳保证了齿轮啮合准确并提供了安装孔。绞盘输出轴上安装一个直径约3/4英寸的线轴作为绞盘。缠绕其上的绳子我用了结实的尼龙绳末端系在投石臂的配重端。当步进电机通过齿轮箱带动绞盘旋转时绳子被收紧从而将投石臂拉下。机械锁销机构这是安全锁定的关键。在最终输出齿轮或绞盘轴上设计一个带缺口的“锁止盘”。一个舵机带动一根金属杆如自行车辐条或打印的锁销。当绞盘将投石臂拉到设定位置时舵机转动将锁销推入锁止盘的缺口从而物理上阻止齿轮反转即使步进电机断电配重也不会下落。装配要点齿轮啮合间隙要适中太紧阻力大太松会打齿、噪音大且可能跳齿。可以在齿轮轴上加微小垫片来调整。所有旋转轴处都要考虑润滑。我使用了特氟龙垫片和少量润滑脂。绞盘上的绳子要缠绕整齐避免叠压防止发射时快速释放卡住。3.4 配重篮与释放机构配重篮我用木条做了一个小篮子用铁链悬挂在投石臂尾端的螺杆上。篮子要结实方便增减配重块我用的是铸铁秤砣。重要安全提示配重篮下方和运动轨迹内绝对不要站人或放置易碎物品。发射时它下落的力量非常大。释放机构最简单的释放方式就是“拔锁”。控制锁销的舵机在收到发射指令时反向转动将锁销从锁止盘中抽出。此时配重重力瞬间克服绳索和轴承的微小摩擦力拉动投石臂快速回转完成发射。另一种更复杂的方案是使用第二个舵机控制一个“挡板”先移开挡板再让锁销被重力拉出这样对锁销舵机的扭矩要求更低。4. 电路连接与布线实战清晰的电路连接是稳定运行的保障。下面是根据原理图进行的实际接线指南和注意事项。4.1 Arduino引脚分配与接线表为了清晰我定义了以下引脚分配你可以根据自己的板子调整但在代码中要同步修改。元件引脚/连接连接到 Arduino说明电位器两端5V, GND提供参考电压中间抽头A0读取模拟值 (0-1023)按钮 (黄)一端GND另一端D2启用内部上拉检测低电平触发按钮 (绿)一端GND另一端D3启用内部上拉按钮 (红)一端GND另一端D4启用内部上拉RGB LED共阴极GND确认你的LED是共阴还是共阳红色引脚D5 (PWM)通过220Ω限流电阻绿色引脚D6 (PWM)通过220Ω限流电阻蓝色引脚D7 (PWM)通过220Ω限流电阻伺服电机1 (锁销)信号线 (橙/黄)D9标准PWM引脚电源线 (红)5V注意电流见下文地线 (棕/黑)GND伺服电机2 (释放)信号线D10标准PWM引脚电源线5V注意电流地线GNDTB6612FNG 驱动VM12V 电源正极电机电源独立供电VCCArduino 5V逻辑电源与Arduino共地GNDArduino GND必须共地AIN1D11控制步进电机A相AIN2D12控制步进电机A相BIN1D13控制步进电机B相BIN2D8控制步进电机B相PWMA, PWMB5V (或接Arduino PWM引脚)接高电平使能或接PWM做微步进AO1, AO2接步进电机A相两线顺序可调只影响转向BO1, BO2接步进电机B相两线顺序可调只影响转向步进电机4根线对应接驱动A、B两路输出需用万用表辨别绕组见下文12V电源正极TB6612FNG VM, 及12V输入正负极TB6612FNG GND, 及Arduino GND所有GND必须连接在一起4.2 关键接线细节与避坑指南电源管理 - 重中之重绝对不要只用USB给整个系统供电USB只能提供约500mA电流而步进电机和两个舵机同时工作可能超过1.5A。必须使用外部电源。我使用了一个12V 2A的直流电源适配器。电源分离12V电源正极接TB6612FNG的VM电机电源同时可以接一个12V风扇等如果需要。12V电源的负极GND必须与Arduino的GND、TB6612FNG的GND、以及所有传感器/舵机的GND连接在一起形成共同的参考地。舵机电源两个SG90舵机如果同时动作峰值电流可能达到1A以上。虽然可以接在Arduino的5V引脚上但强烈建议为舵机提供独立的5V电源例如一个5V 2A的降压模块从12V总电源降压得到或者至少从外部电源的5V输出取电避免大电流冲击导致Arduino复位或损坏。如果坚持使用Arduino的5V务必在电源正极线上加一个470μF或更大的电解电容以缓冲电流冲击。步进电机绕组辨别四线步进电机有两组线圈。用万用表电阻档测量相通的两根线是一组线圈。将这两根线接到驱动器的A相AO1, AO2另外两根接到B相。如果接反电机可能不转或力矩很小。交换同一组线圈的两根线会改变该相的旋转方向交换A、B两相的顺序也会改变整体转向。通过测试确定正确的接线顺序使绞盘收绳方向对应步进电机正转。信号线抗干扰驱动模块的控制线AIN1, AIN2等虽然电流小但步进电机运行时会产生电磁噪声。如果条件允许使用双绞线或屏蔽线连接这些信号线。按钮和电位器的连线不要太长并远离电机和电源线。急停按钮的接法急停按钮红色建议接成常闭触点并连接到Arduino的中断引脚如D2或D3。在代码中设置为下降沿触发中断。这样即使程序跑飞按下按钮断开电路也能立即触发中断执行急停函数切断电机使能可靠性最高。本示例为简化采用与其它按钮相同的常开接法通过主循环查询。5. 控制系统软件设计与代码剖析软件是项目的大脑负责协调所有硬件。代码不仅要实现功能更要健壮、安全。5.1 主程序逻辑与状态机我采用状态机 (State Machine)的编程模型来管理整个系统这是控制类项目的经典方法。系统可以处于以下几种状态IDLE (空闲)系统上电后的初始状态。RGB LED显示待机颜色如白色或蓝色呼吸。等待用户输入。LOADING (装填中)当黄色按钮被按下系统进入此状态。读取电位器值计算出所需步进电机步数。然后启动步进电机缓慢拉动绞盘。RGB LED显示运行中如蓝色常亮。在此状态下持续监控急停按钮和步进电机步数。LOCKED (已锁定)当步进电机走到预定步数后停止电机。然后控制“锁销舵机”转动到锁定位置插入锁止盘。RGB LED变为绿色常亮表示装填完成准备就绪。等待发射指令。FIRING (发射中)当绿色按钮在LOCKED状态下被按下系统进入此状态。首先控制“释放舵机”动作如果有然后控制“锁销舵机”反转拔出锁销。配重下落完成发射。RGB LED快速闪烁红色或发射特效。短暂延时后系统自动复位回IDLE状态。ESTOP (急停)在任何时候按下红色按钮系统立即进入此状态。立即停止步进电机切断驱动使能所有舵机回到安全位置。RGB LED显示红色常亮。需要手动复位如重启或长按某个按钮才能回到IDLE状态。这种状态机结构清晰通过一个全局状态变量如enum State来切换在loop()函数中用switch-case语句根据当前状态执行相应操作和状态转移判断避免了复杂的嵌套if-else逻辑。5.2 核心代码模块解析以下是基于Arduino框架的关键代码片段和解释。完整代码需包含引脚定义、状态枚举、变量声明、setup()和loop()函数。#include Servo.h #include AccelStepper.h // 使用AccelStepper库比Stepper库更强大 // 引脚定义 (根据你的实际接线修改) const int potPin A0; const int btnLoad 2, btnFire 3, btnStop 4; const int ledR 5, ledG 6, ledB 7; const int servoLockPin 9, servoReleasePin 10; // TB6612FNG引脚连接步进电机 const int motorA1 11, motorA2 12, motorB1 13, motorB2 8; // 状态定义 enum State { IDLE, LOADING, LOCKED, FIRING, ESTOP }; State currentState IDLE; // 对象初始化 Servo servoLock, servoRelease; // 定义步进电机驱动方式使用4线双极驱动模式 AccelStepper stepper(AccelStepper::FULL4WIRE, motorA1, motorA2, motorB1, motorB2); // 关键参数 const int maxSteps 2000; // 对应电位器最大值时步进电机最大步数需校准 const int lockAngle 90; // 锁销舵机锁定角度 const int unlockAngle 0; // 锁销舵机解锁角度 const int releaseAngle 180; // 释放舵机动作角度 int targetSteps 0; // 本次装填目标步数 bool loadingAborted false; void setup() { Serial.begin(9600); // 初始化引脚模式 pinMode(btnLoad, INPUT_PULLUP); pinMode(btnFire, INPUT_PULLUP); pinMode(btnStop, INPUT_PULLUP); pinMode(ledR, OUTPUT); pinMode(ledG, OUTPUT); pinMode(ledB, OUTPUT); // 初始化舵机 servoLock.attach(servoLockPin); servoRelease.attach(servoReleasePin); servoLock.write(unlockAngle); // 初始位置解锁 servoRelease.write(0); // 初始位置收回 // 初始化步进电机 stepper.setMaxSpeed(500.0); // 最大速度 (步/秒)不宜过快 stepper.setAcceleration(200.0); // 加速度 (步/秒^2)使启动停止平滑 stepper.setCurrentPosition(0); // 重置位置为0 // 初始状态指示 setColor(0, 0, 255); // 蓝色表示就绪 Serial.println(System Ready. Turn pot and press YELLOW to load.); } void loop() { // 急停按钮具有最高优先级在任何状态都检查 if (digitalRead(btnStop) LOW) { emergencyStop(); return; // 跳出本次loop停留在ESTOP状态 } switch (currentState) { case IDLE: setColor(0, 0, 255); // 蓝色 // 检测装填按钮 if (digitalRead(btnLoad) LOW) { delay(50); // 简单防抖 if (digitalRead(btnLoad) LOW) { startLoadingSequence(); } } break; case LOADING: setColor(0, 255, 255); // 青色表示运行中 // 驱动步进电机向目标位置运动 stepper.run(); // 检查是否到达目标位置 if (stepper.distanceToGo() 0) { completeLoading(); } // 在loading状态下急停按钮已在上方检查 break; case LOCKED: setColor(0, 255, 0); // 绿色表示已锁定 // 检测发射按钮 if (digitalRead(btnFire) LOW) { delay(50); if (digitalRead(btnFire) LOW) { fireSequence(); } } break; case FIRING: // 发射动画或指示短暂延时后回到IDLE setColor(255, 0, 0); delay(100); setColor(0, 0, 0); delay(100); // 发射动作在fireSequence()中已完成这里只是指示 // 等待一段时间让抛射完成然后复位 delay(1000); // 给物理过程留出时间 resetToIdle(); break; case ESTOP: // 急停状态红灯常亮等待外部复位如重启 setColor(255, 0, 0); // 这里可以加入等待复位按钮的逻辑 break; } } void startLoadingSequence() { // 1. 读取电位器值映射到目标步数 int potValue analogRead(potPin); targetSteps map(potValue, 0, 1023, 0, maxSteps); Serial.print(Start Loading. Target Steps: ); Serial.println(targetSteps); // 2. 确保锁销处于解锁位置 servoLock.write(unlockAngle); delay(500); // 等待舵机到位 // 3. 设置步进电机目标位置相对当前位置 stepper.moveTo(targetSteps); // moveTo是绝对位置move是相对位置 // 如果用相对位置stepper.move(targetSteps); // 4. 切换状态 currentState LOADING; } void completeLoading() { Serial.println(Loading Complete. Locking...); // 1. 停止步进电机AccelStepper在到达位置后自动停止 // 2. 执行锁定 servoLock.write(lockAngle); delay(500); // 等待锁定完成 // 3. 切换状态 currentState LOCKED; Serial.println(Locked and Ready to Fire.); } void fireSequence() { Serial.println(Firing!); // 1. 执行释放动作如果有第二个舵机 servoRelease.write(releaseAngle); delay(300); // 2. 解锁 servoLock.write(unlockAngle); delay(300); // 给锁销收回留出时间此时配重应已开始下落 // 3. 切换状态进入发射指示阶段 currentState FIRING; } void emergencyStop() { Serial.println(EMERGENCY STOP!); // 1. 立即停止步进电机设置目标位置为当前位置 stepper.stop(); // AccelStepper的stop()函数 stepper.disableOutputs(); // 关闭电机输出节省电力/减少发热 // 2. 舵机回到安全位置解锁 servoLock.write(unlockAngle); servoRelease.write(0); // 3. 状态切换 currentState ESTOP; setColor(255, 0, 0); // 红色常亮 // 4. 这里可以加入蜂鸣器报警 } void resetToIdle() { // 复位步进电机位置可选物理位置可能已变 // stepper.setCurrentPosition(0); // 谨慎使用需配合机械回零 servoLock.write(unlockAngle); servoRelease.write(0); currentState IDLE; Serial.println(System Reset to IDLE.); } // 辅助函数设置RGB LED颜色 void setColor(int red, int green, int blue) { analogWrite(ledR, 255 - red); // 如果是共阳极则不需要255- analogWrite(ledG, 255 - green); analogWrite(ledB, 255 - blue); }5.3 关键参数校准与调试技巧代码中的一些参数需要根据你的具体硬件进行校准maxSteps(最大步数)这是最关键的一个参数。它表示电位器旋到最大时步进电机需要走多少步才能将投石臂拉到安全范围内的最大角度。校准方法先将电位器置于中间位置上传一个简单的测试程序让电机走一个较小的步数如200步。观察绞盘收绳长度和投石臂角度变化。手动测量从初始位置到你希望的最大发射角度注意不要超过结构承受极限步进电机总共需要的步数。这个值就是maxSteps。务必留有余量防止过度拉拽损坏结构。步进电机速度与加速度 (setMaxSpeed,setAcceleration)setMaxSpeed速度太快可能导致步进电机失步发出的脉冲数多于实际走的步数表现为拉不到位或位置不准。从较低值如200步/秒开始测试逐步增加直到找到在负载下稳定运行的最大速度。setAcceleration加速度设置使电机启动和停止更平滑减少对机械结构的冲击。值太小则动作缓慢太大可能引起振动或失步。200-400是一个不错的起始范围。舵机角度 (lockAngle,unlockAngle)这需要根据你实际安装的锁销机构来确定。上传一个测试程序让舵机在0-180度间扫描观察锁销的运动轨迹找到能可靠插入锁止盘和完全退出的两个角度。务必确保在解锁位置锁销完全脱离齿轮运动路径否则发射时会卡住。防抖动处理代码中使用了简单的delay(50)进行按钮防抖。对于要求更高的场合可以使用状态检测法记录按下时间只有持续低电平超过一定时间才视为有效或中断配合计时器的方式避免误触发。6. 系统集成、测试与安全规范当所有硬件组装完毕代码也上传后就到了最激动人心也最需要谨慎的集成测试阶段。6.1 分阶段测试流程绝对不要一上来就挂配重测试遵循以下顺序步步为营通电前目视检查所有电线连接是否正确、牢固特别是电源正负极有没有接反螺丝、螺母是否拧紧结构件有无明显松动运动部件投石臂、齿轮、绞盘的转动是否顺畅有无干涉空载上电测试不挂配重不装抛射物仅连接Arduino和驱动板电源不接步进电机和舵机。测试按钮、电位器、LED功能是否正常。通过串口监视器查看打印的状态和电位器读数。接上舵机测试锁定和释放动作是否平滑、到位。用手轻轻推动锁销机构感受舵机扭矩是否足够。接上步进电机但将绳子从绞盘上取下。测试步进电机是否能正反转响应电位器控制。听电机声音是否平稳有无尖锐噪音或堵转声。带载测试装绳子不挂重物将绳子系在投石臂上但配重篮空载。进行完整的“装填-锁定”循环。观察步进电机拉绳是否平稳绞盘收绳是否整齐锁销能否可靠锁入。测试“发射”指令观察锁销能否顺利拔出投石臂在空载下是否能回弹。轻载测试小配重在配重篮中放入少量重物如一本厚书。重复测试流程。这是检验机械结构和电机扭矩的关键一步。注意听齿轮箱和电机的声音如果出现异常噪音或电机停转立即急停。测量不同电位器位置对应的投石臂角度验证控制线性度。全载测试与校准逐步增加配重至设计值。每次增加重量后都重复测试。进行发射测试放入一个轻软的安全抛射物如海绵球。所有人员站在侧面或后方绝对不要在发射轨迹前方根据射程微调电位器映射关系map函数或机械结构如绳子悬挂点。6.2 常见故障排查速查表现象可能原因排查步骤上电无反应电源未接通Arduino损坏短路保护。1. 检查电源开关、插头。2. 用万用表测量Arduino Vin/5V引脚电压。3. 检查有无短路点特别是电源线。步进电机不转驱动模块未使能接线错误电机绕组接错电流太小。1. 检查TB6612FNG的PWMA/PWMB是否接高。2. 用万用表确认电机绕组。3. 调整驱动模块上的电流调节电位器如果有。4. 用手转动电机轴如果很紧可能是电流过大。步进电机失步拉不到位电机扭矩不足速度或加速度设置过高机械阻力过大电源电压不足。1. 降低setMaxSpeed和setAcceleration。2. 检查12V电源是否足额测量带载时的电压。3. 检查齿轮箱、轴承是否润滑良好有无卡滞。4. 尝试增大驱动电流注意散热。舵机不动作或抖动电源功率不足信号线接触不良舵机损坏角度指令超出范围。1.首要检查用独立5V电源给舵机供电。2. 检查信号线连接。3. 用servo.write()测试0-180度内多个角度。4. 单独测试舵机。锁销无法锁住或锁不紧舵机扭矩不足锁销与锁止盘未对准机械结构有间隙。1. 检查舵机在锁定位置是否被外力推动断电状态下用手试。2. 调整舵机安装位置或锁销形状确保垂直插入。3. 使用扭矩更大的舵机如MG90S。按钮或电位器读数不稳定接触不良内部上拉电阻未启用或失效干扰。1. 检查接线和焊点。2. 确认代码中使用了INPUT_PULLUP。3. 在模拟输入引脚对地加一个0.1uF电容滤波。4. 在代码中加入软件滤波如多次采样取平均。发射后系统不复位急停按钮被意外触发状态机逻辑错误代码跑飞。1. 检查急停按钮接线是否松动是否为常闭接法。2. 在loop()中每个状态分支后添加串口打印跟踪状态流。3. 检查是否有数组越界、除零等可能导致崩溃的操作。6.3 至关重要的安全规范这是一个动力机械项目安全必须放在首位。个人防护在加工木材、金属特别是钻孔、切割时务必佩戴护目镜。调试电机和机械结构时不要穿宽松的衣服长发要扎起。测试区域清场发射测试时确保前方及可能的反弹区域没有任何人、宠物或易碎物品。最好在户外空旷场地进行。逐步增加能量始终从空载、轻载开始测试逐步增加配重。永远不要一开始就使用最大设计配重。急停功能确保急停按钮随时可及且功能100%可靠。定期测试它。结构检查每次测试前后检查所有木制接合处、螺丝、轴销是否有松动、开裂迹象。疲劳是金属和木材的敌人。电力安全注意12V电源线不要被运动部件磨损。所有裸露的电气连接点应用热缩管或绝缘胶带包好。不要在系统通电时进行接线操作。抛射物永远不要使用坚硬、尖锐或过重的物体作为抛射物进行测试。初期使用海绵球、网球等安全物体。7. 项目优化与扩展思路当基础功能稳定实现后你可以考虑以下优化和扩展让项目更智能、更强大。7.1 硬件层面的优化增加位置反馈目前步进电机是开环控制。可以在绞盘轴或投石臂上安装一个旋转编码器或电位器实时反馈实际位置形成闭环控制。这样即使偶尔失步系统也能感知并纠正精度和可靠性大幅提升。升级动力系统如果发现扭矩不足可以升级步进电机如更大型号的57电机或使用减速比更大的齿轮箱。对于锁销舵机如果感觉力度不够可以换用金属齿轮的舵机如MG996R。加入限位开关在投石臂运动的起点和终点安装微动开关或光电传感器作为“归零”或“极限位置”保护防止过度运行损坏机构。改进人机交互用一个小型OLED显示屏替代RGB LED可以显示当前角度、设定角度、系统状态、电池电压等信息。加入旋钮编码器来替代电位器操作更精准。7.2 软件层面的增强实现闭环控制如果加了编码器可以使用PID控制算法来让步进电机更平滑、精确地到达目标位置并能抵抗外力干扰。增加发射模式除了手动设定角度可以编程实现“固定角度连发”、“角度递增连发”等模式。数据记录与分析通过蓝牙或Wi-Fi模块如ESP-01s或HC-05将每次发射的角度、电机电流等数据发送到电脑或手机用于分析性能和优化参数。加入安全自检上电时系统自动执行一系列自检舵机是否回位、限位开关是否正常、电位器读数是否在合理范围等。7.3 机械与功能扩展自动装弹机构在抛射端增加一个由小型舵机或直流电机驱动的“弹仓”和“推弹杆”实现发射后自动装载下一个抛射物实现全自动连发。方向旋转平台将整个投石机安装在一个由另一个步进电机驱动的转台上实现水平方向的自动瞄准。弹道计算与自动瞄准结合测距传感器如超声波或激光测距和倾角传感器输入目标距离后Arduino自动计算所需的发射角度考虑抛物线弹道并控制步进电机拉到对应位置。这就真正迈向智能化了。这个项目从一块木头、一块电路板开始到最终成为一个能够听从指令、自动完成复杂机械动作的系统整个过程充满了挑战和乐趣。它深刻地展示了如何将软件逻辑与硬件实体结合起来去解决一个具体的物理问题。最大的收获不是最终能抛多远而是在调试过程中对电机特性、机械公差、控制时序、安全规范这些工程细节的切身理解。当你按下按钮听到齿轮啮合的声音看到锁销精准到位最后配重落下、抛射物划出弧线的那一刻所有的调试和折腾都值了。希望这份详细的记录能帮你绕过我踩过的那些坑更顺利地打造出属于你自己的自动投石机。记住安全第一享受创造的过程。