两根长度为 1 的杆,从原点伸向目标 (1,1)。给定关节角度,末端位置很容易计算;反过来给定末端位置,应该把两个关节转到哪里?这就是一个最小的逆运动学问题。

第 30 篇先指定骨骼姿态,再用权重带动网格。本篇向前多走一步:从目标位置求姿态。实验同时保留解析解和迭代解,检查“够不着”“迭代没有求到”和“杆长发生变化”这三种不同情况。一个可达目标,也可能让梯度迭代从第一步就停住。

先把杆长放进参数化

肩关节位于原点,两杆长度为 l₁、l₂,肩角为 θ₁,肘角 θ₂ 相对第一根杆定义。角度使用弧度,位置使用统一的模型长度单位。肘位置 q 和末端 p 是:

q=l1(cos⁡θ1sin⁡θ1),p=q+l2(cos⁡(θ1+θ2)sin⁡(θ1+θ2)).q=l_1\begin{pmatrix}\cos\theta_1\\\sin\theta_1\end{pmatrix},\qquad p=q+l_2\begin{pmatrix}\cos(\theta_1+\theta_2)\\\sin(\theta_1+\theta_2)\end{pmatrix}.

每次只更新两个角度,再重新计算位置,理论上就有 |q|=l₁、|p−q|=l₂。无需在每轮末尾拉伸或投影杆件来修补长度。

目标 t 的误差是 eₜ=|p−t|;杆长误差另记为 eₗ=max(||q|−l₁|, ||p−q|−l₂|)。前者衡量任务完成程度,后者检查几何约束。整条迭代轨迹中,eₗ 最大只有 2.220446049250313×10⁻¹⁶,但有些轨迹的 eₜ 始终为 1。长度正确不能代替目标误差检查。

这也区别于第 30 篇的线性混合蒙皮:那里两个刚性骨骼的加权结果可能收缩,这里直接用关节角度构造杆件,保持的是两段中心线长度。

可达环带与两个肘部方向

把两杆及原点到目标的连线看成三角形,三角不等式给出必要条件;反过来,满足该条件的三角形也能通过两个关节角构造出来。因此,没有关节角限制时,可达区域恰为:

∣l1−l2∣≤r≤l1+l2,r=tx2+ty2.|l_1-l_2|\le r\le l_1+l_2,\qquad r=\sqrt{t_x^2+t_y^2}.

注意这里比较的是距离 r,不能把平方距离与杆长直接比较。将正向位置平方展开,得到余弦定理和两组角度:

c=r2−l12−l222l1l2,s=±1−c2,θ2=atan2⁡(s,c),c=\frac{r^2-l_1^2-l_2^2}{2l_1l_2},\quad s=\pm\sqrt{1-c^2},\quad \theta_2=\operatorname{atan2}(s,c),

θ1=atan2⁡(ty,tx)−atan2⁡(l2s,l1+l2c).\theta_1=\operatorname{atan2}(t_y,t_x)- \operatorname{atan2}(l_2s,l_1+l_2c).

s 的两个符号对应两个肘部方向。等长杆到 (1,1) 的两组解是 (0,π/2) 与 (π/2,−π/2),末端相同,中间关节不同。在伸直或完全折叠的边界上,两分支会合并。atan2 保留象限信息;角度相差整周时也可以描述相同姿态。这套几何构造可对照 Illinois ECE 470 的逆运动学讲义第 3–5 页。

第一种肘部方向:肩角为零、肘角为直角

第二种肘部方向:肩角为直角、肘角为负直角

目标不可达时,程序仍返回一个最近姿态,但明确保留 reachable=false。先把 r 限制到可达环带,再求角度,最小目标残差为 |r−clamp(r,|l₁−l₂|,l₁+l₂)|。

杆长 目标 可达 最小残差
1、1 (3,0) 否 1
1、0.5 (0.1,0) 否 0.4
1、0.5 (0.5,0) 或 (1.5,0) 是 0
0.5、1 (0,0) 否 0.5

原点还需要单独处理:等长杆完全折叠即可到达,肩角任意;不等长杆则有一整圈同样近的末端位置,仍然够不到原点。程序为这些方向不唯一的情况选一个代表姿态,不能把“存在一族最近解”写成“存在一族精确解”。等长折叠的浮点残差约为 1.22×10⁻¹⁶,来自三角函数求值。

用 Jacobian 把位置误差传回关节

解析解给出了可达性和最优残差,接下来用它检查迭代法。定义目标函数 F(θ)=½|p(θ)−t|²,对两个角度求导:

J=∂p∂θ=(−py−l2sin⁡(θ1+θ2)pxl2cos⁡(θ1+θ2)),g=JT(p−t).J=\frac{\partial p}{\partial\theta}= \begin{pmatrix} -p_y&-l_2\sin(\theta_1+\theta_2)\\ p_x&l_2\cos(\theta_1+\theta_2) \end{pmatrix},\qquad g=J^T(p-t).

一轮更新是 θ←θ−ηg。源码在三个非奇异姿态上用中心差分独立检查梯度,差分间距为 10⁻⁶,两分量误差均小于 10⁻⁸。

η 是优化步长。第 31、32 篇的 dt 表示物理时间,这里的一次迭代没有对应多少秒,也没有质量、力或速度状态。把求解过程播放成动画,不会使它自动成为物理运动;橙色轨迹表示历次末端位置。

位置单位也会影响合适的 η。若把杆长和目标坐标同时乘以 a,角度不变,位置残差与 Jacobian 都乘以 a,梯度便乘以 a²。要保持相同角度更新,η 应除以 a²。因此,同一个数值步长不能不经检查就搬到尺度不同的角色上。

步长多大才有下降保证

“较小步长比较稳”可以写成有条件的不等式。令目标极角为 φ,将 F 展开,去掉与角度无关的常数 K:

F=K+l1l2cos⁡θ2−l1rcos⁡(θ1−ϕ)−l2rcos⁡(θ1+θ2−ϕ).F=K+l_1l_2\cos\theta_2-l_1r\cos(\theta_1-\phi) -l_2r\cos(\theta_1+\theta_2-\phi).

三个余弦项的 Hessian 范数分别不超过 l₁l₂、l₁r、2l₂r,故全局上界可取 L=l₁l₂+l₁r+2l₂r。Taylor 余项给出:

F(θ−ηg)≤F(θ)−η(1−ηL2)∥g∥2.F(\theta-\eta g)\le F(\theta) -\eta\left(1-\frac{\eta L}{2}\right)\|g\|^2.

当 0<η<2/L 且 g≠0 时,一步更新保证下降。这个上界是充分条件,超过它不代表每一步必然变坏;满足它也不保证到达全局最优,更不保证固定预算内误差归零。

常规实验取两杆长度 1,目标 (1,1),初始角 (0.2,0.4)。此时 L=1+3√2,2/L≈0.381487。实际结果如下,最后一列统计 200 步中 F 比上一步增加超过 10⁻¹² 的次数。

η 10 步目标误差 40 步目标误差 200 步目标误差 F 上升次数
0.05 0.503746 0.426158 0.0500456 0
0.2 0.426680 0.0984427 3.41392×10⁻⁷ 0
2 2.17806 2.54428 3.26107 104

η=0.05 和 0.2 都满足下降条件,但预算效果不同。η=2 的轨迹反复增大目标函数,200 步后的误差甚至超过两根杆的总长。程序没有把它替换成成功轨迹,也没有因杆长正确而判定求解成功。

η 为 0.2,200 步后的杆件及末端迭代轨迹

η 为 2,同样预算下仍有较大目标误差

目标在身前,梯度却为零

把目标改成 (1,0),初态改成两杆水平伸直,即 θ=(0,0)。目标位于可达区域内部,但此时 p=(2,0),并且:

J=(0021),p−t=(10),g=0.J=\begin{pmatrix}0&0\\2&1\end{pmatrix},\quad p-t=\begin{pmatrix}1\\0\end{pmatrix},\quad g=0.

关节微小转动的一阶位移沿竖直方向,与当前水平误差正交。折叠可以缩短水平距离,但这一变化在当前姿态的一阶导数里没有表现出来。于是三个 η、三个预算组成的九组实验全部保持目标误差 1,梯度为零,杆长误差也为零。减小步长或增加这里的迭代次数都不能改变零更新。

水平伸直的奇异初态:红色目标可达,迭代仍停在原处

解析解可直接找到折叠姿态。改用非奇异初值也可能让梯度法开始移动,但不能据此承诺任意目标都收敛。本实验没有实现重启、线搜索或其他求解器。

结束状态至少应连同目标误差一起报告。预算耗尽只表示停止计算;更新量很小可能表示接近解,也可能像直杆案例一样停在非零残差处。有解析下界时,还可以比较当前误差与可达到的最小残差,避免对不可达目标提出误差归零的验收要求。

复跑与检查边界

从仓库根目录执行下面两条命令,二进制和生成物都位于仓库 build 目录:

1
2
clang++ -std=c++17 -O2 -Wall -Wextra -Wpedantic -Werror examples/computer-graphics/ikE05_check.cpp -o examples/computer-graphics/build/ikE05_check
examples/computer-graphics/build/ikE05_check examples/computer-graphics/build/ikE05

头文件与检查程序给出完整实现。summary.csv保留 18 组结果,iterations.csv保留包含初态的 1518 行轨迹,analytic.json保留七种目标的两分支结果。程序另生成 38 张 PPM;本文图片由对应 PPM 转换,蓝、绿分别表示两根杆,红点表示目标。

验收包括解析手算值、梯度差分、全程杆长及奇异初态。零杆长、NaN 目标、零步长、非平面目标和过小杆长这五项输入均被拒绝。--help 返回 0,未知参数返回 1。输入检查允许的步长上限只是数值范围约束,不能当作收敛保证。

这里没有关节角限位、障碍物碰撞或布料。解析可达环带以关节自由旋转为前提;加入角限位后要重新判断可达性,穿过障碍物的姿态也不会被当前代码拒绝。第 32 篇的碰撞处理不能自动由这个角度更新过程获得。完整运行记录见CPU 回执。

自检

  1. 杆长为 1、0.5,目标为 (0.1,0),能否通过更多迭代消除误差?不能。目标位于内侧不可达区域,最小误差为 0.5−0.1=0.4。
  2. 两根单位杆伸直时,到 (1,0) 的误差为 1,但梯度为零,是否证明目标不可达?不能。两组解析解为 (−π/3,2π/3) 与 (π/3,−2π/3),零梯度只描述当前姿态的局部一阶信息。
  3. η=2 时杆长误差接近机器精度,能否认定求解器正确收敛?不能。杆长由参数化保持,而 200 步目标误差约 3.26107,且出现 104 次目标函数上升;两项误差回答不同问题。

系列入口 · 上一篇:GPU任务队列 · 下一篇:逆向渲染。