逆向運動學完整解法:解析解、Pieper 準則與數值迭代法
- inverse-kinematics
- jacobian
- dh-parameters
- singularity
- numerical-methods
問題定義
逆向運動學(Inverse Kinematics, IK)要回答的問題是:已知末端執行器的目標位置與姿態 ,要找出一組關節角度 ,使得正向運動學算出來的末端姿態恰好等於目標:
這個問題比正向運動學本質上難得多,原因有三個,而且都不是工程實作細節,是數學結構層面的困難:
- 非線性: 是關節角度的三角函數組合(、 疊乘),不存在一般線性方程組的解法。
- 解的數量不固定:同一個末端位置,手臂可能有零組解(目標超出工作空間)、恰好一組解、或者多組解(例如肘部朝上與朝下都能到達同一點)。6 自由度手臂在非奇異姿態下,理論上最多可以有到 16 組不同的關節組合解。
- 可能存在冗餘自由度:如果手臂的自由度數大於任務所需的自由度(例如 7 軸手臂做 6 維度的末端姿態任務),解不是離散的幾組,而是一整個連續的解空間(self-motion manifold),這時候額外需要一個準則(例如避開障礙物、避開關節極限、最小化能量)去挑選解空間裡的其中一點。
解析解:從 2 連桿平面手臂開始
在推導一般化方法之前,先看一個可以完全手算的具體例子:一個平面上的 2 連桿手臂,連桿長度分別是 、,兩個都是旋轉關節,目標是讓末端到達平面上的座標 。
用餘弦定理解肘部角度
末端到基座的直線距離是 。這條直線、、 三條邊構成一個三角形,肘部角度 (第二關節相對第一連桿的夾角)可以直接用餘弦定理反解:
正負號對應的正是「肘部朝上」與「肘部朝下」兩組不同的解——這是前面提到的「解不唯一」在最簡單案例裡的具體展現。 的輸入值必須落在 之間,一旦超出這個範圍,就代表目標點 落在手臂的工作空間之外(太遠或太近),這是解析解可以直接、便宜地判斷「目標無解」的優勢,數值迭代法通常要跑到不收斂才能間接推論出同樣的結論。
反解肩部角度
有了 ,肩部角度 可以用兩步反解:先算出從基座看向末端的角度,再減去第一連桿到目標點方向之間的夾角:
這裡刻意用 atan2 而不是 atan,是因為 atan2 能根據兩個輸入的正負號正確判斷象限,atan 只能算出 範圍內的角度,直接拿來做機械手臂控制會在特定象限算錯方向——這是解析 IK 實作裡最常見、也最隱蔽的 bug 來源之一。
6 自由度手臂:Pieper 解耦解法
真實工業手臂通常是 6 自由度串聯結構,直接對 6 個未知數解一組聯立三角方程,計算複雜度會爆炸。1968 年 Donald Pieper 證明了一個關鍵定理:只要手臂最後三個旋轉關節的軸線交於一點(這一點稱為手腕中心,wrist center),問題就可以解耦成兩個獨立的子問題,各自都有封閉形式解——這正是絕大多數工業六軸手臂刻意採用「球形手腕」(spherical wrist)設計的原因。
解耦之後的兩步驟:
第一步:位置反解(前三軸)。因為最後三軸交於手腕中心點 ,而且旋轉關節不影響該點沿軸線之外的位置,手腕中心的位置只由前三個關節決定:
其中 是最後一節連桿長度、 是目標姿態沿末端 z 軸的方向向量。算出 之後,前三軸 的問題就退化成跟前面 2 連桿平面手臂類似的幾何問題(只是多一個自由度、在三維空間中),一樣可以用餘弦定理與 atan2 解出封閉形式解。
第二步:姿態反解(後三軸)。有了前三軸的解,就能算出前三軸構成的座標系相對基座的旋轉矩陣 。剩下要做的是求解 ,使得:
因為後三軸是球形手腕(三軸交於一點、且互相垂直), 對應的正是一組尤拉角分解(常見的是 ZYZ 或 ZYX 順序),可以直接反解出 ,公式形式跟旋轉矩陣轉尤拉角的標準解法一致。
這個「先解位置、再解姿態」的解耦技巧,把一個 6 維非線性聯立方程組問題,拆成兩個各自 3 維、各自有封閉形式解的子問題,這是解析解法在工程上能夠實用化的關鍵。
數值迭代解法:當解析解不存在時
如果手臂不滿足 Pieper 準則(例如某些非球形手腕的協作手臂、或者自由度數與任務維度不匹配的冗餘手臂),就必須依賴數值迭代法。核心想法是:從一個初始猜測關節角度 出發,每次迭代都用雅可比矩陣算出「如果末端要往目標方向移動一點點,關節角度大致要怎麼調整」,逐步逼近目標。
Newton-Raphson 法
定義末端目前姿態與目標姿態之間的誤差 (位置誤差 3 維 + 姿態誤差 3 維,共 6 維)。雅可比矩陣 描述了關節角速度與末端速度的線性關係 ,把這個關係離散化、反過來用,就得到更新規則:
其中 是 Jacobian 的虛擬反矩陣(Moore-Penrose pseudoinverse)。當 是方陣且非奇異時,;當手臂自由度數與任務維度不同(冗餘或欠缺自由度)時,虛擬反矩陣能給出最小平方意義上最好的解。
阻尼最小平方法(Damped Least Squares, DLS)
Newton-Raphson 法在接近奇異點時會數值不穩定:奇異點附近 的最小奇異值趨近於 0, 裡對應的項會趨近無限大,算出的關節角度增量會出現不合理的巨大跳動。實務上幾乎都改用阻尼最小平方法,在反矩陣公式裡加入阻尼項:
是阻尼係數, 時退化回標準虛擬反矩陣解法。阻尼項讓求解在奇異點附近仍然數值穩定,代價是犧牲一點點追蹤精度——這正是機器人控制裡處理奇異點最常見、最實用的工程妥協,本質上跟 Levenberg-Marquardt 演算法用來解非線性最小平方問題的阻尼項是同一套數學。
程式碼範例
2 連桿解析解(含肘部朝上/朝下判斷)
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
阻尼最小平方法數值迭代解
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(見雅可比矩陣一文),因為數值微分每次迭代都要多算 次正向運動學,計算成本明顯更高。
常見錯誤
- 用
atan取代atan2:atan沒有象限資訊,在特定象限會直接算出錯誤方向的角度,這是解析 IK 裡最常見的隱蔽 bug。 - 忽略肘部朝上/朝下的分支判斷:只實作其中一組解,導致手臂在某些目標點附近選錯關節組態,動作路徑不連續或直接撞到自身。
- 直接用標準虛擬反矩陣、不加阻尼:在靠近奇異點的軌跡上會讓關節角速度指令出現不合理的尖峰,實機上會表現成手臂突然劇烈甩動。
- 沒有先檢查目標是否在工作空間內就丟給數值迭代法:無解的目標會讓迭代法跑滿所有疊代次數卻無法收斂,浪費運算資源;解析解法可以先用簡單的距離檢查提前擋掉這類請求。
- 冗餘自由度手臂只用虛擬反矩陣求最小範數解,卻沒有處理零空間(null space)任務:這樣會讓手臂在避障、避免關節極限這類次要目標上毫無作為,明明有多餘自由度卻沒有被利用。
延伸閱讀
常見問題
- 逆向運動學一定有解析解嗎?
- 不一定。Pieper 在 1968 年證明:只有當機械手臂滿足特定幾何條件時(最常見的是最後三個旋轉關節軸線交於一點,也就是「球形手腕」設計),才保證存在封閉形式的解析解。這也是為什麼絕大多數工業六軸手臂都刻意把手腕設計成球形結構——不是巧合,而是為了讓 IK 可以用解析解即時求解,不需要依賴數值迭代。不滿足這個條件的手臂(例如某些協作型手臂的非球形手腕設計)就只能依賴數值方法。
- 為什麼數值解法要用阻尼最小平方法,而不是直接用 Jacobian 反矩陣?
- 直接用 Jacobian 反矩陣(或虛擬反矩陣)在接近奇異點時會數值爆炸——因為奇異點附近 Jacobian 的最小奇異值趨近於 0,反矩陣裡對應的項會趨近無限大,導致計算出的關節角速度出現不合理的巨大數值,讓手臂做出劇烈甩動的動作。阻尼最小平方法在反矩陣的公式裡加入一個阻尼項 λ²I,犧牲一點點在奇異點附近的追蹤精度,換取數值穩定性,這個 trade-off 在實務上幾乎總是值得的。
- IK 迭代法找不到解的時候,是真的無解還是演算法沒收斂?
- 兩種情況都要檢查,但可以先用比較便宜的方式排除:先檢查目標點是否在手臂的可達工作空間(workspace)內——例如距離基座是否超過手臂完全伸直的長度,或小於手臂完全收起的最短距離,這種情況是真的幾何無解,不管換什麼演算法或初始值都不會收斂。如果目標在工作空間內卻還是不收斂,通常是初始猜測值離解太遠掉進局部極小值、或是目標姿態本身正好落在奇異點附近,這時候換一個初始關節角度重新迭代,或改用 CCD/FABRIK 這類對初始值比較不敏感的方法,往往能解決問題。