基于SCARA机械臂的DIY写字钟:从运动学算法到嵌入式实现
1. 项目概述与核心思路这个项目我称之为“会写字的钟”它本质上是一个融合了嵌入式控制、运动学算法和一点机械创意的桌面艺术品。它没有传统的指针或数字显示屏而是通过一支马克笔在白色书写板上“一笔一划”地写出当前的时间每分钟自动更新一次并用一个简单的擦除动作清空旧的时间。整个系统的核心是借鉴了工业上常见的SCARASelective Compliance Assembly Robot Arm选择顺应性装配机器人手臂机械臂的运动原理用三个廉价的9g舵机伺服电机实现了平面内的两轴定位和垂直方向的抬笔/落笔动作。为什么选择SCARA结构在DIY项目中我们常常面临如何在有限预算和复杂度下实现精确平面运动的问题。SCARA结构以其在平面内的高刚性和相对简单的运动学正反解即从坐标到舵机角度的换算而著称。它由两个旋转关节对应我们的两个主舵机和一个末端垂直移动关节对应我们的抬笔舵机组成非常适合这种在固定平面上进行书写作业的场景。与更常见的笛卡尔坐标X-Y轴直线运动平台相比SCARA结构在相同的运动范围内所需的物理空间更小结构更紧凑视觉上也更具机械美感。整个项目的逻辑链条非常清晰一块Arduino Nano作为大脑从DS1307 RTC实时时钟模块读取精确的时间信息然后大脑根据预设的字体和坐标计算出笔尖需要到达的每一个点的位置X, Y坐标接着通过运动学反解算法将这些坐标转换为两个主舵机需要转动的角度最后通过PWM信号驱动舵机转动带动机械臂控制笔尖完成书写和擦除动作。这个项目最难能可贵的是它把一个看似复杂的机器人学问题用不到100元的硬件成本和一段开源的代码就实现了非常适合作为学习嵌入式系统、运动控制和机电一体化的入门实践。2. 硬件选型与物料清单解析工欲善其事必先利其器。这个项目的硬件选择充分体现了“在满足功能的前提下追求极致性价比”的DIY精神。下面我们来逐一拆解每个核心部件的选型理由和注意事项。2.1 控制核心Arduino Nano选择Arduino Nano而非更常见的Uno主要基于两点考虑尺寸和成本。Nano在功能上与Uno几乎完全一致同样基于ATmega328P芯片但体积小巧得多非常适合嵌入到这种结构紧凑的装置内部。其数字I/O口数量22个和PWM输出能力6个完全满足驱动3个舵机和连接RTC模块I2C接口的需求。注意市面上有大量国产兼容的Nano板价格低廉但需注意其USB转串口芯片可能是CH340需要在电脑上额外安装CH340驱动才能被Arduino IDE识别。购买时最好选择明确标注“CH340驱动已安装”或自带CP2102等免驱芯片的版本避免第一步就卡在驱动上。2.2 时间基准DS1307 RTC模块RTC模块是项目的“心脏”它保证了时钟在Arduino断电后依然能准确走时。DS1307是一款非常经典且廉价的RTC芯片。它通过一个32.768kHz的晶振提供计时基准并自带一个涓流充电器可以为后备电池通常是一个3V的CR2032纽扣电池充电确保时间数据在完全断电的情况下也能保存数月。这里有一个关键细节DS1307的计时精度很大程度上取决于其外部晶振的精度。廉价的模块为了成本可能使用精度较差的晶振日误差可能在数秒甚至更多。如果你对精度有更高要求可以考虑升级到DS3231模块。DS3231内部集成了温度补偿晶体振荡器TCXO其年误差可以控制在分钟级别远高于DS1307但价格也稍贵。对于这个书写时钟而言DS1307的精度已经足够毕竟我们每分钟才更新一次显示。2.3 执行机构9g微型舵机舵机是这个项目的“肌肉”。我们选择了最常见的9g微型舵机如SG90或MG90S。这类舵机内部包含一个小型直流电机、减速齿轮组和一个位置反馈电位器形成一个闭环控制系统。当我们发送一个特定宽度的PWM脉冲通常周期20ms脉宽在0.5ms到2.5ms之间时舵机会转动到对应的角度通常是0-180度。为什么是三个两个用于控制机械臂在平面内的运动X和Y方向这构成了SCARA的两个旋转关节。第三个舵机则用于垂直方向的“抬笔”和“落笔”动作。这个“抬笔”舵机的安装和联动机构是机械设计的关键需要保证笔能被稳定地提起一定高度避免在移动过程中划伤书写面同时落笔时又能施加稳定的压力以确保字迹清晰。实操心得购买舵机时尽量选择同一品牌、同一批次的。不同品牌甚至同品牌不同批次的舵机其中位点对应1.5ms脉宽的角度和线性度可能有细微差异。这种差异在校准环节会被放大导致两个主舵机难以同步书写轨迹出现扭曲。多买一两个作为备用也是明智之举。2.4 机械结构材料塑料板与铝型材原始项目使用了塑料板作为基板和铝型材作为机械臂。这是一个非常实用的选择塑料板如亚克力或PVC板易于切割、钻孔重量轻绝缘性好是制作基板和结构件的理想材料。厚度建议在3-5mm以保证整体刚性。铝型材如2020或2040欧标铝型材质量轻、强度高、表面有标准槽孔方便使用配套的T型螺母和螺栓进行灵活装配和调节。用它来制作机械臂既能保证强度又便于调整舵机的安装位置。如果手头没有铝型材也可以用多层激光切割的亚克力板叠加或者甚至用结实的水条、轻木条来替代核心是保证连接牢固臂长与代码中定义的L1、L2、L3参数严格一致。完整的物料清单与工具参考表类别名称规格/型号数量备注电子部分Arduino NanoATmega328P1兼容版即可RTC模块DS1307 (带电池座)1建议选择带电池的套装微型舵机9g (SG90/MG90S)3用于X, Y轴和抬笔杜邦线公对公、公对母若干连接电路微型面包板可选1方便调试最终可焊接USB数据线Micro USB1为Arduino供电和编程电源5V/2A直流电源1稳定供电避免USB供电不足导致舵机抖动机械部分塑料基板亚克力板~3mm厚1约20cm x 15cm铝型材2020或类似长度按需2-3根制作机械臂连接件T型螺母、螺栓、舵机盘套用于固定舵机和铝型材书写面板白色白板或光面塑料板1A5大小左右需光滑笔具白板笔或记号笔1笔头粗细适中墨水充足笔夹自制或3D打印1用于将笔固定在抬笔舵机上辅助工具电烙铁及焊锡1套最终固定电路螺丝刀套装1套热熔胶枪/AB胶1辅助固定尺子、记号笔1测量和标记切割工具勾刀亚克力、手锯铝材1根据材料选择3. 机械结构设计与组装要点机械结构的精度直接决定了书写质量。SCARA机械臂的几何关系是固定的我们必须严格按照代码中定义的参数来构建物理模型。3.1 关键尺寸参数的理解在提供的代码开头定义了以下几个核心尺寸单位毫米#define L1 35从左侧舵机旋转中心到第一个关节肘关节的长度。#define L2 55.1从第一个关节肘关节到笔尖安装点的长度。#define L3 13.2从笔尖安装点到右侧舵机旋转中心的虚拟连杆长度这是一个用于运动学计算的参数在物理上体现为两个主舵机旋转中心的相对位置关系。#define O1X 22和#define O1Y -25左侧舵机旋转中心在坐标系中的坐标。#define O2X 47和#define O2Y -25右侧舵机旋转中心在坐标系中的坐标。这里的坐标系原点0,0被定义在书写平面的左下角。O1Y和O2Y为负值意味着两个舵机的旋转中心位于书写平面下方。这是为了让机械臂有足够的运动空间覆盖整个书写区域。L3这个参数非常重要它不是一个真实的连杆而是连接笔尖点和右侧舵机中心的一个“虚拟连杆”用于简化运动学反解计算。在物理装配时你需要确保两个舵机旋转中心的水平距离为O2X - O1X 25mm垂直距离一致Y坐标相同。3.2 组装步骤与精度控制制作基板与安装舵机在塑料基板上精确测量并标记出两个主舵机左、右的安装位置。这两个位置必须严格对应(O1X, O1Y)和(O2X, O2Y)但注意Y坐标是负值意味着你需要将舵机安装在书写板下方的某个平面上。钻孔并用螺丝牢固固定舵机。舵机轴心的高度需要仔细设计使得当机械臂水平伸展时笔尖能刚好接触到书写板表面。制作机械臂根据L1和L2的长度切割铝型材。L1连杆的一端通过舵机盘与左侧舵机输出轴固定另一端与L2连杆通过一个旋转关节可以用一个螺栓和螺母充当轴确保转动顺滑但无明显晃动连接。L2连杆的另一端就是笔的安装点。设计抬笔机构这是最容易出问题的部分。第三个舵机抬笔舵机需要安装在基板上并通过一个巧妙的连杆或凸轮机构将旋转运动转换为笔的垂直升降。一种简单有效的设计是将抬笔舵机竖直安装在其舵机盘上垂直固定一根短臂短臂的末端通过一个活动接头如鱼眼轴承或简单的松配合孔连接笔夹。当舵机转动时短臂推动或拉动笔夹实现抬落笔。关键是要保证笔的升降行程足够约5-10mm且运动轨迹尽可能垂直避免横向晃动。整体调试与固定在初步组装后不要急于拧死所有螺丝。先手动移动机械臂检查各个关节的运动范围是否顺畅有无干涉。特别是笔尖在移动到书写区域四个角落时是否会与支架或其他部分碰撞。确认无误后再逐步紧固所有连接。避坑指南机械组装最大的敌人是“虚位”或“回差”。在舵机盘与轴的连接、连杆之间的铰接处如果存在间隙会导致笔尖的实际位置与计算位置出现误差书写出的线条会抖动或出现双线。解决方法包括使用带橡胶垫的舵机盘以增加摩擦力在铰接处使用带法兰的轴承或添加垫片消除轴向间隙在所有螺丝连接处使用螺纹胶如乐泰242或防松螺母。4. 电路连接与系统集成电路部分相对简单核心是确保电源稳定。舵机在启动和堵转时瞬间电流很大可能超过1A如果供电不足会导致Arduino复位或舵机抖动无力。4.1 接线图与原理遵循以下连接方式Arduino Nano 5V - 所有模块VCC为RTC模块和三个舵机供电。Arduino Nano GND - 所有模块GND共地至关重要。Arduino Nano A4 (SDA) - DS1307 SDAArduino Nano A5 (SCL) - DS1307 SCL这是I2C通信线用于读取时间。Arduino Nano D2 - 抬笔舵机信号线黄色/橙色线Arduino Nano D3 - 左侧舵机信号线Arduino Nano D4 - 右侧舵机信号线强烈建议的供电方案不要仅依赖Arduino Nano的USB口或板上5V稳压器为所有设备供电。最佳实践是使用一个外部的5V/2A以上的直流电源如手机充电器其正极5V同时连接到舵机电源的正极和Arduino Nano的VIN引脚如果电源是5V也可接5V引脚但要注意不要同时从USB和此引脚供电负极GND连接到所有设备的GND。Arduino Nano则通过USB线仅用于上传程序。这样可以提供充足且稳定的电流避免因电压跌落引起的问题。4.2 集成与绝缘处理在电路焊接或使用面包板调试无误后可以考虑将元件集成到一块小型PCB或洞洞板上并用扎带或螺丝固定在基板背面使整体更整洁。务必注意绝缘特别是金属螺丝不要短路PCB上的线路。可以用热熔胶或绝缘胶带覆盖裸露的焊点。5. 核心代码解析与运动学原理项目的灵魂在于代码。它不仅要读取时间、驱动舵机更重要的是实现从“目标坐标”到“舵机角度”的转换即运动学反解。5.1 运动学反解算法详解代码中的set_XY(double Tx, double Ty)函数是实现运动学反解的核心。它输入笔尖的目标坐标(Tx, Ty)计算出左右两个舵机需要转动的角度并转换为PWM脉宽发送出去。其原理基于平面几何中的余弦定理。我们把机械臂简化成两个三角形左侧三角形由左侧舵机旋转中心O1、肘关节、笔尖点T构成。已知三边长度L1O1到肘关节、L2肘关节到T、cO1到T的距离通过坐标计算得出。利用余弦定理可以求出角a2进而得到左侧舵机需要转动的总角度(a2 a1 - π)其中a1是向量O1-T与水平轴的夹角。右侧三角形需要先求出肘关节的位置H。已知L3笔尖点到右侧舵机中心的虚拟杆长和角度关系可以计算出H点坐标。然后在由右侧舵机中心O2、H点和肘关节构成的三角形中再次应用余弦定理求出右侧舵机需要转动的角度。代码中的return_angle函数就是余弦定理的实现return acos((a*a c*c - b*b) / (2*a*c));。SERVOFAKTORLEFT和SERVOFAKTORRIGHT是比例系数用于将计算出的弧度值转换为舵机所需的微秒数。SERVOLEFTNULL和SERVORIGHTNULL则是两个舵机的机械零点偏移量用于校准。5.2 主程序逻辑与时间管理在loop()函数中程序的核心逻辑是每分钟检查一次时间是否变化 (if (last_min ! minute()))。如果变化了则执行以下步骤附着舵机重新连接舵机信号线如果之前为了省电而分离了。落笔lift(0)将笔放下。书写时间调用number()函数依次书写小时、冒号、分钟。number()函数内部通过一系列drawTo()调用勾勒出数字的笔画。drawTo()函数则将目标点(pX, pY)与上一个点连线并细分为多个小步通过多次调用set_XY()实现平滑的直线移动。抬笔并移动到擦除器lift(2)将笔抬到最高位置然后移动到擦除器代码中固定点(74.2, 47.5)上方再lift(1)降至擦除高度。分离舵机书写完成后调用detach()方法断开舵机信号。这是一个非常实用的省电和防抖技巧。舵机在收到信号时会持续用力保持位置导致发热和耗电。断开信号后舵机轴会处于松弛状态可以手动移动虽然我们的结构不允许更重要的是停止了电流消耗。5.3 字体与笔画生成数字的书写是通过number()函数硬编码实现的。每个数字0-9以及冒号“:”都是一系列直线段和圆弧段的组合。例如数字“0”被定义为一个近似的椭圆通过bogenGZS()画顺时针弧函数完成。bogenUZS()和bogenGZS()函数利用参数方程通过微小角度增量计算圆弧上的点从而驱动笔尖画出平滑曲线。这种方法的优点是直接、无需复杂的字体库缺点是字体是固定的修改字形比较麻烦。如果你想自定义字体或书写文字就需要重新设计每个字符的笔画路径坐标。6. 校准从理论到现实的桥梁校准是让这个项目从“能动”到“好用”的最关键一步。代码开头通过#define CALIBRATION宏定义开启了校准模式。在校准模式下程序不会书写时间而是反复让机械臂在两个预设点之间移动这两个点被设计为使得两个舵臂互相垂直。6.1 校准步骤详解上传校准代码确保代码中#define CALIBRATION一行没有被注释掉而#define REALTIMECLOCK被注释掉。然后上传代码到Arduino。机械调零给系统上电。两个主舵机会转动试图让笔尖移动到预设的校准点。此时你需要松开固定舵机盘的螺丝手动调整舵机输出轴的位置使得两个机械臂尽可能地互相垂直成90度角。这一步是粗调目的是让机械的“零位”接近软件的“零位”。调整零点偏移量调整SERVOLEFTNULL和SERVORIGHTNULL这两个常量的值。这两个值直接加到计算出的舵机角度上用于补偿机械安装的误差。通过串口监视器代码中需启用Serial观察或者直接目测机械臂位置微调这两个值直到机械臂在移动过程中在两个校准点都能保持完美的垂直状态。每次修改后需要重新上传代码。调整比例因子如果完成上一步后机械臂移动90度实际对应的物理位移不准确例如代码让它画一个10mm的线它只画了8mm则需要调整SERVOFAKTORLEFT和SERVOFAKTORRIGHT。增大因子会使舵机对相同坐标变化反应更“剧烈”移动角度更大。通常需要反复迭代调整NULL值和FAKTOR值直到机械臂的运动范围精确覆盖整个书写板且移动直线平直。抬笔高度校准调整LIFT0落笔、LIFT1中间高度、LIFT2最高这三个值。这些值是直接发送给抬笔舵机的PWM微秒数。通过修改它们确保LIFT0时笔尖刚好接触板面并有一定压力LIFT1时笔尖完全离开板面但不过高LIFT2是移动到擦除器上方时的高度。6.2 校准实战技巧使用激光笔辅助在笔夹上临时固定一个低功率激光笔关闭房间灯光可以在墙上投射出清晰的光点。这样能极其精确地观察笔尖光点的实际运动轨迹比肉眼观察笔尖本身要容易得多。分步校准先只连接一个舵机进行校准调好它的NULL和FAKTOR值然后再连接第二个这样能隔离问题。记录成功参数一旦校准成功立即将最终的SERVOFAKTORLEFT、SERVOFAKTORRIGHT、SERVOLEFTNULL、SERVORIGHTNULL、LIFT0/1/2值记录下来。这些值是专属于你这套机械结构的“指纹”换一套材料或重新组装后都需要重新校准。7. 常见问题排查与优化建议即使按照指南操作你也可能会遇到一些问题。下面是一些常见故障及其解决方法现象可能原因排查与解决思路舵机完全不动或抽搐1. 电源功率不足。2. 信号线接触不良或接错。3. 舵机损坏。1. 使用外接5V/2A以上电源单独为舵机供电。2. 检查杜邦线连接确保信号线黄/橙接在正确的数字引脚上。3. 单独测试舵机用servo.write(90)简单程序测试。书写笔画扭曲、不圆滑1. 机械结构有较大虚位或松动。2. 舵机NULL/FAKTOR值校准不准。3. 两个舵机性能不一致。1. 紧固所有机械连接消除间隙。2. 重新执行精细校准流程使用激光笔辅助。3. 尝试交换左右舵机看问题是否跟随舵机转移。写字断墨或笔迹太浅1. 落笔高度(LIFT0)太高压力不足。2. 书写板表面太光滑或不适合该笔。3. 笔尖磨损或墨水不足。1. 减小LIFT0值增加落笔压力。2. 更换不同材质书写板或使用出墨更流畅的笔如白板笔。3. 更换新笔。擦除不干净1. 擦除器通常是海绵或橡皮位置不准或压力不够。2. 擦除动作轨迹未完全覆盖字迹。1. 调整擦除器的安装位置和高度确保笔经过时能有效接触。2. 在代码中调整擦除动作的路径坐标使其来回擦拭。时间不准或RTC不工作1. DS1307模块电池没电或未安装。2. I2C接线错误SDA, SCL接反。3. 库文件未正确安装。1. 检查并更换CR2032电池。2. 检查A4/A5引脚连接。使用I2C扫描示例程序检查设备地址DS1307通常是0x68。3. 在Arduino IDE中确认已安装DS1307RTC和Time库。运行一段时间后错位1. 舵机因持续受力发热导致性能漂移。2. 电源电压波动。1. 确保在非移动时段代码执行了servo.detach()。2. 使用更稳定的线性电源或在舵机电源端并联一个大电容如1000uF缓冲电流冲击。项目优化方向增加Wi-Fi/网络对时用ESP8266模块替换Arduino Nano连接网络后通过NTP协议获取精确时间无需手动设置也解决了DS1307累积误差问题。设计更优雅的机壳使用3D打印或激光切割为整个机械结构和电路设计一个美观的外壳提升成品质感。实现更多功能修改代码使其不仅能写时间还能写日期、温度湿度需加传感器、甚至简单的图形或短句。改进擦除机制目前的“画线擦除”可能效果不佳。可以改为控制一个额外的舵机带动一块小橡皮或毛刷进行物理擦除。这个项目最迷人的地方在于它清晰地展示了软件算法如何驱动硬件实体去完成一个具象的任务。当你第一次看到自己组装的机械臂颤颤巍巍却准确地写出一个数字时那种跨越虚拟与现实的成就感是单纯编程或单纯做手工都无法比拟的。它需要耐心尤其是校准阶段可能需要反复调整几十次参数。但一旦调通它就会成为一个稳定可靠、充满极客趣味的桌面伙伴。