逆向運動學完整解法:解析解、Pieper 準則與數值迭代法

2026-07-22
  • inverse-kinematics
  • jacobian
  • dh-parameters
  • singularity
  • numerical-methods

問題定義

逆向運動學(Inverse Kinematics, IK)要回答的問題是:已知末端執行器的目標位置與姿態 TtargetSE(3)T_{\text{target}} \in SE(3),要找出一組關節角度 θ=(θ1,θ2,,θn)\boldsymbol{\theta} = (\theta_1, \theta_2, \dots, \theta_n),使得正向運動學算出來的末端姿態恰好等於目標:

T0n(θ)=TtargetT_0^n(\boldsymbol{\theta}) = T_{\text{target}}

這個問題比正向運動學本質上難得多,原因有三個,而且都不是工程實作細節,是數學結構層面的困難:

  1. 非線性T0n(θ)T_0^n(\boldsymbol{\theta}) 是關節角度的三角函數組合(sinθi\sin\theta_icosθi\cos\theta_i 疊乘),不存在一般線性方程組的解法。
  2. 解的數量不固定:同一個末端位置,手臂可能有零組解(目標超出工作空間)、恰好一組解、或者多組解(例如肘部朝上與朝下都能到達同一點)。6 自由度手臂在非奇異姿態下,理論上最多可以有到 16 組不同的關節組合解。
  3. 可能存在冗餘自由度:如果手臂的自由度數大於任務所需的自由度(例如 7 軸手臂做 6 維度的末端姿態任務),解不是離散的幾組,而是一整個連續的解空間(self-motion manifold),這時候額外需要一個準則(例如避開障礙物、避開關節極限、最小化能量)去挑選解空間裡的其中一點。

解析解:從 2 連桿平面手臂開始

在推導一般化方法之前,先看一個可以完全手算的具體例子:一個平面上的 2 連桿手臂,連桿長度分別是 l1l_1l2l_2,兩個都是旋轉關節,目標是讓末端到達平面上的座標 (x,y)(x, y)

用餘弦定理解肘部角度

末端到基座的直線距離是 r=x2+y2r = \sqrt{x^2 + y^2}。這條直線、l1l_1l2l_2 三條邊構成一個三角形,肘部角度 θ2\theta_2(第二關節相對第一連桿的夾角)可以直接用餘弦定理反解:

cosθ2=x2+y2l12l222l1l2\cos\theta_2 = \frac{x^2 + y^2 - l_1^2 - l_2^2}{2 l_1 l_2} θ2=±arccos(x2+y2l12l222l1l2)\theta_2 = \pm \arccos\left(\frac{x^2 + y^2 - l_1^2 - l_2^2}{2 l_1 l_2}\right)

正負號對應的正是「肘部朝上」與「肘部朝下」兩組不同的解——這是前面提到的「解不唯一」在最簡單案例裡的具體展現。arccos\arccos 的輸入值必須落在 [1,1][-1, 1] 之間,一旦超出這個範圍,就代表目標點 (x,y)(x, y) 落在手臂的工作空間之外(太遠或太近),這是解析解可以直接、便宜地判斷「目標無解」的優勢,數值迭代法通常要跑到不收斂才能間接推論出同樣的結論。

反解肩部角度

有了 θ2\theta_2,肩部角度 θ1\theta_1 可以用兩步反解:先算出從基座看向末端的角度,再減去第一連桿到目標點方向之間的夾角:

θ1=atan2(y,x)atan2(l2sinθ2, l1+l2cosθ2)\theta_1 = \operatorname{atan2}(y, x) - \operatorname{atan2}\big(l_2 \sin\theta_2,\ l_1 + l_2 \cos\theta_2\big)

這裡刻意用 atan2 而不是 atan,是因為 atan2 能根據兩個輸入的正負號正確判斷象限,atan 只能算出 (π/2,π/2)(-\pi/2, \pi/2) 範圍內的角度,直接拿來做機械手臂控制會在特定象限算錯方向——這是解析 IK 實作裡最常見、也最隱蔽的 bug 來源之一。

6 自由度手臂:Pieper 解耦解法

真實工業手臂通常是 6 自由度串聯結構,直接對 6 個未知數解一組聯立三角方程,計算複雜度會爆炸。1968 年 Donald Pieper 證明了一個關鍵定理:只要手臂最後三個旋轉關節的軸線交於一點(這一點稱為手腕中心,wrist center),問題就可以解耦成兩個獨立的子問題,各自都有封閉形式解——這正是絕大多數工業六軸手臂刻意採用「球形手腕」(spherical wrist)設計的原因。

解耦之後的兩步驟:

第一步:位置反解(前三軸)。因為最後三軸交於手腕中心點 oc\boldsymbol{o}_c,而且旋轉關節不影響該點沿軸線之外的位置,手腕中心的位置只由前三個關節決定:

oc=otargetd6ztarget\boldsymbol{o}_c = \boldsymbol{o}_{\text{target}} - d_6 \cdot \boldsymbol{z}_{\text{target}}

其中 d6d_6 是最後一節連桿長度、ztarget\boldsymbol{z}_{\text{target}} 是目標姿態沿末端 z 軸的方向向量。算出 oc\boldsymbol{o}_c 之後,前三軸 θ1,θ2,θ3\theta_1, \theta_2, \theta_3 的問題就退化成跟前面 2 連桿平面手臂類似的幾何問題(只是多一個自由度、在三維空間中),一樣可以用餘弦定理與 atan2 解出封閉形式解。

第二步:姿態反解(後三軸)。有了前三軸的解,就能算出前三軸構成的座標系相對基座的旋轉矩陣 R03R_0^3。剩下要做的是求解 R36R_3^6,使得:

R36=(R03)1RtargetR_3^6 = (R_0^3)^{-1} R_{\text{target}}

因為後三軸是球形手腕(三軸交於一點、且互相垂直),R36R_3^6 對應的正是一組尤拉角分解(常見的是 ZYZ 或 ZYX 順序),可以直接反解出 θ4,θ5,θ6\theta_4, \theta_5, \theta_6,公式形式跟旋轉矩陣轉尤拉角的標準解法一致。

這個「先解位置、再解姿態」的解耦技巧,把一個 6 維非線性聯立方程組問題,拆成兩個各自 3 維、各自有封閉形式解的子問題,這是解析解法在工程上能夠實用化的關鍵。

數值迭代解法:當解析解不存在時

如果手臂不滿足 Pieper 準則(例如某些非球形手腕的協作手臂、或者自由度數與任務維度不匹配的冗餘手臂),就必須依賴數值迭代法。核心想法是:從一個初始猜測關節角度 θ0\boldsymbol{\theta}_0 出發,每次迭代都用雅可比矩陣算出「如果末端要往目標方向移動一點點,關節角度大致要怎麼調整」,逐步逼近目標。

Newton-Raphson 法

定義末端目前姿態與目標姿態之間的誤差 Δx=xtargetx(θk)\Delta x = x_{\text{target}} - x(\boldsymbol{\theta}_k)(位置誤差 3 維 + 姿態誤差 3 維,共 6 維)。雅可比矩陣 J(θ)J(\boldsymbol{\theta}) 描述了關節角速度與末端速度的線性關係 x˙=Jθ˙\dot{x} = J \dot{\theta},把這個關係離散化、反過來用,就得到更新規則:

θk+1=θk+J+(θk)Δx\boldsymbol{\theta}_{k+1} = \boldsymbol{\theta}_k + J^{+}(\boldsymbol{\theta}_k) \, \Delta x

其中 J+J^{+} 是 Jacobian 的虛擬反矩陣(Moore-Penrose pseudoinverse)。當 JJ 是方陣且非奇異時,J+=J1J^{+} = J^{-1};當手臂自由度數與任務維度不同(冗餘或欠缺自由度)時,虛擬反矩陣能給出最小平方意義上最好的解。

阻尼最小平方法(Damped Least Squares, DLS)

Newton-Raphson 法在接近奇異點時會數值不穩定:奇異點附近 JJ 的最小奇異值趨近於 0,J+J^{+} 裡對應的項會趨近無限大,算出的關節角度增量會出現不合理的巨大跳動。實務上幾乎都改用阻尼最小平方法,在反矩陣公式裡加入阻尼項:

Δθ=JT(JJT+λ2I)1Δx\Delta\boldsymbol{\theta} = J^T \left(J J^T + \lambda^2 I\right)^{-1} \Delta x

λ\lambda 是阻尼係數,λ=0\lambda = 0 時退化回標準虛擬反矩陣解法。阻尼項讓求解在奇異點附近仍然數值穩定,代價是犧牲一點點追蹤精度——這正是機器人控制裡處理奇異點最常見、最實用的工程妥協,本質上跟 Levenberg-Marquardt 演算法用來解非線性最小平方問題的阻尼項是同一套數學。

程式碼範例

2 連桿解析解(含肘部朝上/朝下判斷)

python
import numpy as np


def ik_2link_analytical(x, y, l1, l2, elbow_up=True):
    """2 連桿平面手臂的解析 IK,回傳 (theta1, theta2)"""
    r_sq = x ** 2 + y ** 2
    cos_theta2 = (r_sq - l1 ** 2 - l2 ** 2) / (2 * l1 * l2)

    if not -1.0 <= cos_theta2 <= 1.0:
        raise ValueError("目標超出工作空間,無解")

    sign = 1 if elbow_up else -1
    theta2 = sign * np.arccos(cos_theta2)

    theta1 = np.arctan2(y, x) - np.arctan2(
        l2 * np.sin(theta2), l1 + l2 * np.cos(theta2)
    )
    return theta1, theta2

阻尼最小平方法數值迭代解

python
import numpy as np


def numeric_jacobian(fk_func, theta, eps=1e-6):
    """用中央差分數值近似 Jacobian,fk_func(theta) 回傳末端座標向量"""
    n = len(theta)
    x0 = fk_func(theta)
    m = len(x0)
    J = np.zeros((m, n))
    for i in range(n):
        dtheta = np.zeros(n)
        dtheta[i] = eps
        x_plus = fk_func(theta + dtheta)
        x_minus = fk_func(theta - dtheta)
        J[:, i] = (x_plus - x_minus) / (2 * eps)
    return J


def ik_dls(fk_func, theta0, target, max_iter=200, tol=1e-4, damping=0.05):
    """阻尼最小平方法數值迭代 IK"""
    theta = theta0.copy()
    for _ in range(max_iter):
        x_current = fk_func(theta)
        error = target - x_current
        if np.linalg.norm(error) < tol:
            return theta, True

        J = numeric_jacobian(fk_func, theta)
        JJt = J @ J.T
        damped_inv = J.T @ np.linalg.inv(JJt + (damping ** 2) * np.eye(JJt.shape[0]))
        theta = theta + damped_inv @ error

    return theta, False  # 未收斂

numeric_jacobian 用中央差分近似而不是解析推導,是刻意的設計選擇:它不需要針對每一種手臂結構手動推導 Jacobian 公式,適合快速原型驗證;真正上線的控制系統通常會改用解析 Jacobian(見雅可比矩陣一文),因為數值微分每次迭代都要多算 2n2n 次正向運動學,計算成本明顯更高。

常見錯誤

  • atan 取代 atan2atan 沒有象限資訊,在特定象限會直接算出錯誤方向的角度,這是解析 IK 裡最常見的隱蔽 bug。
  • 忽略肘部朝上/朝下的分支判斷:只實作其中一組解,導致手臂在某些目標點附近選錯關節組態,動作路徑不連續或直接撞到自身。
  • 直接用標準虛擬反矩陣、不加阻尼:在靠近奇異點的軌跡上會讓關節角速度指令出現不合理的尖峰,實機上會表現成手臂突然劇烈甩動。
  • 沒有先檢查目標是否在工作空間內就丟給數值迭代法:無解的目標會讓迭代法跑滿所有疊代次數卻無法收斂,浪費運算資源;解析解法可以先用簡單的距離檢查提前擋掉這類請求。
  • 冗餘自由度手臂只用虛擬反矩陣求最小範數解,卻沒有處理零空間(null space)任務:這樣會讓手臂在避障、避免關節極限這類次要目標上毫無作為,明明有多餘自由度卻沒有被利用。

延伸閱讀

常見問題

faq_01.log
逆向運動學一定有解析解嗎?
不一定。Pieper 在 1968 年證明:只有當機械手臂滿足特定幾何條件時(最常見的是最後三個旋轉關節軸線交於一點,也就是「球形手腕」設計),才保證存在封閉形式的解析解。這也是為什麼絕大多數工業六軸手臂都刻意把手腕設計成球形結構——不是巧合,而是為了讓 IK 可以用解析解即時求解,不需要依賴數值迭代。不滿足這個條件的手臂(例如某些協作型手臂的非球形手腕設計)就只能依賴數值方法。
faq_02.log
為什麼數值解法要用阻尼最小平方法,而不是直接用 Jacobian 反矩陣?
直接用 Jacobian 反矩陣(或虛擬反矩陣)在接近奇異點時會數值爆炸——因為奇異點附近 Jacobian 的最小奇異值趨近於 0,反矩陣裡對應的項會趨近無限大,導致計算出的關節角速度出現不合理的巨大數值,讓手臂做出劇烈甩動的動作。阻尼最小平方法在反矩陣的公式裡加入一個阻尼項 λ²I,犧牲一點點在奇異點附近的追蹤精度,換取數值穩定性,這個 trade-off 在實務上幾乎總是值得的。
faq_03.log
IK 迭代法找不到解的時候,是真的無解還是演算法沒收斂?
兩種情況都要檢查,但可以先用比較便宜的方式排除:先檢查目標點是否在手臂的可達工作空間(workspace)內——例如距離基座是否超過手臂完全伸直的長度,或小於手臂完全收起的最短距離,這種情況是真的幾何無解,不管換什麼演算法或初始值都不會收斂。如果目標在工作空間內卻還是不收斂,通常是初始猜測值離解太遠掉進局部極小值、或是目標姿態本身正好落在奇異點附近,這時候換一個初始關節角度重新迭代,或改用 CCD/FABRIK 這類對初始值比較不敏感的方法,往往能解決問題。