Skip to content
Published on

机器人学所需的数学,按顺序学:以及哪些可以先放一放

分享
Authors

引言 —— 「想搞机器人,数学该先学什么」

这个问题常见的答案有两种。一种是「线性代数和微积分」,但范围太大,不知道该在哪里停下来。另一种是把研究生课程表原样照搬过来的清单,而把那份清单全部学完才开始做机器人的人,几乎不存在。

问题不在清单本身,而在顺序和深度。做机械臂的过程中,会在某些时刻需要用到数学,而每个时刻所需的深度天差地别。把旋转矩阵相乘,本科一年级第一个月就够用;而要理解为什么关节速度在奇异点附近会发散,得一路学到奇异值分解,两者之间差了好几个学期。可如果把这两件事塞进同一张清单里,人往往在第一行就停下来了。

所以这篇文章按顺序整理六条分支,每条分支都写清楚三件事:

  • 在机械臂的哪个环节被精确地用到
  • 学到什么程度算够用
  • 哪些可以放到后面再学

第三点最重要。大多数人卡住的地方,恰恰是「这个不学是不是也能往下走」这件事变得没把握的那一刻。把可以延后的东西明确标出来,进度才能往前推。

这篇文章是从机械臂结构出发,经过正运动学逆运动学,一路走到控制回路这个系列的最后一篇。前四篇文章已经把该算的都算完了,这篇文章要回头看看,这些计算底下到底铺着什么。

线性代数 —— 六条分支里唯一不能延后的一条

在机器人学里,线性代数不是一种工具,而是一门语言。其余五条分支都可以等需要的时候再学,唯独这一条不行。看不懂旋转矩阵,连正运动学的第一行都读不下去。

用在哪里。 旋转矩阵、齐次变换矩阵、雅可比矩阵、惯性矩阵——这些全都是矩阵。给每根连杆挂一个坐标系、在它们之间来回转换,做的全都是基变换。奇异点正是雅可比矩阵的秩下降,可操作度由奇异值定义,冗余机器人的零空间,字面意思上就是零空间。

按深度拆开来看:

第一,矩阵乘法与向量。 马上就要用到。需要能手算 3×3 和 4×4 矩阵的乘法,并且从骨子里知道乘法顺序一变,结果就跟着变。T01 · T12T12 · T01 不同,光这一个事实,就能挡掉一半的坐标系符号错误。

第二,基变换。 这个也马上要用到。把这一个概念吃透,正运动学基本上就算完成了。

如果只把旋转矩阵当成「让东西转起来的运算」,要背的东西会越来越多。不如去读它的列。 旋转矩阵的第一列,是旋转后坐标系的 x 轴在原坐标系里指向哪个方向;第二列是 y 轴;第三列是 z 轴。

R = [ x̂'  ŷ'  ẑ' ]     ← 把新坐标系的三根轴当作列并排写出来

所以 R · p_local 的意思就是
「把 p_local 的各个分量,沿着新的轴方向做加权求和」。

这样看,旋转矩阵的性质就不再是要背的东西,而是理所当然的事。各列互相正交、长度为 1,说的就是坐标轴正交、单位长度,所以逆矩阵才等于转置。它不过是把轴列成了一张表,反过来读就是逆变换。

第三,秩与零空间。 到正运动学为止都用不上,进入逆运动学之后就需要了。

如果雅可比矩阵是 6×6,但秩掉到了 5,末端能移动的方向就从六维缩减到了五维。丢掉的那一个方向,正是奇异点上去不了的那个方向。反过来,在一条七关节的手臂上,雅可比矩阵是 6×7,会剩下一个一维的零空间。在这个零空间里活动关节,末端保持不动,只有肘部在动。冗余自由度正是这种结构。

第四,奇异值分解。 一旦开始用数值方法求解逆运动学就需要了。而且这个工具是分析雅可比矩阵的主力。

SVD 把一个矩阵拆成三个阶段。

J = U · Σ · Vᵀ

V  : 关节空间里「该用哪种组合来动」的主轴
Σ  : 那种组合会被放大多少倍、变成末端速度(奇异值,对角)
U  : 作业空间里「结果指向哪个方向」的主轴

为什么雅可比矩阵偏偏要用 SVD,看清楚奇异值的含义就明白了。每一个奇异值都是那个特定方向上的速度传递比。奇异值大的方向,意味着关节稍微一转,末端就动很多;奇异值小的方向,意味着关节要转很多,末端才动一点点。

由此,奇异点是什么也就自然而然地推出来了。就是最小奇异值趋于零的那一点。想沿那个方向移动末端,就需要无穷大的关节速度。所以最小奇异值天然就是「离奇异点还有多远」的度量,而最大值和最小值之比,也就是条件数,用一个数字就能告诉你手臂当前姿态有多「平衡」。

自己确认一遍,比读解释更快。来分解一下两连杆平面手臂(上臂 0.20 米,前臂 0.15 米)的雅可比矩阵,同时扫描肘部角度看看变化。

import numpy as np

L1, L2 = 0.20, 0.15


def jacobian(t1, t2):
    """两连杆平面手臂的解析雅可比矩阵。每一列是一个关节的贡献。"""
    s1, c1 = np.sin(t1), np.cos(t1)
    s12, c12 = np.sin(t1 + t2), np.cos(t1 + t2)
    return np.array([
        [-L1 * s1 - L2 * s12, -L2 * s12],
        [ L1 * c1 + L2 * c12,  L2 * c12],
    ])


def fk(t1, t2):
    return np.array([L1 * np.cos(t1) + L2 * np.cos(t1 + t2),
                     L1 * np.sin(t1) + L2 * np.sin(t1 + t2)])


print(" 肘部角度   σ_max     σ_min     条件数")
for deg in [90, 45, 20, 10, 5, 1, 0.1]:
    J = jacobian(np.radians(30), np.radians(deg))
    sv = np.linalg.svd(J, compute_uv=False)
    print(f"  {deg:6.1f}°  {sv[0]:.6f}  {sv[1]:.6f}  {sv[0] / sv[1]:12.1f}")

运行结果如下。

 肘部角度   σ_max     σ_min     条件数
    90.0°  0.269451  0.111337           2.4
    45.0°  0.351840  0.060292           5.8
    20.0°  0.375011  0.027361          13.7
    10.0°  0.379341  0.013733          27.6
     5.0°  0.380427  0.006873          55.4
     1.0°  0.380774  0.001375         276.9
     0.1°  0.380789  0.000138        2769.3

肘部越伸直,最小奇异值就越往零走,条件数随之爆炸。角度从 10 度减到 1 度、再减到 0.1 度,条件数几乎精确地每次放大 10 倍:27.6、276.9、2769.3。这是因为最小奇异值几乎和角度成正比地缩小。逆运动学那篇文章里说过手臂完全伸直的姿态就是奇异点,这句话此刻正以数字的形式,原原本本地摆在这张表里。

而阻尼最小二乘法为什么行得通,到这里也能解释清楚了。伪逆矩阵是把奇异值直接反转成 1/σ,所以 σ 趋近于零时就会发散。加上阻尼之后,变成 σ / (σ² + λ²) 的形式:σ 较大时和 1/σ 几乎一样,σ 趋近于零时则平滑地趋向零。在奇异点上,不再是趋向无穷大,而是变成「那个方向就放弃了」。不用 SVD 想解释清楚这一点,会困难得多。

学到什么程度算够用。 能手算 3×3 矩阵的乘法和转置,知道旋转矩阵的列就是轴,能用语言解释秩和零空间,知道 SVD 的三个部分各自是什么,就够了。计算本身交给 numpy.linalg.svd 就行。如果只需要奇异值,把 compute_uv 设为假,就不用计算 U 和 Vᵀ,速度更快——上面的代码正是这么写的。

哪些可以放到后面。 抽象向量空间的公理、若尔当标准形、行列式的代数余子式展开、格拉姆-施密特正交化的手算、张量记法。这些在机械臂上基本用不上,就算哪天真的需要了,到那时候现学也不迟。

三角学与旋转表示 —— 一个函数就占了一半

这条分支内容不多,但出错率很高。

用在哪里。 两连杆和三连杆手臂的逆运动学解析解,是靠余弦定理解出来的。旋转表示解决的是末端姿态该存成什么、该怎么插值的问题。

先从一个函数讲起:atan2

import numpy as np

# atan2(y, x) 会分别看两个符号,所以能区分象限。
print(np.degrees(np.arctan2( 1.0,  1.0)))   #   45.0
print(np.degrees(np.arctan2( 1.0, -1.0)))   #  135.0
print(np.degrees(np.arctan2(-1.0, -1.0)))   # -135.0

# 而如果只传入一个比值,就没法区分第1象限和第3象限。
print(np.degrees(np.arctan(1.0 /  1.0)))    #   45.0
print(np.degrees(np.arctan(-1.0 / -1.0)))   #   45.0  ← 实际上是-135度

机械臂代码里肩部角度突然反转 180 度的 bug,大多数都是这么来的。求角度时,不要只传一个相除之后的比值,应该把 y 和 x 分开传进去。光这一个习惯,就能省掉一大半排查符号错误的时间。

asinacos 也有同样的陷阱。它们的定义域是被截断的,一旦舍入误差让输入略微超过 1,就会得到 NaN。用余弦定理求肘部角度的代码里,当目标点恰好落在两连杆长度之和的距离上时,这种情况真的会发生。需要有一行代码去裁剪输入。

cos_t2 = np.clip(cos_t2, -1.0, 1.0)   # 没有这一行,边界处会出现NaN

旋转表示有四种,各自用在不同的地方。

表示法存储的数值个数优点缺点主要用在哪里
旋转矩阵9能直接乘到坐标上存储量大,会有数值漂移内部计算
欧拉角3人能读能写万向锁,还有十几种不同的顺序约定用户输入、日志
轴角4物理意义直观合成起来比较麻烦表达旋转指令
四元数4没有奇异点,插值成本低人读不懂姿态的存储与插值

实务中这张表的意思很简单:在人接触的边界用欧拉角,内部计算用旋转矩阵或四元数。 而且把两者之间的转换集中放在一个地方,能减少符号错误。

欧拉角的顺序约定尤其需要小心。同样三个数字,按不同约定会代表不同的旋转。混用两个库之后如果姿态看起来不对,先检查约定是否一致。

学到什么程度算够用。 能反射性地用 atan2,能用余弦定理解三角形;对四元数,知道「相乘就是合成旋转,必须保持归一化,插值用 slerp」就够了。不需要去推导四元数代数。

哪些可以放到后面。 四元数的代数构造、李群与李代数、指数映射与对数映射、旋量理论。这些以后会非常有用,尤其是能让基于优化的控制代码变得干净,但动第一条手臂用不上它们。等你把一条串联手臂从头到尾做完一遍,再回头看这些内容,会好懂得多。

微积分与多变量方法 —— 雅可比矩阵就是一张偏导数表

用在哪里。 几乎全是雅可比矩阵。而雅可比矩阵在机械臂上会出现三次:速度关系里、力关系里,以及扩展卡尔曼滤波器的线性化步骤里。

「雅可比矩阵」这个名字听起来吓人,实质却很简单。它是把每个输出对每个输入求偏导之后的值,排成一张网格。

如果末端位置是 (x, y),关节是 (θ1, θ2),

J = [ ∂x/∂θ1   ∂x/∂θ2 ]
    [ ∂y/∂θ1   ∂y/∂θ2 ]

第 j 列 = 「只让 θj 以 1 rad/s 转动时,末端移动的方向和大小」

养成这样按列去读的习惯,雅可比矩阵就变得很直观了。在手臂完全伸直的姿态下,两列会几乎指向同一个方向,于是这两列能张成的方向范围就变窄了——这正是前一节里最小奇异值趋于零的那个现象。同一个事实,既能从偏导数看到,也能从 SVD 看到。

这里有个值得养成的实务习惯。推导出解析雅可比矩阵之后,一定要用数值微分交叉验证。 一个符号错了的雅可比矩阵会让逆运动学收敛到完全错误的方向,而症状往往只表现为「收敛得比较慢」,原因很难找。

def numeric_jacobian(f, q, eps=1e-6):
    """用中心差分近似雅可比矩阵。用来核对解析解。"""
    q = np.asarray(q, dtype=float)
    base = f(*q)
    J = np.zeros((len(base), len(q)))
    for j in range(len(q)):
        dq = np.zeros_like(q)
        dq[j] = eps
        J[:, j] = (f(*(q + dq)) - f(*(q - dq))) / (2 * eps)
    return J


q = np.radians([30.0, 40.0])
Ja = jacobian(*q)
Jn = numeric_jacobian(fk, q)

print("解析雅可比矩阵:\n", np.round(Ja, 8))
print("数值雅可比矩阵:\n", np.round(Jn, 8))
print("最大误差      :", np.max(np.abs(Ja - Jn)))

运行结果如下。

解析雅可比矩阵:
 [[-0.24095389 -0.14095389]
 [ 0.2245081   0.05130302]]
数值雅可比矩阵:
 [[-0.24095389 -0.14095389]
 [ 0.2245081   0.05130302]]
最大误差      : 2.6600312230673495e-11

误差在 1e-9 量级说明推导是对的,在 1e-2 量级说明哪里出错了。如果出现中间量级的误差,通常不是符号错了,而是把单位(角度和弧度)搞混了。

链式法则也是在这里露出真面目的。在六关节的手臂上,末端位置是六个角度的复合函数,求雅可比矩阵就是对这个复合函数求导。求导的过程,和把各个坐标系相乘串起来的结构是一样的。

学到什么程度算够用。 能计算偏导数,会用链式法则,知道梯度指向上升最快的方向,能用数值微分做核对,就够了。手工推导解析雅可比矩阵,做到两连杆和三连杆就足以找到手感。六连杆的交给库去处理。

哪些可以放到后面。 微分几何、流形上的微积分、变分法、张量微积分。而且积分用得出乎意料地少。机械臂上出现积分的地方,是 PID 的 I 项和轨迹的累积量,两者都是用离散求和来实现的,所以用不上解析积分技巧。

微分方程与控制理论 —— 在手动调增益之前

用在哪里。 手臂为什么会猛地一抖,PID 的三个项为什么各自扮演那样的角色,控制周期为什么会侵蚀稳定性——这些全都属于这条分支。

机械臂的运动方程长这样。

M(q)·q̈  +  C(q, q̇)·q̇  +  g(q)  =  τ

M(q)      惯性矩阵 —— 随姿态变化
C(q, q̇)  科里奥利力与离心力 —— 正比于速度的平方
g(q)      重力项 —— 只取决于姿态
τ         关节扭矩 —— 我们要产生的东西

不需要去解这个方程,但必须能读懂它。能看出三件事。第一,惯性不是常数,而是姿态的函数,所以手臂折叠时调好的增益,伸展开之后就不对了。第二,存在一个正比于速度平方的项,所以动得越快,非线性就越强。第三,重力项与速度无关、始终存在,所以提前用前馈把重力补偿掉,能大幅减轻反馈要做的工作。

控制理论里真正需要上手的,是一个二阶系统。

标准形:   ẍ  +  2ζωn·ẋ  +  ωn²·x  =  ωn²·u

ωn  固有频率 —— 有多快
ζ   阻尼比   —— 怎样到达

ζ < 1   欠阻尼,冲过头再折回来(超调)
ζ = 1   临界阻尼,不超调的情况下最快
ζ > 1   过阻尼,慢慢地渗进去

给单个关节接上 PD 控制器,正好就是这个形式。P 增益对应 ωn²,D 增益对应 2ζωn。看清这一点之后,调增益的意义就变了。不再是凭感觉去拨弄两个数字,而是先决定「要多快(ωn)、以什么方式(ζ)到达」,再把它换算成增益。

知道这层对应关系,常见症状立刻就能诊断出来。超调大,说明 ζ 偏小,该调高 D。慢而且不超调,说明 ωn 偏小,该把 P 和 D 一起调高。只调高 P,速度是上去了,但 ζ 会下降,于是又产生超调。这个关系正是控制回路那篇文章里调增益那部分工作背后的原理。

延迟问题也是从这里冒出来的。控制周期长,或者传感器滤波器带来延迟,反馈就会对着一个过时的误差做出反应,这意味着相位向后偏移了。相位裕度一旦变小,哪怕只是把增益稍微调高一点,系统也会开始振荡。「把采样率提高一倍之后,增益就能调得更高了」这种经验,正是这套理论的直接推论。

学到什么程度算够用。 能看着二阶系统标准形读出 ζωn,知道它们怎么对应到 PD 增益,知道延迟会侵蚀相位裕度,就够了。拉普拉斯变换只需要知道「求导变成乘法」以及极点位置决定响应形状这两个概念就行。

哪些可以放到后面。 状态空间最优控制(LQR)、鲁棒控制、李雅普诺夫稳定性证明、奈奎斯特判据的严格应用、自适应控制。这些在把机械臂做成产品的阶段会用得上,但在刚开始让手臂动起来的阶段,PD 加重力补偿就已经能走得相当远了。

概率与估计 —— 当传感器在撒谎的时候

用在哪里。 编码器是量化的,IMU 会漂移,视觉会随光照晃动。当多个传感器的读数彼此不一致时,该相信谁,这就是这条分支要解决的问题。

这条分支比前面四条更晚才需要。 只做位置控制、只用一个编码器、并且直接相信它读数的手臂,大体上也能凑合运转。概率通常是在传感器数量超过一个之后才开始变得必要——给手臂装上相机,加一个 IMU,加一个力传感器。

核心概念有三个。

第一,如何用数字写出不确定性。 不再只用一个测量值,而是用「均值和方差」这一对数字来处理。到了多维情形,就变成协方差矩阵,而这个矩阵就是一个椭圆,描述不确定性在哪个方向上散布了多少。线性代数在这里又出现了——协方差矩阵的特征向量是椭圆的轴,特征值是轴的长度。

第二,不确定性的传播。 用不准确的关节角度去算正运动学,末端位置也会跟着不准确。到底有多不准确,用雅可比矩阵来算。

当关节角度的协方差是Σq时,末端位置的协方差是

Σx  ≈  J · Σq · Jᵀ

雅可比矩阵又出现了。这正是这篇文章说雅可比矩阵会出现三次的原因。而且这个公式说出了一件实务上很重要的事:在奇异点附近,雅可比矩阵会在特定方向上大幅放大,于是同样的编码器误差,到了末端就会变成大得多的误差。奇异点不只是速度的问题,也是精度的问题。

第三,卡尔曼滤波器。 名字听起来吓人,但在一维情形下一句话就能讲完。当预测值和测量值各自带着不确定性时,把两者按精度(方差的倒数)做加权平均

预测:  x̂ = 5.0,  方差 = 4.0   (精度 0.25)
测量:  z  = 6.0,  方差 = 1.0   (精度 1.00)

权重 = 1.00 / (0.25 + 1.00) = 0.8       ← 这就是卡尔曼增益
更新值 = 5.0 + 0.8 × (6.0 - 5.0) = 5.8
更新方差 = 1 / (0.25 + 1.00) = 0.8       ← 比两者中的任何一个都小

因为测量更准,结果就更偏向测量值;因为合并了两份信息,不确定性也随之降低。这就是全部内容。多维卡尔曼滤波器不过是把这套计算搬到矩阵上,扩展卡尔曼滤波器则是每一步都用雅可比矩阵把非线性模型线性化,再做同样的计算。

学到什么程度算够用。 理解高斯分布的均值和方差,对协方差矩阵是个椭圆有直觉,能手算上面那个一维卡尔曼更新,就够了。实际实现交给库,但必须知道调节参数——过程噪声和测量噪声——到底代表什么。

哪些可以放到后面。 粒子滤波、因子图与图 SLAM、贝叶斯推断的理论基础、信息滤波器、无迹卡尔曼滤波器。而且如果只做机械臂,整个 SLAM 都可以放到后面。SLAM 是移动机器人的问题,不会出现在基座固定的手臂上。

优化 —— 逆运动学和轨迹规划,说到底都是这个

用在哪里。 这条分支的要点在于,前面分开学的东西,到这里会汇聚到同一个框架下。

数值逆运动学是一个最小二乘问题:寻找能让目标位置和当前位置之差最小的关节角度。阻尼最小二乘法在这基础上又加了一项关于关节速度大小的惩罚,也就是正则化的最小二乘法,和机器学习里的岭回归是同一种结构。

普通最小二乘:   minimize  ‖J·Δq - Δx‖²
阻尼最小二乘:   minimize  ‖J·Δq - Δx‖²  +  λ²·‖Δq‖²
                                          └── 这一项阻止了发散

轨迹规划也是优化问题。最小加加速度轨迹是把加加速度平方的积分最小化之后的结果,时间最优轨迹是在扭矩和速度限制下把时间最小化之后的结果。控制回路那篇文章里推导五次多项式,也是同一类问题——在满足六个边界条件的函数里,挑出最平滑的那一个。

如今手臂控制的标准做法,是把速度层面的逆运动学当成二次规划(QP)来求解。目标是让末端以指定速度运动,而关节角度限制、关节速度限制、避碰这些约束,都以不等式的形式加进去。这是一种把原本逐个处理的问题,捆绑成一个问题一起解决的方式。

这里有三个值得了解的概念。

凸与非凸的区别。 凸问题里,局部解就是全局解,所以不管从哪里出发都会到达同一个地方。最小二乘和 QP 都属于这一类。而位置层面的逆运动学是非凸的,所以不同的初值会走到不同的解上——肘部朝上弯的解和朝下弯的解同时都是正确答案,正是这种情形。在非凸问题里,初值本身就是算法的一部分。 用上一次的姿态作为初值,原因就在这里。

正则化的含义。 把惩罚项的系数调高,解会变得更小、更稳定,但跟踪目标的效果会变差。调节阻尼最小二乘法的阻尼系数,正是在做这个取舍;只在奇异点附近才加大阻尼这种技巧,是根据情况动态调整这个取舍的位置。

有约束问题与无约束问题。 把关节限制当作惩罚项加进去,和当作不等式约束加进去,是两回事。惩罚项允许解稍微超出限制,约束则不允许。在真实硬件上,超出关节限制意味着物理上真的会撞上去,所以约束这种做法才是对的。

学到什么程度算够用。 能把最小二乘问题写成正规方程,知道正则化项在做什么,知道凸性和初值依赖性之间的关系,知道梯度下降是怎么运作的,就够了。QP 求解器交给库来处理。

哪些可以放到后面。 内点法的实现、对偶理论与 KKT 条件的推导、序列二次规划(SQP)的细节、轨迹优化里直接法和间接法的比较。这些是自己写求解器或者写论文时才需要的。要让手臂动起来,会调用求解器就够了。

按顺序整理 —— 什么时候学什么,什么放到以后

把六条分支收进一张表里,大致是这样。越靠左越早需要,右边这一列是这篇文章最重要的部分。

分支什么时候需要在机械臂的哪里学到这个程度就够可以放到以后的内容
矩阵乘法、基变换第一天旋转矩阵、齐次变换手算3×3、理解列即是轴抽象向量空间、若尔当标准形
三角学、atan2第一周两连杆IK解析解余弦定理、区分象限李群、指数映射、旋量理论
旋转表示进入三维时姿态的存储与插值分清四种表示各用在哪、slerp四元数代数的构造
偏导数、雅可比矩阵处理速度时速度/力的关系、数值IK偏导数与链式法则、数值核对微分几何、变分法
秩、零空间、SVD遇到奇异点时奇异点、可操作度、冗余自由度理解奇异值就是方向传递比亲自实现数值SVD算法
二阶系统、阻尼比调增益时PID调参、延迟与稳定性ζωn对应增益的关系LQR、鲁棒控制、李雅普诺夫证明
最小二乘、正则化用数值IK时DLS、轨迹优化正规方程、惩罚项的作用内点法、KKT推导、SQP
高斯分布、协方差传感器不止一个时误差传播、滤波手算一维卡尔曼更新粒子滤波、因子图

如果只选一本教材,推荐凯文·林奇(Kevin Lynch)和弗兰克·帕克(Frank Park)的《Modern Robotics: Mechanics, Planning, and Control》。预印本 PDF、视频讲座,以及 Python、MATLAB、Mathematica 的实现全部公开,不管在上面这张表的哪一行卡住,都能直接翻到对应的章节。不过这本书是以旋量理论为主线来组织内容的,所以会先撞上表里「可以放到以后」的那些东西。第一遍读的时候,跳过那部分、只挑需要的章节看也完全没问题。

最常见的错误,是想把这张表从上到下学完才开始做机器人。反过来做要快得多。只带着前两行去让一条两连杆手臂动起来,亲眼看着它在奇异点附近发疯,再回头读第五行,当天就能理解。需求出现之后再学,和一边想象需求一边学,吸收的效率完全不是一回事。

也请再看一眼「可以放到以后」这一列。右边这一列里的每一项,无一例外都占据了本科课程相当大的篇幅:李群、LQR、KKT 条件、粒子滤波。这不代表它们不重要,而是意味着没有它们,也能做出一条能用的手臂。 而一个已经亲手做出过一条能用手臂的人,再去读那份清单,速度会快得多,因为到那时候,每一项解决的是什么问题,已经一目了然了。

结语 —— 同样的工具会反复出现

写这篇文章的过程中,我又确认了一件事。虽然分成了六条分支,但实际上反复出现的,不过是那几件工具。

雅可比矩阵最先出现在速度关系里,又在力关系里出现一次,在误差传播里再出现一次,在扩展卡尔曼滤波器的线性化里又出现一次。SVD 解释了奇异点,定义了可操作度,解释了阻尼最小二乘法为什么行得通,还告诉你协方差椭圆的轴在哪。最小二乘法既是逆运动学,也是轨迹规划,也是滤波。

学习策略也就随之确定了。与其广泛地泛泛涉猎,不如把常出现的工具吃透。 把雅可比矩阵放在四个不同的场景里理解透彻,比把这四个地方分开学,花的时间更少,记得也更牢。

而这篇文章清单里价值最高的部分,不在前面,而在后面——那些可以延后的内容。决定不学什么,比决定学什么更能推动进度。因为数学不够而没能开始做机器人的人,远远比不上那些想把一切都学完再开始、结果反而没能开始的人多。

浓缩成一句话就是——只带着矩阵乘法和 atan2 也能让手臂动起来。剩下的,等手臂开始表现得奇怪的时候再学就行。