做一只四自由度机械臂:PCA9685、舵机与运动学
一只听从指令到达指定位姿的桌面机械臂——由 PCA9685 驱动,它用到的逆运动学,你在接上任何一只舵机之前就已经在仿真里弄懂并调好。
机械臂是第一个项目:机器人不再四处移动,而是对世界开始做些事情。循迹车跟随,避障车闪避,而机械臂够取、定位、抓握。从驱动到操作的这个转变,带来了所有机械臂赖以建立的那一个概念:运动学,也就是把你能指令的角度和真正想要的空间中的点连起来的那套几何。
这条制作路线正是这样看待它的:一堂你可以拿在手里的运动学课。你先理解两连杆机械臂如何把关节角变成工具位置、以及如何反过来算回去;再把一台舵机机械臂所需的零件收集齐,并弄清每个零件的作用;然后,在花钱或崩掉齿轮之前,先在浏览器模拟器里探索可达范围、肘部的两个解,以及够不到的位姿。只有到这一步,你才去接真实的机械臂,手工标定舵机,并在硬件上运行逆运动学。
技术树从上往下跟。每节点前置做完解锁,进度存这台设备,周末做完不丢位置。
物料单
| 零件 | 数量 | 约价 | 说明 |
|---|---|---|---|
| Arduino Uno | 1 | $5–8 | 哪块板都行——这里只说 I²C |
| PCA9685 16 通道驱动 | 1 | $3 | 驱动四个舵机严格来说并非必需,但它是对的工具 |
| SG90 舵机 | 4 | $8 | 大臂买 MG90S——金属齿,先坏的就是它 |
| 四自由度臂套件 | 1 | $12–25 | 亚克力用来学习够用;铝的形变要小得多 |
| 5–6 V 电源 ≥3 A | 1 | $8 | 四舵机齐堵约 2.6 A |
| 1000 µF 电容 | 1 | $1 | V+ 并,贴端子 |
| 备用舵盘 | 1 套 | $2 | 装错一个。人人都装错 |
合计约 $40–60。
有两件东西值得你动脑筋再买,而不是照默认来。大臂上的金属齿 MG90S 只贵一美元,而它正是一个要扛住其它所有关节重量的关节——尼龙齿轮在那里是由于持续负载而崩,不是由于冲击。还有按堵转电流选型的电源,因为机械臂最有代表性的失效方式是:当它撞上自己的框架或者抓到一个很结实的东西时,每一个舵机会同时堵转。
写代码前量臂
逆运动学就是拿连杆长度做算术。这四个数字就是全部输入,而从商品页上抄下它们而不是用尺子量,是机械臂自信地伸到错误位置的最常见原因。
| 符号 | 从 | 到 | 典型 |
|---|---|---|---|
L1 |
大臂关节轴 | 小臂关节轴 | 90–105 mm |
L2 |
小臂关节轴 | 夹爪实际夹持点 | 80–95 mm |
最后一行要做的事情比看上去多。“夹爪实际的夹持点”会随着钳口闭合而移动,而夹爪本身的选型是一个和定位分开的问题——它有自己的一堵墙、自己的算术和自己的失效模式。如果你的机械臂够到了零件,随后却把它掉了或者压坏了,答案并不在这一页上:请从夹爪模拟器开始。
| h | 桌面 | 大臂关节轴 | 50–70 mm |
| d | 底座转轴 | 大臂轴,水平 | 0–15 mm |
**在关节轴之间测量,而不是在支架边缘。**关节轴是舵机输出花键的中心,常常位于可见支架内侧 5–10 mm 处。在一根 95 mm 的连杆上,10 mm 的误差就超过了 10%,它会让机械臂系统性地偏短,而这种偏差是任何增益都修正不了的。
两长度工作空间直接:
maximum reach = L1 + L2 e.g. 100 + 90 = 190 mm
minimum reach = |L1 - L2| e.g. |100 - 90| = 10 mm
工作空间是一个环带,而不是一个圆盘。中间有一块机械臂物理上够不到的区域,因此靠近底座的目标返回“够不到”是正确行为,而不是缺陷。
里程碑制作
装配顺序一步几乎不可逆,第二步。
| # | 里程碑 | 测试 | 通过长这样 |
|---|---|---|---|
| 1 | 驱动有应答 | 扫描 I²C 总线 | 扫到 0x40;V+ 有自己独立的电源 |
| 2 | 装配前舵机回零 | 在压舵盘之前把每个舵机指令到 90° | 每个舵盘都装在你想要的零位姿上 |
| 3 | 单个舵机的限位 | 从中心逐步往外加,直到舵机开始嗡嗡响 | 你有了每台舵机各自的最小、最大计数,而不是库默认值 |
| 4 | 关节映射记下 | 每关节全行程指令 | 四关节偏移符号机械限位写下 |
| 5 | 碰撞检查 | 让每个关节缓慢走完整个行程 | 没有任何东西撞到框架;限位值反映你实际观察到的范围 |
| 6 | 正运动学 | 设定已知角度,算出工具位置,再量出来 | 算出的值和量到的值相差几毫米以内 |
| 7 | 往返一致 | 把算出的位置再送回逆运动学 | 输出的角度和输入的角度一致 |
| 8 | 四个象限 | 分别请求前方、后方、左侧、右侧的目标 | 四个都正常——如果有两个是镜像的,说明你用的是 atan 而不是 atan2 |
| 9 | 可达性 | 请求一个远超 L1 + L2 的目标 |
干净地返回“够不到”——不是 NaN,也不是猛地一扑 |
| 10 | 运动平滑 | 指令一次大范围移动 | 分若干步插值,所有关节同时进行,没有一顿 |
| 11 | 重复定位 | 从不同方向回到同一个位姿十次 | 测出落点的散布范围——这个数就是你的背隙 |
**里程碑 2 后面救不回。**舵盘花键——约 20 齿——离散位置约 18° 间隔装。先装配后标定,某关节可用范围偏 40° 机械无解。每舵机上电指令 90°,再压舵盘。
永远轻轻地移动
被指令到新角度的舵机会全速转到那里。在机械臂上这意味着一次剧烈的运动、四个舵机同时出现的电流尖峰,以及一次穿过尼龙齿轮的机械冲击,而机械臂自身的杠杆还会把它放大。
// Interpolate every joint together, so the tool takes a sensible path and
// no single servo carries the whole transition.
void moveTo(float base, float shoulder, float elbow, int steps, int stepMs) {
static float cur[3] = {0, 0, 0};
const float target[3] = {base, shoulder, elbow};
for (int s = 1; s <= steps; s++) {
const float t = (float)s / steps;
for (int j = 0; j < 3; j++) {
const float a = cur[j] + (target[j] - cur[j]) * t;
pwm.setPWM(JOINT[j].channel, 0, countsFor(JOINT[j], a));
}
delay(stepMs);
}
memcpy(cur, target, sizeof(cur));
}
所有关节一起插值和做平滑本身一样重要。逐个关节依次运动,会让工具在工作空间里走出一条奇怪的弧线——既慢得多,半路撞上东西的可能性也大得多。
做好长什么样
| 测量 | 亚克力套件 SG90 | 铝 MG90S |
|---|---|---|
| 重复定位,同一接近方向 | ±3–5 mm | ±1–2 mm |
| 重复定位,混合接近方向 | ±8–15 mm | ±3–5 mm |
| 全伸可用负载 | 20–30 g | 40–60 g |
| 全伸时下垂,无负载 | 3–8 mm | 1–3 mm |
上面那两行重复定位数据之间的差距就是背隙,而它是入门级机械臂上最大的单一误差来源。大臂上 4° 的空程放在 190 mm 的臂展上,在夹爪端就是超过 13 mm。实用的答案不是更好的数学——而是永远从同一个方向接近目标,这会把符号变化整个消掉,把 ±13 mm 的误差变成一个可以标定掉的、可重复的固定偏移。
故障排查
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 机械臂完全伸到了错误的位置 | 舵盘装在任意一个角度上 | 装舵盘之前先把每个舵机归位到 90° |
| 某关节范围偏约 18° | 舵盘差一齿 | 拔了重装——软件修不了 |
| 前面正常、后面却镜像 | 用了 atan 而不是 atan2 |
atan2 能解出全部四个象限 |
| 逆运动学返回 NaN 而且机械臂猛地一扑 | acos 的参数超出了 −1 到 1 |
在做三角计算之前先检查可达性 |
| 靠近底座的目标失败 | 落在最小臂展的空洞里 | 正确行为——工作空间本来就是环带 |
| 臂一动板子复位 | 舵机电流过 Arduino | 独立 V+ 电源,共地,1000 µF 电容 |
| 全伸臂塌 | 大臂超舒适扭矩 | 大臂 MG90S;橡皮筋配平 |
| 保持位姿嗡嗡 | 舵机极限顶负载 | 减负载或不保持 detach |
| 接近全伸时关节速度出现尖峰 | 驱动路径穿过了奇异形 | 把规划路径留在边界以内并留出余量 |
| 从不同方向过来落点不一致 | 背隙 | 永远从同一个方向接近 |
| 所有角度都系统性地偏短 | PCA9685 振荡器不是精确的 25 MHz | 实测输出频率并调用 setOscillatorFrequency |
下一步去哪
**加上直线移动。**上面那段代码是在关节空间插值,于是工具走的是一条曲线。而在笛卡尔空间插值——沿着一条直线每一步都计算逆运动学——就会让它真正走直线,这正是任何真实的抓取搬运所需要的。这是个小改动,也是奇异形从理论变成现实的地方:一条接近全伸的直线路径会要求极大的关节速度。
**加上相机并闭合成环。**这里的一切都是开环的:机械臂去它被指到的地方,从不核对。用相机读取物体上的 ArUco 标记,机械臂就能修正背隙和下垂留下的误差——一台便宜机械臂就是这样变准的。
**一旦碰到天花板,就升级硬件。**铝连杆去掉大部分形变,MG90S 舵机去掉大部分背隙,六自由度机械臂加上一个手腕,让工具的接近角度由你自己决定。这些都不改变数学本身:你在这里写的二连杆求解器,和更好的机械臂用的是同一个,这也正是先在便宜框架上搭一遍值得做的原因。
项目路线图
搭建路径
沿着技术树,从零件一路走到一台能正常工作的机器人。每个节点在其前置条件完成后解锁,你的进度会保存在这台设备上。
已完成 0 / 13
元器件
本路径中的教程
接线之前先练习
用在线模拟器把它调好
搭建路径会经过一个浏览器实验台。先在这里把行为调对,再把参数迁移到真实的机器人上。
常见问题解答
怎样用 Arduino 做一只机械臂?
把四只舵机装进一个四自由度框架——底座、肩、肘和夹爪——并通过 I²C 用 PCA9685 驱动它们,这样 Arduino 就不会缺 PWM 引脚。标定每只舵机从角度到脉宽的范围,然后把目标点送进一个二连杆逆运动学求解器,由它返回肩关节和肘关节的角度。这条制作路线会带你走完整个链条,而且你会在接任何线之前,先在模拟器里调好几何参数。
四自由度机械臂需要几只舵机?
四只——每个自由度一只:可旋转的底座、肩、肘和夹爪。每只舵机都需要自己的 PWM 通道,这正是多关节机械臂要用 PCA9685 驱动板,而不是 Arduino 那几个硬件 PWM 引脚的原因。
机械臂一定要用 PCA9685 吗?
它是合适的工具,但严格来说并非必需。只要单独供电,四只舵机也能直接由 Arduino 驱动;但 PCA9685 能释放单片机的定时器,只用两个 I²C 引脚驱动所有关节,并让舵机电流走它自己的 V+ 电源——这样大电流就远离了你的逻辑电路,主板也不会欠压。
开始这个项目之前必须先买零件吗?
不必。运动学和位姿求解都在浏览器模拟器里运行,所以你可以在买任何东西之前,先理解并探索机械臂的可达范围、它的两组解,以及它够不到的位姿。只有最后几步——接线、标定和运行逆运动学——才需要实体的 Arduino、PCA9685、舵机、机械臂框架和电源。
逆运动学在机械臂里是怎么用的?
正运动学告诉你,给定关节角,工具最终会在哪里;逆运动学做的是更有用的反向运算——给定一个目标点,返回能到达它的关节角。对这只机械臂来说,肩和肘构成一个二连杆平面求解器,底座负责让这个平面对准目标,夹爪负责抓取。模拟器让你看到求解器的工作过程,包括目标在哪里超出了可达范围。