十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Arduino Uno与meArm机械臂:桌面级机械臂控制从零入门

Arduino Uno与meArm机械臂:桌面级机械臂控制从零入门 先说结论这套组合就是目前入门桌面级机械臂最稳的路线。Arduino Uno负责底层舵机控制meArm提供完整机械结构两者配合得当的话从零到能让机械臂画一个圆或者夹起一个乒乓球一个下午就能搞定。这篇文章我会把硬件选型、控制原理、完整代码和调试踩坑全部拆开讲清楚并把可复现的代码直接贴在后面适合刚接触机械臂控制的新手也适合想快速搭一套原型做验证的开发者。1. 项目整体设计与方案选型1.1 meArm机械臂的结构特点meArm是一套开源的小型桌面机械臂方案由英国工程师David Eckinson设计结构件以亚克力板激光切割为主。从机械架构上看它有四个自由度底座旋转、肩关节、肘关节、夹爪开合。不同于工业六轴机械臂的复杂连杆结构meArm在本质上是一个平面两连杆机械臂加上一个旋转底座和末端夹爪所以运动学解算相对简单非常适合作为教学和原型验证平台。这个结构带来的好处是控制逻辑清晰底座决定机械臂在水平面上的朝向肩关节和肘关节共同决定末端在垂直平面上的位置夹爪决定能不能抓住东西。每个关节由一个舵机驱动整机重量轻、响应快放在桌面上就能运行不挑场地。但结构简单也意味着有它的短板亚克力件存在装配间隙塑料齿轮舵机有回程误差整机刚性比不上金属结构。所以做精确定位时需要在实际控制中预留误差修正的余地这一点在后面的代码里我会专门处理。1.2 为什么选Arduino Uno作为控制板市面上可以驱动meArm的板子很多树莓派、ESP32、STM32都能做但Arduino Uno在这类场景里依然是最合适的选择之一原因有几个。首先是生态成熟。Arduino的Servo库是最经典的舵机控制方案封装好了PWM生成逻辑直接用myservo.write(angle)就能让舵机转到指定角度不用关心底层定时器和比较寄存器的配置。对于新手来说这是最平滑的学习曲线。其次是资源够用。meArm只需要四路PWM输出Uno有六路还有剩余的I/O口可以接按键、电位器、超声波模块做交互扩展。串口通信是原生支持后面接Python上位机或者手机蓝牙模块都很方便。第三是成本低、损坏风险容易接受。Uno板和SG90/MG90S舵机都是十几块钱到几十块钱的级别实验过程中烧一个舵机不心疼。相比STM32或者带驱动的工业控制板Uno更适合在原型阶段做各种“暴力测试”。当然这不是说Uno没有限制。它只有一个硬件串口处理复杂上位机通信时带宽有限8位单片机跑浮点运算是短板做逆运动学解算时执行时间比32位板子慢一些。但这套简化版几何解算的计算量很小Uno完全跑得动所以不需要在这个阶段追求更高性能。2. 硬件准备与装配要点2.1 舵机选型SG90还是MG90SmeArm套件默认标配的舵机通常是SG90也就是常见的9g微型舵机。SG90的价格低扭矩大概在1.6kg·cm到1.8kg·cm之间驱动轻量级的亚克力臂本体在空载状态下没问题。但如果你计划让机械臂夹取有一定重量的物体或者长时间连续运行导致舵机发热SG90的塑料齿轮就很容易出现扫齿。我的建议是预算允许的情况下底座和肩关节这两个受力最大的位置换MG90S金属齿轮舵机。MG90S的齿轮是金属的抗冲击能力好很多堵转时不太容易直接把齿轮打坏代价是重量比SG90多几克电流也稍微大一点。肘关节和夹爪继续用SG90完全够用。注意MG90S分模拟信号版本MG90S analog和数字信号版本MG90S digital两者引脚兼容但数字版响应速度更快、定位精度更细价格也更高。实际上对于meArm这种小玩具级别应用模拟版的性能已经绰绰有余没必要刻意追求数字版。2.2 供电方案别指望USB口带动四路舵机这是新手最容易翻车的环节。Arduino Uno板上的5V引脚能提供的电流一般只有500mA左右这个电流如果仅仅驱动Uno主控芯片和传感器是没问题的但四路舵机同时动作时瞬间电流会飙到1A以上特别是电机启动和堵转的瞬间电流峰值更大。这时候如果全部从Uno的5V引脚取电电压会被拉低到4V以下导致主控复位舵机抖动甚至完全不动。可靠的方案是给舵机单独供电。我用的是5V 2A的DC电源适配器把正负极引到一块面包板上的电源轨然后所有舵机的红线接电源正极棕线或黑线接GND信号线单独接到Uno的数字引脚。关键点是Uno的GND和外部电源的GND必须共地否则PWM信号无法形成回路舵机照样不工作。另一个实用的技巧是在电源轨上加一个470μF到1000μF的电解电容用来吸收舵机启动瞬间的电流尖峰。加了电容之后舵机在启动时的电压跌落会明显改善抖动概率也会低很多。2.3 接线表与整体连接示意以我手头的套件为例具体的接线顺序如下部件舵机信号线电源正极电源负极底座旋转数字引脚 25V外部电源GND肩关节数字引脚 35V外部电源GND肘关节数字引脚 45V外部电源GND夹爪数字引脚 55V外部电源GND外部电源地线接Uno GND引脚—电源负极注意每个舵机的三根线颜色可能不同。一般来说红色是正极棕色或黑色是负极黄色或橙黄色是信号线。接线前先确认线序接反了会导致舵机内部芯片烧毁这是不可逆的损坏。机械装配方面亚克力板的组装顺序建议从底座往上装先固定底座舵机再装肩关节支撑件随后连接大臂、小臂和夹爪。螺丝不要一次性拧死各个关节之间要留一点点活动余量等所有部分都装好后再整体紧固。如果哪个关节转动明显卡顿检查装配角度是不是偏了舵机的初始位置是否对中了零位。3. 控制原理与Servo库机制3.1 舵机的PWM控制逻辑很多人一听到“PWM控制舵机”就头皮发麻觉得要跟高频信号打交道其实不是。舵机用的PWM并不是电机调速那个高频PWM而是一套慢速的角度位置指令协议。常见9g舵机内部有一个基准电路和一个电位器反馈外部PWM信号的脉宽告诉舵机“你要转到的目标角度是X”。标准舵机的控制信号是周期20ms的方波其中高电平脉宽在0.5ms到2.5ms之间。脉宽0.5ms对应0度1.5ms对应90度2.5ms对应180度近似线性关系。也就是说舵机的角度位置实际是由“高电平持续时间”决定的频率反而不那么关键。Arduino的Servo库在这一层做了封装它自动生成20ms周期的信号myServo.write(90)会自动把90度映射到对应的脉宽。这个映射不要求绝对精确到0.5~2.5ms库内部有一个微调的偏置值但这对于绝大多数应用来说足够了。3.2 Servo库与Uno定时器资源Arduino Uno基于ATmega328P芯片Servo库在Uno上默认使用Timer1定时器来生成舵机控制信号。这会带来一个直接影响Uno的第9和第10引脚原本依赖Timer1做analogWrite输出使用Servo库后这两个引脚的模拟输出功能会失效。好在meArm接舵机用的是2、3、4、5引脚这些引脚不受Timer1影响所以不会冲突。如果你后续想在同一块Uno上用舵机的同时驱动直流电机比如做巡线小车加机械臂的组合项目就要留意定时器的分配问题必要时换Mega或者把舵机控制放到SoftwareServo之类的软PWM方案上。还有一点值得提Servo库的write函数实际上有两种模式write(angle)是角度模式范围0到180度writeMicroseconds(us)是微秒脉宽模式直接指定高电平时间。在需要自定义舵机死区或者微调角度范围时后者更精确。4. 核心代码实现从单舵机测试到逆运动学控制4.1 单舵机初始化与基础测试不管后面功能怎么扩展先确保四路舵机都能正常转动这是基础。以下代码是最简单的单舵机测试让夹爪的舵机在0度和90度之间来回摆动看是否有卡顿或者啸叫。#include Servo.h Servo gripper; void setup() { gripper.attach(5); // 夹爪舵机接在数字引脚5 } void loop() { gripper.write(0); // 全开 delay(1000); gripper.write(90); // 半开 delay(1000); }如果测试过程中舵机抖动剧烈或者发出尖锐的啸叫声先检查供电是否充足再检查舵机连杆有没有卡住。如果在空载状态下依然抖动把write()改成writeMicroseconds()手动指定一个更小的脉宽范围试试。4.2 meArm运动学解算思路到这一步你已经能通过直接给舵机角度来控制机械臂了但用户体验很反直觉。你想让末端往前走5厘米可能就得手动去调肩关节和肘关节分别转多少度靠经验试。为了让机械臂按照直角坐标去运动我们需要做运动学解算。meArm的正运动学相对直观已知底座旋转角、肩关节角、肘关节角用三角函数就能推出末端坐标。但实际使用中更常用的是反问题——逆运动学已知目标位置的x、y、z坐标算出四个舵机各自应该转多少度。对meArm来说这种簡化结构的解法用几何法就够了不需要上DH参数矩阵。基本思路是这样把机械臂的侧视投影看成一个两连杆平面结构大臂和小臂长度固定我这里量的大臂约70mm小臂约60mm末端位置(x, y)就是小臂端点的坐标。根据两连杆长度和末端到肩关节原点的距离用余弦定理可以直接推出大臂和小臂之间的夹角也就是肘关节的角度再算出大臂与水平面的夹角得到肩关节的角度。我在实际推导后用代码验证过这种方法对meArm这种平面双连杆结构精度足够而且省掉了矩阵运算Uno跑起来没压力。4.3 逆运动学计算流程详细拆解假设目标位置是末端坐标(x, y)这里的x是末端在水平面上的投影距离y是末端高度。大臂长L1和小臂长L2是已知常量。第一步计算末端到肩关节原点的直线距离dd sqrt(x^2 y^2)注意如果d大于L1L2或者小于|L1-L2|说明目标位置在机械臂工作范围之外解不存在程序里要加保护。第二步用余弦定理计算肘关节的角度β。三角形中边L1和边L2夹出的角就是肘关节的内角cosβ (L1^2 L2^2 - d^2) / (2 * L1 * L2) β acos(cosβ)第三步计算大臂相对于末端连线的角度γcosγ (L1^2 d^2 - L2^2) / (2 * L1 * d) γ acos(cosγ)第四步计算末端连线与水平面的夹角αα atan2(y, x)这样肩关节的目标角度就是αγ肘关节目标角度就是β但注意角度需要进行弧度到角度的转换且要映射到实际舵机安装的零点方向。我在实际编码中发现这里最容易出问题的就是角度零位的校准。不同套件的舵机安装方向可能不同代码算出来的理论角度可能跟实际舵机零点差90度或者180度需要用一个OFFSET常量做修正。这并非不精确而是对物理安装误差的补偿后面我会给一个完整的校准建议。4.4 带串口控制协议的完整代码下面这份代码实现了四路舵机驱动、逆运动学解算、串口指令解析三个核心功能。把代码烧录进Uno后打开串口监视器设置波特率9600然后发送类似X100 Y120 G0的指令就能让机械臂末端移动到坐标(100, 120)的位置G后面跟的是夹爪开合角度。指令的格式是为了调试方便才设计成人类可读文本后续接上位机也很简单。#include Servo.h // 定义舵机对象 Servo baseServo, shoulderServo, elbowServo, gripperServo; // 舵机引脚定义 const int basePin 2; const int shoulderPin 3; const int elbowPin 4; const int gripperPin 5; // 机械臂尺寸常量单位mm按自己套件实测修改 const float L1 70.0; // 大臂长度 const float L2 60.0; // 小臂长度 // 舵机角度修正校准后修改 const int baseOffset 90; const int shoulderOffset 90; const int elbowOffset 90; const int gripperOffset 90; // 当前目标坐标 float targetX 100; float targetY 100; void setup() { Serial.begin(9600); baseServo.attach(basePin); shoulderServo.attach(shoulderPin); elbowServo.attach(elbowPin); gripperServo.attach(gripperPin); // 上电先回到安全位置避免瞬间动作幅度过大 baseServo.write(90); shoulderServo.write(90); elbowServo.write(90); gripperServo.write(80); delay(1000); Serial.println(meArm Ready. Send command like: X100 Y120 G0); } void loop() { // 解析串口指令 if (Serial.available() 0) { String cmd Serial.readStringUntil(\n); cmd.trim(); parseCommand(cmd); } } void parseCommand(String cmd) { int xIndex cmd.indexOf(X); int yIndex cmd.indexOf(Y); int gIndex cmd.indexOf(G); if (xIndex 0 yIndex 0) { String xStr cmd.substring(xIndex 1, yIndex); String yStr cmd.substring(yIndex 1, gIndex); targetX xStr.toFloat(); targetY yStr.toFloat(); calculateAndMove(targetX, targetY); } if (gIndex 0) { String gStr cmd.substring(gIndex 1); int gripperAngle gStr.toInt(); gripperServo.write(constrain(gripperAngle, 0, 180)); } } void calculateAndMove(float x, float y) { float d sqrt(x * x y * y); // 检查目标是否超出可达范围 if (d (L1 L2) || d fabs(L1 - L2)) { Serial.println(Target out of reach!); return; } // 逆运动学计算 float beta acos((L1 * L1 L2 * L2 - d * d) / (2 * L1 * L2)); float gamma acos((L1 * L1 d * d - L2 * L2) / (2 * L1 * d)); float alpha atan2(y, x); // 弧度转角度 float shoulderAngle (alpha gamma) * 180.0 / PI; float elbowAngle beta * 180.0 / PI; // 舵机角度映射到实际安装方向 int shoulderServoAngle map(constrain(shoulderAngle, 0, 180), 0, 180, 0, 180); int elbowServoAngle map(constrain(elbowAngle, 0, 180), 0, 180, 0, 180); // 施加修正并执行 shoulderServo.write(shoulderOffset shoulderServoAngle); elbowServo.write(elbowOffset elbowServoAngle); // 调试输出 Serial.print(Shoulder: ); Serial.print(shoulderOffset shoulderServoAngle); Serial.print( Elbow: ); Serial.println(elbowOffset elbowServoAngle); }这里有个设计细节值得展开为什么把舵机的角度输出加了一个offset因为不同套件安装舵机时0度、90度、180度的初始方向可能各不相同如果直接把计算角度原样写出机械臂大概率指向错误的方向或者直接撞到限位。先让每个舵机归零观察实际姿态再算出偏差量填入offset这是最省事的校准方式。4.5 坐标系的标定方法代码写完以后最关键的步骤是标定坐标系。我建议按照下面这个流程操作先让所有舵机都转到offset值对应的角度此时机械臂处于一个已知的参考姿态。用尺子量一下末端到肩关节原点的水平距离和垂直高度记录为(x0, y0)。然后发送一条你计算的坐标指令看末端实际到达的位置是否跟预期一致。如果不一致先量出实际偏差方向再调整offset值。例如发送X70 Y60后理论上是让大臂水平伸出、小臂竖直向上末端在高度60、水平距离70的位置。如果实际末端明显偏下可能是肩关节offset负向偏了修改offset并重新测试。这个过程需要反复几次但每次调节的幅度不要太大5度一档慢慢逼近。标定完成后机械臂的重复精度基本上误差在1到2厘米以内这主要受结构件间隙影响已经不是控制层面的问题了。5. 避坑指南实操中的典型问题与排查方案5.1 常见问题速查表现象根本原因解决办法舵机完全不动接线错误或供电不足检查三线顺序单独5V供电四路舵机上电瞬间乱转舵机初始上电时未收到稳定信号程序开头加长延时先attach再指定角度单个舵机抖动严重舵机负载过大或电压跌落加电容检查关节是否卡滞换金属齿轮舵机同一个角度指令每次停的位置不一致舵机回程误差或齿轮磨损尽量同一方向到达目标减少反向运动串口发送指令没反应波特率不匹配或没有换行符确认9600波特率发送\r\n结尾机械臂末端偏向某个方向安装零点偏置未校准调整代码中的offset值5.2 舵机供电不足问题这个坑我刚开始调试时也踩过。直接用USB线给Uno供电四路舵机空载时看上去正常但一旦让肩关节和肘关节同时动作去负载一个较大行程就能看到舵机明显变慢甚至抽搐严重的时候Uno直接重启串口监视器打印乱码。解决方式前面说过外接5V电源加共地。但要注意电源适配器的电流输出能力最好大于2A这样即使四个舵机同时堵转也有余量。还有一个细节面包板上长距离的跳线本身有电阻布线时尽量让电源线和地线短而粗。条件允许的话直接用电烙铁把舵机电源线焊到一块杜邦线母座上减少接触电阻。5.3 机械结构间隙带来的回程误差meArm用的是螺丝加亚克力板的连接方式各个关节的回程间隙累加起来末端位置误差会放大。控制端能做的有限但有一些小技巧可以明显改善定位的一致性。首先是尽量让每个运动从同一个方向逼近目标。例如夹爪从上方下降抓取时让肘关节始终从大于目标角度的方向转到目标角度利用舵机内部齿轮的单向受力消除一部分间隙。这个思路跟大惯量伺服系统的消隙逻辑是相通的只不过这里用软件方式实现。其次是在机械层面想办法。关节螺丝不要拧得太紧但也不能太松找到一个合适的松紧度让关节转动顺滑又没有明显框量。还可以在亚克力件之间的接触面贴一层薄薄的聚四氟乙烯胶带既能减小摩擦也能吸收一部分间隙。5.4 通信协议和数据格式的稳定性如果用串口监视器直接调试往往会遇到发送指令后机械臂没反应的情况。原因通常出在换行符上。Serial.readStringUntil(\n)需要接收端发送一个换行符而串口监视器默认的“发送新行”选项如果没有勾选会导致解析不到完整指令。另一个问题是字符串解析的健壮性。我代码里用了indexOf(X)和indexOf(Y)这种简单方式如果输入的指令大小写不一致或者数字之间多了空格就可能解析出错误的值。更稳的做法是使用Serial.parseFloat()配合分隔符读取但在调试阶段可读性没有文本指令直观。实际项目要接上位机时建议直接用JSON或者纯二进制协议效率和稳定性都更好。6. 进阶玩法Python上位机与多轴联动6.1 用Python串口控制meArmArduino部分稳定运行后下一步通常是想用上位机来控制。Python是首选因为它做串口通信和坐标计算都很方便还能直接跟OpenCV做视觉联动。下面这个简单的Python脚本可以读取用户输入的x、y、g值通过串口发送给Arduinoimport serial import time ser serial.Serial(COM10, 9600, timeout1) time.sleep(2) while True: try: x input(输入X坐标: ) y input(输入Y坐标: ) g input(输入夹爪角度(0-180): ) cmd fX{x} Y{y} G{g}\n ser.write(cmd.encode()) time.sleep(0.1) resp ser.readline().decode().strip() if resp: print(Arduino:, resp) except KeyboardInterrupt: break ser.close()这里有几个细节值得注意。串口打开后要等1到2秒因为Arduino上电后需要复位初始化过早发送数据会丢失。通信过程中加一个小的延时给Arduino留出处理指令的时间避免上位机发送速度太快导致缓冲区溢出。6.2 从点到轨迹多步插补与平滑运动单点控制只是第一步真正让机械臂看起来“智能”的是让末端平滑地沿轨迹运动。最简单的做法是线性插补把起点和终点坐标在空间中分成N个中间点每个周期让机械臂运动到其中一个点形成连续轨迹。例如想让末端从(80, 60)直线移动到(120, 100)可以每50ms发送一组插补后的坐标import math import serial import time ser serial.Serial(COM10, 9600, timeout1) time.sleep(2) x0, y0 80, 60 x1, y1 120, 100 steps 40 for i in range(steps 1): t i / steps x x0 (x1 - x0) * t y y0 (y1 - y0) * t cmd fX{int(x)} Y{int(y)} G0\n ser.write(cmd.encode()) time.sleep(0.05) ser.close()这种插补方式虽然简单但注意它并没有考虑机械臂运动学上的路径约束。理想情况下应该先把末端在笛卡尔空间的轨迹通过逆运动学解算成每个舵机的时间序列角度再对角度做插补这样的话运动才平滑。把逆运动学放在Python里做也行但移植到Arduino端做实时计算响应更快这也是我在4.4节代码里把解算放在板子侧的原因。6.3 精度提升的可能路径meArm的物理结构决定了它的绝对定位精度有限但可以通过一些手段提升它的实用价值。第一是换成带反馈的舵机。市面上的串行总线舵机比如LX-16A、串行总线舵机支持读取当前位置和设置目标位置形成闭环。这种情况下可以在Arduino端写一个简单的PID控制环让舵机每40ms比较一次目标角度和实际角度输出修正脉宽。这就是热词里“级联PID控制”在机械臂场景中的雏形——外层控制末端末端位置内层控制关节角度每一层都有反馈参与调节。第二是给每个关节加磁编码器。在小舵机的输出轴上粘一个直径很小的径向磁铁再用AS5600磁编码器芯片读取绝对角度这样即使舵机内部没有反馈外部也能获得实际关节角度。有了真实角度数据就可以在Arduino端做误差补偿和闭环控制精度提升非常明显。第三是从机械层面消除间隙。用3D打印件替换亚克力件设计时可预留过盈配合结构或者使用带轴承的关节方案。这样做成本会上升但结构的刚性和一致性都会好很多如果你想在meArm上做视觉抓取或者其他精度要求稍高的实验这一步值得考虑。7. 最终经验与调试心得这套项目跑下来个人最大的体会是机械臂控制难的不是代码而是对物理世界的敬畏。代码写错了大不了改一下重新烧录但供电设计不合理、机械安装不牢固、舵机选型不到位这些问题会在运行时反复折磨你而且表现千奇百怪。所以在开始阶段花时间在硬件可靠性的验证上是完全值得的。我列一个简单的检查清单每次调试前走一遍能省掉大量的排查时间确认外部5V电源已连接并且跟Uno共地确认舵机电源线正负极没有接反确认每个舵机信号线插在Uno正确的引脚上确认serial监视器波特率是9600且勾选了发送新行发送指令前先让机械臂处于一个已知的安全位置还有一个建议是保留一套完整的“备份方案”。我在调通之后把零件参数、offset值、代码版本都记录在一个文档里后面改代码改乱或者机械结构拆了重装可以快速恢复到可用状态。这款项目看似小但能延伸的方向很多接上超声波传感器做避障抓取、配上摄像头做颜色识别并夹取对应色块、升级成总线舵机做多机协同都是很好的毕业设计或者课程项目选题。从一个桌面小机械臂起步把底层原理摸透后面踩更大的坑时就有底气了。
返回列表