小幅路径,大幅指令
软件为机器人夹爪生成一系列点,相邻点仅相距几毫米。计算确认每个点都可达,但序列中间却出现很大的肘关节转动请求。这不一定是目标跳动,也不一定是电机故障,可能只是软件改变了所选解。本文解释为什么末端位置并不总是对应唯一臂姿,以及如何沿路径保持一致的选择。
摘要。分析一个理想平面机械臂,具有两个旋转关节,连杆长 0.5 和 0.4 米。先推导目标点的两个解,再在半径 0.6 米的圆弧上比较两种选择规则:逐点最小化肩关节角的绝对值,或选择最接近前一构型的解。实际执行的计算中,目标约移动 6 毫米,第一种规则却使肩关节指令变化约 82.25°,肘关节变化约 194.36°;第二种规则保持同一分支,只使肩角变化约 0.573°。这些是请求构型之间的差异,不是实测速度,也不是实际机器人瞬间完成的运动。
位置与构型,是不同的信息
设定 x、y 平面,肩关节位于原点。L₁、L₂ 是两个连杆的长度,单位米。q₁ 是第一连杆相对 x 轴的角度,q₂ 是第二连杆相对第一连杆的角度。正角为逆时针,计算使用弧度,180° 等于 π 弧度。构型 q=(q₁,q₂) 描述两个关节,点 p=(x,y) 只描述末端位置。没有约束夹爪朝向;加入朝向会改变问题,可能排除一些解。
正运动学回答“知道角度后,末端在哪里”。将两个连杆在 x、y 上的投影相加。第二个使用 q₁+q₂,因为其在平面中的方向包含两次转动。该式还用于检查逆解:代入任何候选构型,都应还原目标点。不能只接受算法声称找到了解,还可以做几何验证。
推导两个分支,而不把它们藏在软件里
逆运动学反过来问:哪些角度能到达给定点?先利用末端到肩的距离 r 求 q₂。通过 q₁ 旋转整个机械臂只改变方向,不改变 r。将 x、y 平方相加,交叉项变为 2L₁L₂ cos(q₂),而 cos²+sin²=1 消去其余角度,只剩一个未知数。C 没有量纲,因为分子和分母都是平方米。
C 严格位于 −1 与 1 之间时,余弦对应两个互为相反数的角度,即两个分支。超出该区间则没有实数解。边界处两分支在几何上重合,需注意 ±π 的等价表示。所选长度的可达半径为 |0.5−0.4|=0.1 米至 0.5+0.4=0.9 米。半径 0.6 米的圆弧位于该环形区域内部。多解不是噪声或近似造成的,而是理想几何本身的性质。
还需要为每个分支确定 q₁。用 φ 表示目标方向,由 atan2(y,x) 计算。与仅计算 y/x 的反正切不同,atan2 利用两个坐标的符号区分象限。q₁=0 时,指向末端的向量分量为 L₁+L₂ cos(q₂) 与 L₂ sin(q₂),其角度 β 由另一项 atan2 给出。将第一关节转动 φ−β,就与目标方向对齐。因此,每个 q₂ 都对应一个不同的 q₁。
x=0.6 米、y=0 时,C=−0.125。程序得到 q₂=±97.18076°,对应肩角为 ∓41.40962°。将两种构型代回正运动学,都在已检查的数值容差内返回 (0.6,0)。这是同一模型的不同构型,却具有相同末端点。图中末端重合,肘部位于 x 轴两侧。尚未定义选择准则,就不能说哪种姿态更好。
| 分支 | q1(度) | q2(度) | x(米) | y(米) |
|---|---|---|---|---|
| + | -41.40962 | 97.18076 | 0.6 | 0 |
| − | 41.40962 | -97.18076 | 0.6 | 0 |
对单个构型合理的规则,可能在序列上失败
构造短圆弧上的 31 个点,φ 从 −0.15 到 +0.15 弧度,步长 0.01。各点为 (0.6 cosφ,0.6 sinφ),相邻距离为 2×0.6 sin(0.01/2),约 0.005999975 米。没有分配时间戳,因此是离散几何路径,而非包含速度的时序轨迹。设定合成限位:肩角在 −170° 至 +170°,肘角在 −150° 至 +150°。全部 31 个点的两个分支都满足限位,代码对此及重建位置进行检查。
第一种规则逐点选择 |q₁| 最小的解,可理解为希望肩关节接近零位,但它不使用前一构型。φ 为负时偏好 q₂ 为负的分支,φ 为正时偏好 q₂ 为正的分支。φ=0 时成本相同,代码取首个候选。在这个平局附近,最小值改变分支:选中成本仍然较小,但选中构型大幅变化。跳变的是所选解,不是目标点。
按零起始编号,从第 14 点到第 15 点,请求肩角变化 82.24629°,肘角变化 194.36151°,但目标末端约只移动 6 毫米。软件并未指定数学意义上的瞬时物理运动,而是产生相距很远的连续参考值。真实控制器可能减速、拒绝或插值,后果需要另行评估。这里可核验的结论更有限:笛卡尔路径连续,并不能保证独立选择得到的构型连续。
仅加密采样点就能解决吗?需要验证而非假定。考虑方向 −ε 和 +ε 的两个目标。ε=0.005 弧度时相距约 6 毫米,降至 0.0005 时只相距约 0.6 毫米。但独立规则仍选相反分支,肘角变化仍为 194.36°,肩角差趋近 82.82°,而非零。代码包中的敏感性计算确认:减小几何步长并不能消除选择缺陷。
公式无需仅靠图表也能解释原因。设 γ=arccos(C)、β₀=β(γ),本例都为正。零点前选中分支为 q₂=−γ、q₁=−ε+β₀,零点后为 q₂=+γ、q₁=+ε−β₀。绝对变化在肘部为 2γ,肩部为 2β₀−2ε。ε 趋零时,两者仍不为零。需要纠正的是选择规则的这种性质,而非采样间距。在零点如何打破平局,只改变跳变出现在哪一侧。
使用前一构型的信息
第二种规则只记住前一个 q。初始时明确选择 q₂ 为负的分支,每到新点比较候选解与前一构型的距离,取最近者。在这个短区间内,原分支只需肩角改变 0.01 弧度,换分支则需改变两个角度,所以规则保持原分支。无需程序也能分析:半径不变,C 不变,q₂ 与 β 也不变,因此 q₁ 恰好随 φ 同幅变化。

右图主要看绿色肘角曲线:独立选择的参考值从负变正,带记忆的则保持负值。肩角曲线是相关后果,不是第二个实验。连线仅便于阅读,不表示已验证的物理插值。横轴是目标方向而非时间,没有时间规律就不能从斜率得出速度或加速度。
角度距离需要约定
该范数是两个弧度角组成向量的欧氏距离,权重相同。它不是某个轴的转角,也不是能量测量。本模型关节有界,直接比较区间内数值。对于真正连续旋转的轴,179° 与 −179° 可能沿允许路径仅相差 2°,直接相减却得到 358°。因此距离度量必须符合关节类型和实际限位,不能只依赖角度的数字表示。
这里肘部跳变并非单纯越过 ±180° 的表示边界。解约为 ±97°,限位为 ±150°,经 180° 的另一条转动路径不允许。差别是真实构型不同,不只是角度显示问题。从负 q₂ 连续转到正 q₂,必须经过零,此时机械臂完全伸直,r=0.9 米,所以转换过程中不可能一直保持在半径 0.6 米的圆弧上。在本模型中,两分支沿该圆弧分离。
什么时候需要完整路径和其他约束
最近前一解的规则是局部的,只看新点,不看未来。在其他问题中,它可能保留一个之后碰到限位或障碍的分支,而另一个初始选择能完成整个任务。一种替代方案把每个点的候选解作为节点,将连续候选之间可行的转换作为边,再搜索完整路径。但端点可达不代表边可行,中间运动也必须用适当模型检查。
本例每点两个候选,局部比较每步为常数工作量,N 个点为 O(N)。每点 B 个候选的图,在计入几何验证成本之前,可能需要 O(NB²) 次相邻层比较。我们没有实现或测量该规划器,因此只是结构分析,不是基准测试。加入朝向、障碍、速度、加速度及力矩后,问题会扩大。解决此圆弧不证明工业机器上的安全、无碰撞或动力学可行性。
复现,并区分计算与物理测试
代码包包含 experiment.py、plot.py 和结果 JSON 中全部 31 个样本。ik 用 arccos 和 atan2 求候选,fk 重建位置。断言检查指定点的笛卡尔误差低于 10⁻¹² 米,且满足限位。程序在同一序列比较两条规则,找出最大构型步长。10⁻¹² 是模型数值检查容差,不是机器人精度。没有随机性,所以不需要种子。下方片段需在解压后的代码包目录运行。
通用软件还需检查超出范围的目标、精确限位、等价表示、分支合并及 C=±1 附近的容差。若总把 C 裁剪到 [−1,1],会隐藏真正不可达的目标;容差处理应区分数值误差与几何不可能。附带代码刻意限定于声明的数据。物理测试还需真实参数、标定、时序控制与路径验证。这里没有移动机械臂,也没有测量硬件性能。
这个例子回答了什么
末端目标相邻,而请求构型仍可能跳变,因为逆问题有多个解,逐点选择会换分支。对于所分析短圆弧,记住前一构型可避免换支,使关节变化保持一致。当 AI 应用为机器人提出目标时,这引出一个有用问题:提议之后保留了哪些连续性和约束?工业与协作机器人是 EL-AI 希望探索的方向。本教学研究说明一项设计要求,不把已存在的机器人、安装或实验归于该企业。
来源与贡献
一手来源:Kris Hauser 的 Robotic Systems,第 6 章“Inverse Kinematics”,查阅逆问题、解的选择及 2R 机械臂解析推导部分,阅读了推导步骤与算法,而不仅是开篇介绍。教学页面查阅于 2026 年 10 月 10 日,不赋予未经核实的发布日期。连杆长度、圆弧、选择规则和数值比较为本文构建。这是 AI 辅助的教学推导与分析,不是同行评审原创研究或新的机器人基准测试。
Kris Hauser — Robotic Systems, Chapter 6: Inverse Kinematics.
import json
from experiment import ik, fk
import math
for q in ik(.6, 0):
print('angles deg', [math.degrees(v) for v in q])
print('position m', fk(q))
r = json.loads(open('results.json', encoding='utf8').read())
print('largest transition', r['largest_naive_transition'])
代码、数据与说明 · JSON. 教学计算使用 Python 3.14.0,图使用 Matplotlib 3.11.2。分析由 AI 辅助,不声称经过同行评审或人工审核。原创 ImageGen 封面仅作示意,不记录 EL-AI 人员、场所或实际安装。来源查阅于 2026 年 10 月 10 日。

