最优永存:用雅可比伪逆求解 IK
在 Unity 里拖动一个 IK Target 时,我们希望角色的手、脚或者武器挂点跟着目标移动,而不是手动调骨骼链上的每一个关节。
这个需求说起来很直观:目标在那里,让手过去就行。但真正开始写 Solver,就会遇到几个问题:
- 一条骨骼链可能有很多自由度,同一个目标通常不止一个解;
- 目标可能在骨骼链的可达范围之外;
- 手臂完全伸直时,某些旋转几乎无法带来有效位移;
- 更新太激进会抖动甚至发散,太保守又会在有限帧数内追不上目标。
于是我把 CCD、Jacobian Transpose、固定阻尼 DLS 和基于 SVD 的自适应 DLS 都塞进 Unity,拿同一组目标点跑了一遍。这篇就顺着实现过程聊两个问题:雅可比矩阵在 IK 里究竟代表什么,以及这些 Solver 到底该怎么选。
完整代码放在 Gelercatty/IK。

从 FK 到 IK
先看一条由
从根节点一路把变换乘到末端,就得到了末端执行器的位置:
这就是正向运动学(Forward Kinematics,FK):给定关节参数
IK 做的是反方向的事。现在我们只有目标位置
麻烦在于,FK 是一串旋转和平移的连乘,关于关节角通常是非线性的。想直接把它“倒过来”求解,大多数时候并不现实。
两种不需要矩阵求逆的办法
Two-Bone IK
如果链上只有两段骨骼,例如上臂和前臂,可以利用余弦定理直接求解析解。先根据目标距离算出肘关节夹角,再结合指定的弯曲平面恢复肩部旋转。
Two-Bone IK 很快、很稳定,也容易控制肘部朝向,因此游戏里经常直接用于手臂和腿。但它依赖“两段骨骼”这个特殊结构,不能自然地扩展到尾巴、触手或脊柱这样的长链。
CCD
CCD(Cyclic Coordinate Descent)更通用。它从末端向根节点倒着遍历关节,每次只旋转一个关节,让“关节到末端”的方向尽量对齐“关节到目标”的方向。
1 | repeat: |

CCD 的优点是简单直观,而且不依赖大型矩阵运算。缺点也很明显:它逐关节贪心更新,容易产生比较夸张的弯曲;当需要同时处理末端朝向、多个控制点或软约束时,逻辑会迅速变复杂。
雅可比矩阵:关节动一点,末端会往哪走?
雅可比矩阵在这里并不神秘。它回答的是一个非常具体的问题:
当前姿态下,每个关节沿自己的自由度转动一点,末端执行器会产生怎样的瞬时位移?
把 FK 记为
在当前姿态附近做一阶近似:
令当前误差为
那么一次关节更新需要尽量满足
原本的非线性 IK,就这样被变成了一个局部线性问题。每次只相信当前姿态附近的一小步,更新关节后重新计算 FK 和 Jacobian,再继续下一轮。
1 | q = initial_pose |
不同 Jacobian Solver 的核心区别,基本都集中在 SolveDeltaQ 这一步。
不用真的去求所有偏导
如果末端只约束三维位置,
设某个旋转自由度的关节位置为
因此 Jacobian 的第
整条链的位置 Jacobian 就是
这在实现上很方便:执行一次 FK,拿到所有关节的世界坐标位置和旋转轴,就可以按列组装 Jacobian,不需要在运行时做符号求导。
如果还要约束末端朝向,可以把角速度 Jacobian 拼在位置部分下面:
同理,想让肘部靠近某个平面、让武器挂点保持稳定,或者同时控制多个末端,也可以继续追加误差项和 Jacobian 子块。这是 Jacobian 方法相比 CCD 更有扩展性的地方。
直接使用 的局限
如果
但游戏里的 IK 很少这么规整:
- 自由度多于约束时,
是宽矩阵,同一个末端目标可能有无穷多组关节解; - 约束多于自由度时,
是高矩阵,目标通常只能近似满足; - 骨骼完全伸直或折叠时,一些列会趋于相关,
会掉秩并接近奇异。
以完全伸直的手臂为例。此时两个关节带来的末端运动方向非常接近,某些方向几乎无法通过微小旋转实现。数学上表现为 Jacobian 的小奇异值;视觉上则表现为关节突然大幅旋转、来回震荡,或者怎么迭代都推不动末端。
因此我们通常不显式计算逆矩阵,而是选择更适合实时迭代的增量求解方法。
三种 Jacobian 更新方式
Jacobian Transpose
把位置误差写成能量
其负梯度方向可以由
其中
它不需要求逆,计算便宜,很适合实时游戏。但它对步长敏感:太小追不上,太大容易越过目标并震荡。
伪逆
Moore-Penrose 伪逆给出
在超定系统中,它给出最小二乘解;在欠定系统中,它给出满足约束的最小范数解。不过,接近奇异位形时,小奇异值会被取倒数并大幅放大,造成很大的关节更新。
阻尼最小二乘 DLS
DLS 在误差之外再惩罚过大的关节变化:
对应的解为
如果对
就能更直接地观察每个方向的可控性。普通伪逆在奇异方向上的增益是
从而抑制小奇异值带来的爆炸性放大。还可以根据最小奇异值动态调整
把几种 Solver 拉到同一张跑道上
我在 Unity 6.4 中实现了一套独立的骨骼链数据结构、FK、Jacobian 构建器和多种 Solver。下面两张图分别展示了目标可达和目标超出链长的情况。


上场的选手有:
- CCD;
- Jacobian Transpose;
- 固定阻尼 DLS,
; - 基于 SVD 的自适应阻尼 DLS。
可达目标和不可达目标各使用 200 个固定随机种子生成的样本,所有 Solver 共用同一批目标。最大迭代次数为 16,位置容忍度为 0.1,并在
除了成功率、最终误差和耗时,我还记录了关节相对初始姿态的平均旋转偏移。这个指标很重要:两个 Solver 即使都把手送到了目标点,其中一个可能只是轻微调整整条链,另一个却把中间关节扭得很夸张。
可达目标

不可达目标

为了方便横向看,我把成功率、迭代次数、运行时间、最终误差和关节偏移压成了一个归一化加权分数。这个分数不是 IK 界的英雄榜,只代表在这组测试条件下,我更在意哪种折中。

| Solver | 最佳步长 | 综合分数 |
|---|---|---|
| Jacobian Transpose | 0.05 | 79.1 |
| CCD | 0.05 | 75.7 |
| DLS, |
0.05 | 73.2 |
| SVD 自适应 DLS | 0.05 | 71.9 |
| DLS, |
0.05 | 70.7 |
| DLS, |
0.02 | 66.7 |
结果比“高级算法一定更好”更有意思
这次测试里,最简单的 Jacobian Transpose 反而拿到了最高综合分。原因并不神秘:迭代预算只有 16 次,容忍度也相对宽松。在这种设置下,低成本、推进速度快的方法占了优势。
几个比较明显的现象:
- 步长往往比 Solver 名字更重要。 步长低于 0.01 时,大多数方法不是数值发散,而是根本来不及走到目标;进入 0.02 到 0.05 后表现才明显改善。
- CCD 仍然非常能打。 它速度快、容易实现,也能稳定接近可达目标;代价是关节旋转偏移较大,姿态容易显得激进。
- DLS 的价值是稳定,而不是免费提升精度。 较大的固定阻尼在这次测试中比低阻尼更好,说明抑制大幅关节更新确实有效,但它也付出了更多运行时间。
- SVD 自适应阻尼没有在这个简单场景里赢。 它更适合多约束、严格精度和明显奇异位形。对于单末端位置、短迭代预算,额外计算还没有转化为足够收益。
所以 IK Solver 没有绝对排名。手脚的 Two-Bone IK、尾巴的 CCD、需要多目标和关节限制的 DLS,完全可能同时出现在一个项目里。
实际项目里我会怎么选
- 两段手臂或腿: 优先 Two-Bone IK,配合明确的弯曲方向。
- 简单长链、追求低成本: 先试 CCD。
- 需要在有限帧数内快速逼近: Jacobian Transpose 很实用,但一定要认真调步长或做线搜索。
- 容易遇到完全伸直、不可达目标或多约束: 使用 DLS,并对步长和阻尼做上限控制。
- 需要分析奇异方向或自适应稳定性: 再考虑 SVD;不要因为它数学上更完整就默认它一定更快。
无论选择哪种方法,工程上都建议加入:最大单步关节角、关节限位、不可达目标预处理、误差停机条件,以及对目标和关节更新的时间滤波。Solver 负责找到“能走的方向”,动画系统还要负责让它看起来像角色,而不是一串数学关节。
代码与参考
- 项目代码:Gelercatty/IK
- 胡敏课程:GAMES103 - 基于物理的计算机动画入门
- Unity 文档:Animation Rigging
- Title: 最优永存:用雅可比伪逆求解 IK
- Author: GelerCAT
- Created at : 2026-04-06 18:58:41
- Updated at : 2026-09-04 13:24:26
- Link: https://redefine.ohevan.com/2026/04/06/用雅可比矩阵做一个Unity IK Solver/
- License: This work is licensed under CC BY-NC-SA 4.0.