资讯动态

Delta并联机械臂运动学正反解:从公式推导到代码实现

发布时间:2026/10/4 6:57:50 来源:尧图企业网站定制
最近在调一台Delta并联机械臂从伺服上电到稳定抓取中间最硬的一块骨头就是运动学正反解。网上关于串联机械臂的资料一抓一大把到了Delta这种并联结构要么是纯论文式推导要么是“代码拿走就能用但从没解释为什么”的片段。这篇文章把我从推导到落地的完整过程写下来包括正解、反解、坐标约定、代码实现以及调试中踩过的坑。适合正在做并联机器人、准备竞赛、写毕业设计或者刚接触机器人运动学但被公式劝退的朋友。1. Delta机器人的结构拆解与建模前提1.1 并联结构为什么反直觉先从结构说起。Delta机器人属于典型的并联机构和常见的六轴串联机械臂思路完全相反。串联臂是一个关节接着一个关节末端位置可以从基座开始沿着每一段杆件的方向一路累加过去正解好算反解靠迭代。Delta则是三组主动臂加三组平行四边形从动臂同时拉着同一个动平台三根链条一起约束动平台的位置。关键是平行四边形从动臂。每组从动臂由两根等长平行杆和两端的球铰组成形成平行四边形机构它的作用是死死限制动平台的姿态让动平台始终和基座保持平行没有旋转自由度。所以Delta末端只有X、Y、Z三个平动自由度这也是它速度快、刚度高、适合抓取的根本原因。但这种结构也带来了代价三个电机的角度和末端位置之间不是简单的叠加关系而是一组非线性约束方程。你给末端一个坐标每个电机该转多少不是直接“角度相加”能算出来的反过来你给三个电机角度末端在哪也需要解方程组。这就是运动学正反解的来源也正是很多人卡住的地方。1.2 坐标系与关键几何参数先定规矩再动手推导之前我强烈建议先统一坐标系和符号。Delta机构虽然品牌不同、尺寸不同但建模方法几乎一样。本文用的符号和参考值如下表后面所有代码都按这套参数来。参数符号参考值说明基座铰点分布圆半径R300 mm三个驱动电机铰点在基座上的分布圆半径动平台铰点分布圆半径r100 mm三个球铰在动平台上的分布圆半径主动臂长度La500 mm电机输出端到从动臂铰点的上臂长度从动平行四边形臂长度Lb900 mm从动臂的等效杆长各支链方位角φi0°、120°、240°三条支链在基座上的圆周布置角坐标系这样定原点O在基座中心Z轴向上动平台在基座下方所以末端位置在正常工作空间内Z坐标是负值。这个约定和大多数文献一致推导时符号不容易乱。参考值里的R300、r100只是示例实际设备上量出来是多少就填多少。三条支链的位置角φi分别是0°、120°、240°第一条支链放在X轴正方向。对任一支链i基座铰点Pi、主动臂末端Ai、动平台球铰点Bi的关系如下Pi (R·cosφi, R·sinφi, 0)主动臂在水平面内的投影沿径向且主动臂的转动轴沿圆周切线方向。设主动臂相对水平面的仰角为θi则 Ai ((R La·cosθi)·cosφi, (R La·cosθi)·sinφi, La·sinθi)动平台保持水平末端位置为P (x, y, z)则 Bi (x r·cosφi, y r·sinφi, z)约束条件就是从动臂长度不变|Ai - Bi| Lb。这一组式子就是整个运动学的地基。正解和反解本质上都是在解这个约束关系。这里有个新手容易犯的错Ai的表达式里La·cosθi代表主动臂在水平方向的投影方向是沿径向向外的。如果电机安装方式不同主动臂的旋转平面可能不是径向平面那公式要相应调整。我见过有人拿别人代码套自己的机械结构结果反解出来的角度总差一个固定偏移就是这个原因。2. 运动学反解从末端位置到电机角度2.1 反解思路把三维约束拆成三个平面反解是已知动平台位置(x, y, z)求三个主动臂的转角θ1、θ2、θ3。这个相对好算也是我建议先做的部分。核心思路是把三维空间问题拆成三个平面问题。对每条支链单独看它所在的“径向平面”。做法是把末端位置坐标投影到该支链的径向方向上去得到一个沿径向的投影值u和一个垂直径向的偏移量v。在支链i的局部坐标系里径向投影为 u x·cosφi y·sinφi垂直径向的偏移为 v -x·sinφi y·cosφi注意v这个量虽然是能算正负的但后面约束方程里它只以平方形式出现所以不需要纠结符号。再把基座和动平台的半径差合并成一个量 X R - r - u这样第i支链的约束方程就变成 (X La·cosθi)² (La·sinθi - z)² v² Lb²这里每一项的含义很直观第一项是主动臂末端在径向方向上相对动平台球铰点的水平距离第二项是垂直高度差第三项是支链平面外的偏移。三项平方和就是从动臂长度Lb。2.2 化成二次方程与解的选择把上面的约束方程展开整理可以化成关于θi的标准形式 2·La·X·cosθi - 2·La·z·sinθi (X² v² z² La² - Lb²) 0令 A 2·La·X B -2·La·z C X² v² z² La² - Lb²就得到 A·cosθi B·sinθi C 0这是一个非常经典的三角方程。用万能代换令t tan(θi/2)则 sinθi 2t/(1t²) cosθi (1-t²)/(1t²)代入整理后得到一个关于t的二次方程 (C - A)·t² 2B·t (A C) 0解出来 t [-B ± sqrt(B² - C² A²)] / (C - A)最终θi 2·atan(t)。这里有两个关键点第一判别式Δ A² B² - C²如果小于0说明这个目标点超出了该支链的可达范围整个目标位置在工作空间之外。代码里一定要先判断这个不要等到求根号时崩掉。第二每个θi会得到两个解对应主动臂的两种装配姿态一种“肘往外张”一种“肘往里收”。具体取哪个取决于机械结构的限位和装配方式。大多数Delta取主动臂大体朝下方、从动臂向外张的那个解但不同厂家会不一样。我的建议是代码里把两个候选都算出来通过一个配置项去选标定现场如果发现方向反了切一下这个配置就行。我自己的代码里统一取t较小的那个解但要验证和机械限位一致一定要实测确认。2.3 反解代码实现与算例验证下面是一段可直接测试的Python反解代码。注意Z轴向上约定所以算例里z-700表示末端在基座下方700mm。import math def delta_inverse(x, y, z, R300.0, r100.0, La500.0, Lb900.0): Delta机器人运动学反解 输入末端位置 x, y, z单位mmz一般为负 输出[theta1, theta2, theta3]单位弧度 若目标点不可达返回None phis [0.0, 2.0 * math.pi / 3.0, 4.0 * math.pi / 3.0] # 配置项True取较小的tFalse取较大的t take_smaller True angles [] for phi in phis: u x * math.cos(phi) y * math.sin(phi) v2 (x * math.sin(phi) - y * math.cos(phi)) ** 2 X R - r - u A 2.0 * La * X B -2.0 * La * z C X * X v2 z * z La * La - Lb * Lb delta A * A B * B - C * C if delta 0: return None # 目标点不可达 sqrt_delta math.sqrt(delta) # 解二次方程 (C - A)*t^2 2B*t (A C) 0 denom C - A if abs(denom) 1e-9: # 退化为一次方程 t -(A C) / (2.0 * B) if abs(B) 1e-9 else 0.0 else: t1 (-B sqrt_delta) / denom t2 (-B - sqrt_delta) / denom t t1 if (t1 t2) take_smaller else t2 theta 2.0 * math.atan(t) angles.append(theta) return angles if __name__ __main__: target (200.0, 50.0, -700.0) angles delta_inverse(*target) print(反解结果(rad):, angles) print(反解结果(deg):, [math.degrees(a) for a in angles])我用参考参数跑了一下目标(200, 50, -700)的反解结果大约在0.09到0.16弧度附近也就是几度到十几度电机摆角不大符合直觉。这个数值因为机构的安装方向可能有人算出来是负的角度没关系关键是正反解能不能互相吻合。判断反解写得对不对最直接的办法是反解出来的角度再送回正解看末端坐标能不能回到原始值。这也是我建议在写完反解后立刻做的自测流程比对着示波器看角度靠谱得多。3. 运动学正解从电机角度回到末端位置3.1 三个球面的交点问题正解比反解麻烦。已知三个θi求末端位置(x, y, z)。从几何上看每条支链的主动臂末端Ai是确定的点从动臂长度Lb固定所以动平台上的球铰点Bi必须落在以Ai为球心、Lb为半径的球面上。三个支链共同限制末端位置就是三个球面的交点。三个球面相交一般情况下有两个交点一个是机构实际能到的位置另一个是数学上存在但机构到不了的“虚交点”。Delta实际机构会通过限位和装配避开虚位置所以正解要做的是从两个交点里挑出符合物理含义的那一个。解析解不是没有但推导过程会引出高次多项式工程上很少人用。实际控制中更常用的是数值迭代法也就是牛顿-拉夫逊迭代先猜一个末端位置算出它和真实位置的差距反复修正直到残差足够小。3.2 迭代初值怎么选迭代法最关键的是初值。初值离真实值太远可能收敛到虚交点甚至直接发散。我总结出三种比较实用的初值选取方式。第一种如果机器人正在运行中直接用上一控制周期的末端位置作为当前迭代初值。Delta循环周期一般1到4ms相邻周期位置变化很小通常迭代两三次就能收敛到毫米级甚至更小。这是实时控制里最常用的方式。第二种如果机器人刚上电还没有位置信息可以用三个球心坐标的平均值作为初值的x、yz方向初值用三个球心z坐标的平均值再减掉Lb。这个初值比较粗略有些位姿下可能不收敛但只要第一次迭代成功保存下位置之后就能切回第一种方式。第三种借助反解来给正解“搭桥”。比如在调试上位机时可以随机给一组目标位置用反解算出对应的关节角再用这组关节角做正解的初值。这个方式本质上是在工作范围内给正解找一个靠谱的起点测试代码时很有用。真正上控制板的时候我强烈建议把初值逻辑写成这样有上一周期位置就用上一周期位置否则用球心均值减去Lb的粗略估计再不行才报错。千万不要每次迭代都用同一个固定初值去解那会遇到大量“正解发散”的假故障。3.3 正解代码实现与正反解互验正解的牛顿迭代实现如下。这个函数同时适合上位机测试和嵌入式实时运行唯一要注意的是嵌入式环境下别用动态内存分配矩阵运算用固定大小的数组。import numpy as np def delta_forward(angles, R300.0, r100.0, La500.0, Lb900.0, init_posNone, tol1e-6, max_iter50): Delta机器人运动学正解牛顿-拉夫逊迭代 输入angles[theta1, theta2, theta3]弧度 init_pos迭代初值None则使用球心估计初值 输出(x, y, z)单位mm phis [0.0, 2.0 * np.pi / 3.0, 4.0 * np.pi / 3.0] # 计算三个球心坐标和球半径 centers [] for i in range(3): theta angles[i] phi phis[i] cx (R La * np.cos(theta)) * np.cos(phi) cy (R La * np.cos(theta)) * np.sin(phi) cz La * np.sin(theta) centers.append((cx, cy, cz)) # 生成初值 if init_pos is None: cx_bar sum(c[0] for c in centers) / 3.0 cy_bar sum(c[1] for c in centers) / 3.0 cz_bar sum(c[2] for c in centers) / 3.0 pos np.array([cx_bar, cy_bar, cz_bar - Lb], dtypefloat) else: pos np.array(init_pos, dtypefloat) def residual(p): r_arr np.zeros(3) for i in range(3): dx p[0] - centers[i][0] dy p[1] - centers[i][1] dz p[2] - centers[i][2] r_arr[i] dx*dx dy*dy dz*dz - Lb*Lb return r_arr def jacobian(p): J np.zeros((3, 3)) for i in range(3): J[i, 0] 2.0 * (p[0] - centers[i][0]) J[i, 1] 2.0 * (p[1] - centers[i][1]) J[i, 2] 2.0 * (p[2] - centers[i][2]) return J for _ in range(max_iter): r residual(pos) if np.max(np.abs(r)) tol: break J jacobian(pos) try: delta np.linalg.solve(J, -r) except np.linalg.LinAlgError: return None # 雅可比奇异迭代失败 pos pos delta return tuple(pos) if __name__ __main__: # 先用反解算一组角度再用正解验证 target (200.0, 50.0, -700.0) angles delta_inverse(*target) if angles is None: print(反解失败) else: got delta_forward(angles, init_pos(0, 0, -1000)) print(目标位置:, target) print(正解位置:, got) print(误差:, tuple(got[i] - target[i] for i in range(3)))我自己测试时的典型结果是迭代3到5次之后误差就在1e-6毫米量级。如果初值用上一周期位置通常两次迭代就够。正反解互验这段代码看起来简单实际上是整个运动学模块的看门狗。每次改完代码我都会把一组典型位置和一组极限位置各跑一遍正反解互验。曾经有一次我改了R的数值单位从毫米写成厘米互验结果立刻差了10倍靠这个才抓出问题。所以强烈建议把互验做成自动化测试每次提交代码前跑一遍。4. 实操踩坑笔记误差、奇异与调试经验4.1 装配误差与参数标定运动学模型建的再漂亮机械装出来也会有偏差。R、r、La、Lb这四个参数图纸上的值和实际值之间往往差那么几毫米。Delta的从动臂是平行四边形结构球铰间隙和安装偏心也会等效改变从动臂长度Lb。这几毫米放在理想模型里不算什么但放在实际抓取精度测试里可能就是几毫米甚至更大的末端定位误差这对高速抓取来说是致命的。所以设备落地后做一次参数标定非常有必要。最朴素的标定方法用激光跟踪仪或千分尺测量一组末端位姿记录下对应的编码器角度然后把参数当作未知量做最小二乘拟合。具体操作上把反解公式写成参数R、r、La、Lb的函数给定一组实测的(θi, P)利用它们之间的约束关系构造残差再用优化算法求解这四个参数的最优值。工程上不一定要用最玄乎的算法Levenberg-Marquardt就够用很多机器人工具链里都有现成实现。实测下来标定后的R和r变化不大但La、Lb因为球铰间隙的等效效果常常比图纸值大好几毫米。如果只标定一个参数优先标定Lb它对末端定位精度的影响最敏感。4.2 奇异位形与工作空间边界Delta机器人的工作空间不是一个规则球体而是近似一个倒置的“伞形”或“三棱柱”。边界由主动臂和从动臂的极限位置决定。有一个特别实用的判断方法在反解时如果某条支链的判别式Δ A² B² - C² 接近0就说明目标点逼近工作空间的边界了。在实际控制里我习惯在反解返回None之前多做一个判断如果Δ大于0但小于某个阈值就认为目标点已进入“预警区”停止执行或者降低速度。这个阈值和设备速度有关我常用的是0.5到1毫米对应的判别式余量具体要对设备实测调整。更危险的是奇异位形。Delta的奇异通常发生在主动臂和从动臂接近共线的时候比如动平台被举到接近基座平面、某条从动臂完全伸直或完全折叠的瞬间。这时候机构会突然失去某个方向的约束控制上表现为关节力矩异常、末端抖动甚至机构弹跳。这类位置必须靠软限位提前挡掉不能等反解报错才处理。4.3 实时性优化与常见问题速查运动学正反解在Delta的控制周期里占用时间极短一般只有几十微秒但代码写不好也会埋雷。我踩过的问题都整理在下面。现象可能原因处理方式反解返回无解目标点超出工作空间检查轨迹规划是否越界缩小运动范围正解迭代发散初值离真实位置太远使用上一周期位置作为初值必要时加阻尼迭代正反解互验不一致坐标定义、单位或参数不一致检查z轴方向、毫米/米制、R/r/La/Lb数值末端定位有固定偏差编码器零点偏移或参数标定不准做零点标定再做参数标定跑到特定位置机构抖动接近奇异位形用反解判别式设软限位避开奇异区域两条支链角度总差固定值电机方向或减速比配置错误单支链手动点动正向确认角度符号嵌入式优化方面有几个特别实惠的技巧。第一算反正切时如果用的是DSP或高性能ARM直接调库函数没问题如果MCU性能弱可以先算出sin、cos查表近似或者直接用CORDIC。第二矩阵求逆在正解迭代里会出现但3x3矩阵的求逆有解析公式写成固定公式比调用通用矩阵库快得多。第三正解迭代的收敛条件不要只看位置增量也要看残差否则在某些斜率极小的区域会误判收敛导致输出还是错的。还有一点关于球铰间隙的体会。Delta机器人跑一段时间后球铰磨损会导致回差增大运动学模型参数也会漂移。这时候最典型的现象是正反解互验依然一致但实际抓取精度下降而且空载速度快的时候末端有轻微“画圈”的轨迹误差。处理办法是定期检查球铰间隙必要时整套更换从动臂球铰然后重新标定参数。这个问题容易被忽略因为纯粹看软件模型永远挑不出毛病。最后分享一个调试上的小习惯把正反解模块的代码写干净之后我会在示波器里同时看三路“反解角度”和三路“实际编码器角度”做一次手动画圆或者矩形轨迹的运动。如果两者波形完全重合说明运动学模块至少没有方向性和零点错误如果存在随位置变化的偏差再去查参数标定和机械间隙。这套排查顺序帮我省下了大量现场调试时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑