
1. 项目概述一个会“抓人”的互动龙如果你对Arduino、传感器和手工制作都感兴趣那么把这三者结合起来做一个能和你玩“红灯停绿灯行”游戏的自动机绝对是个让人兴奋的挑战。这个项目源于一个课程作业但它的魅力远不止于此。它本质上是一个集成了环境感知、逻辑判断和物理动作的微型嵌入式系统。核心玩法很简单你按下按钮游戏开始系统进入“绿灯”状态此时你可以移动当系统切换到“红灯”状态时你必须立刻静止否则就会被PIR运动传感器捕捉到判定为失败伴随着灯光闪烁和蜂鸣器警报一只手工钩织的龙还会“愤怒”地动起来。我选择这个项目是因为它完美地融合了代码的逻辑之美、电路的精准控制和手工的温暖触感。对于第一次接触Arduino和电子制作的我来说它涵盖了从电路搭建、状态机编程、传感器调试到结构设计与手工制作的完整流程。最终这个会“监视”你、会“生气”的小龙不仅是一个有趣的游戏装置更是一个生动的学习成果它让我深刻理解了从传感器信号输入到物理世界动作输出的整个闭环是如何实现的。2. 核心系统设计与思路拆解2.1 游戏逻辑与状态机设计这个项目的“大脑”是一套清晰的状态机逻辑。状态机是嵌入式系统中处理复杂流程的经典模型特别适合这种有明确状态和转换规则的游戏。我们的系统主要包含以下几个状态待机状态系统上电后的初始状态等待玩家按下启动按钮。绿灯状态玩家可以自由移动。系统持续监控PIR传感器但在此状态下移动信号被忽略。玩家需要在此状态下尽可能向后退远离传感器为后续的“红灯”阶段争取时间。玩家可以随时按下按钮来宣布胜利假设已到达安全距离。红灯状态玩家必须保持绝对静止。系统持续监控PIR传感器任何检测到的移动都会被视为违规立即触发失败流程。同时红色LED亮起伺服电机驱动龙做出动作模拟“监视”或“警告”。胜利/失败状态游戏的终结状态。无论是玩家主动按下按钮获胜还是因移动被检测到而失败都会进入此状态。系统会通过LED的特定闪烁模式和蜂鸣器的音效给出明确反馈。状态之间的转换由两个主要事件驱动时间事件例如绿灯持续10秒后自动切换到红灯和外部触发事件按钮按下、传感器触发。在代码中我们通常使用一个enum来定义这些状态并用一个switch-case结构来组织每个状态下的行为逻辑和状态转换条件。2.2 硬件选型与交互架构硬件是项目的“躯体”选型直接决定了系统的可靠性、成本和最终体验。主控Arduino Uno R3。这是创客项目的经典起点。它拥有足够的数字I/O引脚本项目用了约7个易于编程社区资源丰富对于实现我们的状态机和驱动外围设备绰绰有余。感知核心HC-SR501 PIR运动传感器。这是项目的“眼睛”。它通过探测红外辐射的变化来感知移动非常适合检测人体这样的热源。其输出是简单的数字信号高电平代表检测到移动极大简化了编程。需要注意其两个可调旋钮灵敏度探测距离和延时时间触发后输出高电平的持续时间。在本游戏中为了快速响应违规我们将延时时间调到最小。执行单元SG90微型伺服电机作为“肌肉”负责驱动龙形玩偶上下运动。伺服电机可以精确控制角度我们通过Arduino的Servo库发送脉宽调制PWM信号来控制它。红/绿LED与330Ω限流电阻作为“信号灯”直观显示游戏状态。绿色代表安全可移动红色代表危险静止。必须串联电阻防止过电流烧毁LED或Arduino引脚。有源蜂鸣器作为“警报器”发出提示音和失败音效。有源蜂鸣器只需给高电平就会响控制简单。输入设备轻触开关按钮与10kΩ上拉电阻。按钮用于启动游戏和宣告胜利。连接时使用上拉电阻确保引脚在按钮未按下时处于稳定的高电平状态避免因引脚悬空导致的误触发。动力与连接整个系统由5V移动电源通过USB口供电确保了便携性。使用杜邦线在面包板或洞洞板上搭建电路方便调试和修改。整个交互架构形成了一个清晰的链条按钮/PIR传感器输入 - Arduino处理 - LED/蜂鸣器/伺服电机输出。理解这个链条是调试和扩展项目的基础。2.3 结构设计与工艺融合为了让电子部分和手工玩偶协同工作结构设计至关重要。核心挑战在于如何将伺服电机的旋转运动转化为龙玩偶稳定、可靠的上下往复运动。我设计的解决方案是使用一根铁艺线作为传动杆。一端用胶带或热熔胶固定在伺服电机的舵盘上另一端穿过外壳顶部的孔并弯折成一个钩子钩住龙的身体。当伺服电机在一定角度范围内来回转动时就会带动铁艺线从而牵引龙上下运动。为了确保传动顺畅且龙不会因自身重量下垂需要在箱体内部安装一个自制的“线缆支架”作为铁艺线的滑动轴承。外壳采用3mm MDF板激光切割而成这种材料易于加工、强度足够且外观整洁。设计时需要在前面板为PIR传感器和LED开孔在顶板为按钮和传动铁艺线开孔在侧板为USB电源线开槽。内部还需要设计隔板或支架用来固定电路板、伺服电机和线缆支架实现整洁的内部走线和稳定的机械结构。注意在最终组装前务必进行“空载测试”和“负载测试”。先让伺服电机带着铁艺线空跑观察运动范围是否合适、有无卡滞。再装上龙玩偶测试确保伺服电机有足够的扭矩带动它且运动轨迹符合预期。我的初版设计就低估了龙的重量和体积导致不得不从“空中飞舞”改为“趴在盒子上起伏”这是一个重要的经验教训。3. 电路搭建与核心代码解析3.1 电路连接详解与避坑指南电路是项目的神经系统连接错误轻则功能失常重则损坏元件。以下是基于原理图的引脚连接详解及关键注意事项元件引脚/端连接至 Arduino Uno 引脚说明与注意事项PIR传感器VCC5V供电GNDGND接地OUT数字引脚 2建议使用中断引脚2或3可实现更即时响应。按钮一端数字引脚 3配合上拉电阻使用。另一端GND按下时将引脚3拉低到GND。10kΩ电阻一端数字引脚 3上拉电阻接在按钮与引脚3之间。另一端5V确保按钮未按下时引脚3为高电平。红色LED阳极长脚数字引脚 4通过330Ω限流电阻连接。330Ω电阻一端数字引脚 4必须串联保护LED和Arduino。另一端红色LED阳极红色LED阴极短脚GND绿色LED阳极数字引脚 5通过另一个330Ω电阻连接。330Ω电阻一端数字引脚 5另一端绿色LED阳极绿色LED阴极GND有源蜂鸣器VCC ()数字引脚 6控制端高电平鸣响。GND (-)GNDSG90伺服电机红色线 (VCC)5V注意最好通过外部5V电源供电或确保USB电源能提供足够电流。棕色线 (GND)GND橙色线 (信号)数字引脚 9标准PWM引脚用于控制角度。搭建与调试心得电源管理伺服电机在启动和堵转时电流较大如果和Arduino共用USB的5V可能导致Arduino复位。最稳妥的做法是所有元件包括Arduino都从一个能提供足够电流建议1A以上的5V电源取电。PIR传感器调试刚上电时PIR需要30-60秒初始化时间期间输出可能不稳定这是正常的。将跳线帽设置在H可重复触发模式这样只要一直有移动输出就一直为高。将延时旋钮逆时针拧到最小以减少触发后的保持时间让检测更灵敏。将灵敏度旋钮调到中间位置根据实际游戏距离微调。按钮防抖机械按钮在按下和松开时触点会产生物理抖动导致Arduino误读多次按下。必须在软件中做防抖处理例如检测到按下后延时50毫秒再读取状态。3.2 核心代码逻辑与状态机实现代码是项目的灵魂它定义了游戏的所有规则。以下是基于状态机模型的核心代码框架解析。#include Servo.h // 引脚定义 const int pirPin 2; const int buttonPin 3; const int redLedPin 4; const int greenLedPin 5; const int buzzerPin 6; const int servoPin 9; // 游戏参数 const unsigned long greenLightDuration 10000; // 绿灯持续时间10秒 const unsigned long redLightDuration 5000; // 红灯持续时间5秒示例 const int winDistance 700; // 假设的“7米”对应传感器阈值实际需校准 // 状态枚举 enum GameState { STATE_IDLE, // 待机 STATE_GREEN, // 绿灯 STATE_RED, // 红灯 STATE_WIN, // 胜利 STATE_LOSE // 失败 }; GameState currentState STATE_IDLE; // 计时器 unsigned long stateStartTime; unsigned long lastMoveTime; int playerDistance 0; // 模拟或记录玩家后退距离 Servo dragonServo; // 伺服电机对象 void setup() { pinMode(pirPin, INPUT); pinMode(buttonPin, INPUT_PULLUP); // 启用内部上拉电阻 pinMode(redLedPin, OUTPUT); pinMode(greenLedPin, OUTPUT); pinMode(buzzerPin, OUTPUT); dragonServo.attach(servoPin); dragonServo.write(90); // 初始位置 Serial.begin(9600); // 用于调试 Serial.println(System Ready. Press button to start.); } void loop() { int buttonState digitalRead(buttonPin); int motionDetected digitalRead(pirPin); switch (currentState) { case STATE_IDLE: digitalWrite(redLedPin, LOW); digitalWrite(greenLedPin, LOW); if (buttonState LOW) { // 按钮被按下上拉模式按下为LOW delay(50); // 简单防抖 if (digitalRead(buttonPin) LOW) { startGame(); } } break; case STATE_GREEN: digitalWrite(greenLedPin, HIGH); digitalWrite(redLedPin, LOW); // 绿灯状态下可以记录“后退”动作这里用计时模拟 // 或者通过其他传感器测量真实距离 if (motionDetected HIGH) { // 假设检测到一次移动代表后退了一步 playerDistance 10; // 示例增量 lastMoveTime millis(); } // 检查是否按下胜利按钮 if (buttonState LOW) { delay(50); if (digitalRead(buttonPin) LOW playerDistance winDistance) { winGame(); } } // 检查绿灯时间是否结束 if (millis() - stateStartTime greenLightDuration) { enterRedState(); } break; case STATE_RED: digitalWrite(redLedPin, HIGH); digitalWrite(greenLedPin, LOW); // 让龙动起来 animateDragon(); // 在红灯状态下检测移动即为失败 if (motionDetected HIGH) { loseGame(); } // 红灯状态持续时间可选项也可以一直持续直到被触发 if (millis() - stateStartTime redLightDuration) { enterGreenState(); // 切换回绿灯继续游戏 } break; case STATE_WIN: // 胜利动画绿灯闪烁欢快音效 celebrateWin(); // 一段时间后回到待机状态 if (millis() - stateStartTime 3000) { resetGame(); } break; case STATE_LOSE: // 失败动画红灯闪烁警报音效龙剧烈动作 indicateLoss(); // 一段时间后回到待机状态 if (millis() - stateStartTime 3000) { resetGame(); } break; } } // 状态转换函数 void startGame() { Serial.println(Game Start!); currentState STATE_GREEN; stateStartTime millis(); playerDistance 0; playStartTone(); } void enterRedState() { Serial.println(Red Light! FREEZE!); currentState STATE_RED; stateStartTime millis(); playStateChangeTone(); } void enterGreenState() { Serial.println(Green Light! GO!); currentState STATE_GREEN; stateStartTime millis(); playStateChangeTone(); } void winGame() { Serial.println(You Win!); currentState STATE_WIN; stateStartTime millis(); playWinTone(); } void loseGame() { Serial.println(You Moved! You Lose!); currentState STATE_LOSE; stateStartTime millis(); playLoseTone(); } void resetGame() { currentState STATE_IDLE; dragonServo.write(90); digitalWrite(buzzerPin, LOW); } // 辅助功能函数 void animateDragon() { // 让伺服电机在80-100度之间缓慢摆动模拟“监视” int angle 90 10 * sin(millis() / 500.0); // 每5秒一个周期 dragonServo.write(angle); } void playStartTone() { tone(buzzerPin, 523, 200); } // Do void playStateChangeTone() { tone(buzzerPin, 659, 150); } // Mi void playWinTone() { tone(buzzerPin, 784, 200); delay(200); // Sol tone(buzzerPin, 1047, 400); // Do高八度 } void playLoseTone() { for (int i 0; i 3; i) { tone(buzzerPin, 200, 200); // 低音警报 delay(250); } } void celebrateWin() { /* 控制LED闪烁模式 */ } void indicateLoss() { /* 控制LED闪烁和伺服电机快速震动 */ }代码关键点解析状态机核心GameState枚举和switch-case结构是骨架。每个case里只处理该状态下的逻辑和退出条件。非阻塞延时游戏计时使用millis()函数而非delay()。delay()会阻塞整个程序导致无法检测按钮或传感器。millis()通过记录状态开始时间并与当前时间比较来实现计时不影响主循环。传感器读取PIR传感器输出在触发后会有几秒高电平。在红灯状态一旦读到HIGH即判负反应迅速。音效生成使用tone(pin, frequency, duration)函数可以产生简单的方波音调通过组合不同频率和时长能创造出开始、状态切换、胜利、失败等多种音效。4. 手工制作钩织龙形玩偶4.1 钩织基础与材料准备钩织部分为项目注入了独特的个性与温度。即使你不是钩织高手跟着步骤也能完成。你需要准备中粗棉线2团颜色自选。棉线质地挺括易于塑形。3mm钩针适合中粗线。缝合针、剪刀。填充棉用于填充头部、身体和手臂使其饱满。10mm安全眼珠2颗让龙更有神。铁艺线用于翅膀定型约1-2毫米粗易于弯折又足够支撑。钩织使用美式缩写文中已列出。核心针法包括短针sc、加针inc同一针目钩两针、减针dec两针并一针。环形起针mr是制作球形部件的关键。4.2 分部件钩织详解龙玩偶由头部、身体、翅膀手臂、翅膀、角五个部分组成分开钩织再缝合。头部从环形起针4针开始通过规律加针形成半球再钩织多圈短针形成头部主体最后减针收口。关键点在钩织第10-11圈之间在两侧对称位置安装安全眼珠。填充要均匀、饱满但不要过紧导致变形。身体这是最复杂的部分需要塑造出龙的大致体型。起24个锁针然后首尾连接成环钩织短针形成圆柱。通过有规律的加针和减针在特定行数塑造出身体的曲线。务必使用记号扣标记每一圈的起始点和身体侧面的中心线这是保持对称性的生命线。原作者在身体中后段减少了钩织圈数使身体更短粗更稳定。翅膀手臂2个作为连接身体和翅膀的关节需要一定的厚度。从环形起针开始钩织一个细长的圆柱体并轻微加针使其一端略粗。只需轻微填充保持可弯折性。翅膀2个这是视觉效果的关键。从翅膀手臂的顶端接入线钩织30个锁针作为翅膀骨架然后沿着骨架两侧钩织短针形成一个扁平的三角形基底。最后一行使用从半双针到八倍长针的各种长针快速增加针数模拟出翅膀边缘的羽毛或薄膜质感。核心步骤钩织完成后必须用铁艺线进行骨架加固。将铁艺线沿着翅膀的顶部、底部边缘以及中心轴线用针小心地穿入针目缝隙中然后弯折出翅膀展开的造型。这能防止翅膀软塌保持灵动姿态。角大、中、小各2个简单的圆锥体从环形起针开始通过少量加针或直接钩织短针形成。只需在缝合前填入少许填充棉即可。4.3 缝合与整体组装缝合决定最终成品的美观度。定位在缝合前先用珠针将角、翅膀在头部和身体上大致固定从各个角度观察调整找到最协调的位置后再开始缝合。缝合头部与身体将头部的收口边缘与身体顶部的开口边缘对齐。眼睛应位于身体两侧。使用卷缝或挑针缝合确保牢固且线迹隐蔽。缝合角与翅膀将角缝合在头顶。将翅膀手臂的根部牢固地缝合在身体两侧预定位置然后将已用铁艺线定型的翅膀与翅膀手臂的顶端缝合。确保翅膀的加固线朝下即贴近身体这样外观更整洁。最终调整检查所有缝合点是否牢固填充是否均匀造型是否满意。可以适当弯曲铁艺线微调翅膀的角度。实操心得钩织是一个需要耐心的过程尤其是数针目。每钩完一圈最好用记号扣标记并核对针数是否正确。身体部分因为加减针频繁最容易出错。如果某圈针数不对宁愿拆掉几行重来也不要将就否则后续形状会越来越歪。填充时使用钩针柄或镊子将棉塞到角落但要避免戳破织物。5. 机械结构与外壳组装5.1 外壳设计与激光切割外壳的作用是容纳电路、支撑机械结构并呈现整体外观。使用激光切割MDF板是高效精准的方法。设计工具我使用了MakerCase网站生成一个基础盒子的矢量图DXF格式然后导入到Inkscape免费开源矢量软件中进行自定义修改。开孔设计前面板根据PIR传感器和LED的尺寸精确绘制并切割出安装孔。顶板切割一个圆孔用于传动铁艺线穿过切割一个方孔用于安装按钮。侧板切割一个矩形槽用于USB电源线引出。内部结构件设计伺服电机支架一个带卡槽的零件能将伺服电机牢牢固定在底板或侧板上。线缆导向支架一个带有小圆孔的零件安装在顶板下方作为铁艺线的垂直运动导轨防止它左右晃动。按钮加固板一个小零件从内部粘在顶板按钮孔周围增加按钮安装点的强度。电路板支架/隔板用于固定面包板或洞洞板并将其与机械运动部分适当隔离。切割与打磨将设计好的DXF文件交给激光切割机。切割完成后务必对所有零件进行试组装特别是各开孔。MDF激光切割后孔洞可能偏小需要用砂纸仔细打磨扩大确保传感器、按钮、USB线能严丝合缝地装入。5.2 传动机构制作与调试这是连接电子世界和物理世界的桥梁。制作传动杆取一段足够长的铁艺线比如30厘米根据伺服电机舵盘到顶板孔洞的距离以及希望龙运动的幅度将其弯折成“L”形或“Z”形。一端留出一个小环用于固定在伺服电机舵盘上用螺丝或强力胶另一端向上穿过顶板孔和线缆导向支架后弯折成一个钩子。连接伺服电机将传动杆用螺丝或胶水固定在伺服电机舵盘上。确保安装牢固且伺服电机转动时传动杆不会碰到其他部件。空载测试上传一段简单的测试代码如让伺服电机在0-180度间往复运动观察传动杆的运动是否平滑、范围是否合适。调整代码中的角度限制使其运动范围与顶板孔洞和线缆导向支架匹配。负载测试与调整将龙玩偶挂到传动杆顶部的钩子上。再次运行测试代码。观察伺服电机是否有力带动龙如果出现抖动或无法到达指定位置可能是扭矩不足需减轻龙的重重或换用扭矩更大的伺服电机。龙的运动轨迹是否笔直是否有卡顿调整线缆导向支架的位置确保传动杆垂直运动。运动幅度是否美观通过调整代码中的角度值和传动杆弯曲的形状可以改变龙上下起伏的幅度。5.3 总装与内部理线按照由内到外、先机械后电子的顺序进行总装。粘合主体结构使用木工胶或快干胶将盒子的底板、侧板、前面板粘合起来。顶板和背板先不要粘以便内部操作。安装内部支架将伺服电机支架、线缆导向支架、电路板支架用热熔胶或强力胶粘在盒内预定位置。确保伺服电机支架和线缆导向支架在垂直方向上对齐。安装电子元件将PIR传感器、LED从内部插入前面板的孔中用热熔胶从内部固定。将伺服电机卡入支架并粘牢。将按钮穿过顶板孔从内部用螺母拧紧或在内部用加固板和热熔胶固定。布置电路将Arduino、面包板等放入盒内放在电路板支架上。尽量使用短线连接并使用扎带或胶水固定线束避免杂乱。将传动杆与伺服电机连接好并穿过线缆导向支架和顶板孔。连接与测试将所有电子元件按照电路图连接到Arduino。此时背板还未封闭方便调试。上电运行完整游戏代码测试所有功能按钮启动、LED状态切换、PIR检测、蜂鸣器发声、伺服电机带动龙运动。最终封闭确认所有功能正常后将龙玩偶挂上。用热熔胶在传动杆与龙身体的连接点附近少量点胶加固避免影响可拆卸性。最后粘上顶板和背板完成组装。6. 系统调试与问题排查实录即使按照步骤精心制作调试阶段也总会遇到各种问题。以下是可能遇到的典型问题及解决方法。问题现象可能原因排查步骤与解决方案上电后无任何反应1. 电源未接通或损坏。2. Arduino未正确烧录程序或死机。1. 检查移动电源开关、USB线连接。用万用表测Arduino Vin或5V引脚是否有电。2. 重新插拔USB线尝试上传一个简单的Blink示例程序测试Arduino。按钮按下无反应1. 按钮接线错误或虚焊。2. 引脚模式设置错误应为INPUT_PULLUP。3. 代码中按钮检测逻辑有误。1. 用万用表通断档检查按钮按下时是否导通。2. 检查setup()中是否设置了pinMode(buttonPin, INPUT_PULLUP)。3. 在loop()中打印按钮引脚的电平值观察按下前后的变化。PIR传感器一直触发或不触发1. 传感器未初始化完成。2. 灵敏度或延时调节不当。3. 安装环境有干扰源如通风口、热源。4. 接线错误。1. 上电后等待一分钟再测试。2. 逆时针调节延时旋钮到最小灵敏度先调至中间。用手在传感器前缓慢移动测试。3. 改变传感器安装位置或角度避开干扰。4. 检查VCC、GND、OUT是否对应连接5V、GND、数字引脚。LED不亮或亮度异常1. LED正负极接反。2. 限流电阻阻值过大或忘记接。3. 引脚输出模式错误。1. LED长脚为正阳极应接电阻再到IO口短脚为负阴极接GND。2. 确保使用了330Ω电阻。用万用表测量电阻两端电压。3. 确认pinMode设置为OUTPUT。蜂鸣器不响1. 有源/无源蜂鸣器混淆。2. 引脚控制错误。有源蜂鸣器高电平触发。1. 本项目用有源蜂鸣器。直接给VCC接5VGND接地应常响。确认类型。2. 检查代码是否为digitalWrite(buzzerPin, HIGH)或tone()函数。伺服电机不转或抖动1. 供电不足。2. 信号线接触不良。3. 机械负载过重或卡死。1.最常见原因尝试单独用5V/2A电源给伺服电机供电。2. 检查信号线是否连接到了PWM引脚如9号。3. 断开传动杆测试电机空载是否正常。检查传动机构是否顺畅。龙玩偶运动不顺畅1. 传动杆弯曲角度不当与孔壁摩擦。2. 线缆导向支架未对齐。3. 玩偶过重。1. 调整传动杆形状确保其垂直运动无阻碍。2. 重新调整线缆导向支架的位置使其与伺服电机舵盘中心对正。3. 减少玩偶内部填充棉或选用更轻的纱线。游戏逻辑混乱如状态切换错误1. 状态机逻辑有bug。2. 使用了delay()导致无法及时检测输入。3. 变量未正确初始化或溢出。1. 在串口监视器中打印当前状态(currentState)和关键变量观察其变化是否符合预期。2.确保所有计时都使用millis()杜绝delay()。3. 检查unsigned long类型变量在millis()回滚约50天后后的处理。调试核心心法分模块测试逐步集成。不要一次性组装完所有东西再调试。应先确保Arduino能控制单个LED、蜂鸣器、伺服电机再单独测试PIR和按钮输入是否正常然后将输入输出结合起来测试简单逻辑最后集成机械部分和完整游戏逻辑。善用串口监视器输出调试信息它是你窥探程序运行状态的“眼睛”。完成这个项目你收获的不仅仅是一个有趣的玩具。你实践了嵌入式系统开发的完整流程从需求分析、方案设计、硬件选型、电路搭建、软件编程到机械结构设计、手工制作和系统集成调试。每一个环节遇到的问题和解决的方法都是宝贵的经验。这条钩织的龙最终能否栩栩如生地动起来并和你成功互动取决于你在每个细节上的耐心和思考。这正是创客精神的迷人之处——将想法通过双手变为现实。