4、运动学基础(三):逆向运动学(IK)解析法与数值法
好,咱们接着聊。前面两章我们把正运动学搞定了——给定关节角度,算出末端位姿。但实际干活的时候,问题往往是反过来的:我知道手要伸到那个点,也知道手掌要朝哪个方向,那每个关节该转多少度?
这就是逆向运动学(Inverse Kinematics,IK)。说白了,IK 就是正运动学的逆映射。但你别指望它像正运动学那样简单直接——正运动学是“一个萝卜一个坑”,给定角度就有唯一位姿;IK 呢?可能无解、可能多解、可能无穷多解。我刚开始做机器人那会儿,就被这个“多解”坑过好几次。
4.1 问题定义:从位姿到关节空间
先明确一下数学描述。我们有正运动学方程:
T = f(q)
其中 T 是末端位姿矩阵(4×4 齐次变换矩阵),q 是关节角度向量。IK 就是已知 T,求 q:
q = f⁻¹(T)
听起来简单?但 f 是非线性函数,它的逆通常没有闭式解。而且 IK 有几个天然难题:
- 存在性:目标位姿在不在工作空间内?
- 多解性:同一个位姿,可能有多个关节构型(比如“肘上”和“肘下”)
- 奇异性:某些位姿下,关节速度会突变甚至无穷大
核心要点:IK 不是求一个解,而是从多个候选解中选一个“合理”的解。什么叫合理?关节限位内、无碰撞、运动平滑——这些约束往往比 IK 本身更难搞。
4.2 解析法:精确但挑剔
解析法,也叫封闭解法。就是通过代数或几何推导,直接写出关节角度的表达式。它的优点是快、准、稳——一次计算就能得到所有解。
但解析法有个前提:机器人结构必须满足 Pieper 准则。Pieper 说,如果机器人的最后三个关节轴线交于一点(比如大多数工业六轴),或者前三个关节轴线交于一点,那么 IK 就有解析解。
| 条件 | 典型结构 | 解析解情况 |
|---|---|---|
| 最后三轴交于一点 | PUMA 560、UR5 | 有闭式解,可分离位置和姿态 |
| 前三轴交于一点 | 部分球腕机器人 | 有闭式解 |
| 不满足 Pieper 准则 | 7自由度冗余臂、非球腕 | 一般无解析解,需数值法 |
我记得有一次做项目,客户非要在一个非球腕的六轴上用解析法。我劝了半天没用,最后硬推了三个月,发现某些位姿下方程根本解不出来。嗯,最后还是老老实实换了数值法。
4.2.1 解析法的一般步骤
以典型的六轴球腕机器人为例,解析法通常分两步:
- 位置解耦:利用最后三轴交点的位置,先解前三个关节
- 姿态解耦:已知前三个关节,再解后三个关节的姿态
具体来说,对于球腕机器人,腕点位置只由前三个关节决定。我们可以把末端位姿拆成:
T = T₁(q₁,q₂,q₃) · T₂(q₄,q₅,q₆)
其中 T₁ 是前三个关节到腕点的变换,T₂ 是腕点到末端的姿态变换。先解 T₁ 得到 q₁,q₂,q₃,再解 T₂ 得到 q₄,q₅,q₆。
个人经验:解析法虽然快,但推导过程极其繁琐。我建议你先把 DH 参数列清楚,然后用符号计算工具(比如 SymPy)辅助推导。手算?我试过一次,三天没出结果,后来再也不干这种傻事了。
4.2.2 解析法的优缺点
| 优点 | 缺点 |
|---|---|
| 计算速度快,适合实时控制 | 只适用于特定结构(Pieper 准则) |
| 能得到所有解,便于选择 | 推导过程复杂,容易出错 |
| 精度高,无迭代误差 | 对参数误差敏感 |
| 适合嵌入式实现 | 修改结构后需重新推导 |
4.3 数值法:通用但需要调教
数值法不追求显式表达式,而是通过迭代优化逼近解。最经典的是雅可比伪逆法,也叫牛顿-拉夫森法在 IK 上的应用。
核心思想很简单:
- 给定一个初始猜测 q₀
- 计算当前末端位姿与目标位姿的误差 Δx
- 用雅可比矩阵 J 把 Δx 映射回关节空间:Δq = J⁺ · Δx
- 更新 q = q + Δq
- 重复直到误差足够小
其中 J⁺ 是雅可比矩阵的伪逆。对于非冗余机器人(6自由度),J 是方阵,伪逆就是普通逆矩阵。
while ||Δx|| > ε:
J = compute_jacobian(q)
Δq = np.linalg.pinv(J) @ Δx
q = q + Δq
Δx = target - f(q)
你想想看,这个流程是不是很像在爬山?每一步都往误差减小的方向走。但问题也来了:
- 初值敏感:初值不好,可能收敛到错误解
- 奇异问题:接近奇异位姿时,雅可比矩阵病态,Δq 会变得巨大
- 局部最优:数值法只能找到一个解,不是全部
避坑指南:我曾经在一个焊接机器人项目里,直接用纯伪逆法做 IK。结果在某个奇异点附近,关节速度指令直接爆表,电机差点烧了。后来加了阻尼项(DLS 方法)才稳住。数值法一定要处理奇异鲁棒性,别偷懒。
4.3.1 常见数值法对比
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 雅可比伪逆法 | Δq = J⁺ · Δx | 简单直接,收敛快 | 奇异时不稳定 |
| 阻尼最小二乘法(DLS) | Δq = (JᵀJ + λ²I)⁻¹JᵀΔx | 奇异鲁棒性好 | 有稳态误差 |
| Levenberg-Marquardt | 自适应调整阻尼 λ | 兼顾收敛速度和鲁棒性 | 参数调起来麻烦 |
| 循环坐标下降法(CCD) | 逐个关节优化 | 实现简单,无矩阵运算 | 收敛慢,精度一般 |
我个人习惯用 DLS 或者 LM 方法。DLS 的阻尼系数 λ 是关键——λ 太小,奇异时还是炸;λ 太大,收敛慢而且有误差。我一般从 λ=0.01 开始试,根据实际运动效果微调。
4.4 解析法 vs 数值法:怎么选?
这个问题没有标准答案,但根据我的经验,可以按下面这个思路来:
- 能用解析法就用解析法——如果机器人结构满足 Pieper 准则,解析法永远是第一选择。快、准、稳。
- 数值法做兜底——对于冗余机器人、非球腕结构、或者需要避障的场景,数值法更灵活。
- 混合使用——我见过很多工业机器人控制器,先用解析法算一个初值,再用数值法微调。这样既快又准。
一句话总结:解析法适合“结构固定、追求速度”的场景;数值法适合“结构复杂、需要灵活”的场景。如果你刚开始学,我建议先掌握解析法,理解 IK 的几何意义,再学数值法处理更复杂的问题。
4.5 本章知识体系
下面这张图把 IK 的整体脉络梳理了一下,你可以对照着看:
4.6 一个简单的数值 IK 实现
最后,给你一个 Python 风格的伪代码。这个实现用了阻尼最小二乘法,适合做原型验证:
def ik_dls(target_pose, q_init, dh_params, max_iter=100, tol=1e-6, lambda_=0.01):
q = q_init.copy()
for i in range(max_iter):
# 正运动学:当前位姿
T_current = forward_kinematics(q, dh_params)
# 计算误差(位置 + 姿态)
error = compute_error(target_pose, T_current)
if norm(error) < tol:
break
# 计算雅可比矩阵
J = compute_jacobian(q, dh_params)
# DLS 公式:Δq = (JᵀJ + λ²I)⁻¹Jᵀ · error
JtJ = J.T @ J
damping = lambda_**2 * np.eye(JtJ.shape[0])
delta_q = np.linalg.inv(JtJ + damping) @ J.T @ error
# 更新关节角度
q = q + delta_q
# 关节限位约束
q = np.clip(q, joint_limits[:,0], joint_limits[:,1])
return q
小技巧:实际调试时,我习惯把每次迭代的误差打印出来。如果误差震荡或者不降反升,说明阻尼系数太小或者步长太大。这时候我会把 λ 调大一点,或者加个线搜索(line search)——说白了就是试几个步长,选误差最小的那个。
好了,这一章就到这里。IK 是个大话题,后面我们还会在轨迹规划、避障、冗余控制里反复用到。你现在先把解析法和数值法的思路理清楚,后面就好办了。