除了旋轉矩陣,旋轉可以用角度表示,也可以用complex number of the unit circle表示,但它確實是一回事。更重要的是,你需要一個代表T
的rigid body transformations,這樣你就可以編寫像t1 * t2 * t3
這樣的東西來計算第三個鏈接的位置和方向。
使用atan2
來計算angle between the vectors。
正如以下Python示例所示,這兩件事足以構建一個小型IK解算器。
from gameobjects.vector2 import Vector2 as V
from matrix33 import Matrix33 as T
from math import sin, cos, atan2, pi
import random
的gameobjects庫沒有2D轉換,所以你要自己寫matrix33
。它的接口就像gameobjects.matrix44
。
定義從一個關節到下一個關節轉換的正向運動學函數。我們通過angle
擔任聯合旋轉,接着是一個固定的轉型joint
:
def fk_joint(joint, angle): return T.rotation(angle) * joint
工具的轉型是tool == fk(joints, q)
其中joints
是固定的轉換和q
是關節角度:
def fk(joints, q):
prev = T.identity()
for i, joint in enumerate(joints):
prev = prev * fk_joint(joint, q[i])
return prev
如果手臂的底部有偏移量,則替換T.identity()
變換。
OP正在通過循環座標下降來解決位置IK問題。這個想法是通過一次調整一個關節變量來將工具靠近目標位置。設q
爲聯合的角度,並且prev
爲聯合的基礎的變換。接頭應通過向工具和目標位置的矢量之間的角度進行旋轉:
def ccd_step(q, prev, tool, goal):
a = tool.get_position() - prev.get_position()
b = goal - prev.get_position()
return q + atan2(b.get_y(), b.get_x()) - atan2(a.get_y(), a.get_x())
遍歷關節和更新關節值的每一個變化的工具結構:
def ccd_sweep(joints, tool, q, goal):
prev = T.identity()
for i, joint in enumerate(joints):
next = prev * fk_joint(joint, q[i])
q[i] = ccd_step(q[i], prev, tool, goal)
prev = prev * fk_joint(joint, q[i])
tool = prev * next.get_inverse() * tool
return prev
注意對於3D,fk()
和ccd_sweep()
是相同的;你只需要重寫fk_joint()
和ccd_step()
。
構造一個臂n
相同的鏈接和運行CCD掃描的cnt
迭代,從隨機臂形態q
開始:
def ccd_demo(n, cnt):
q = [random.uniform(-pi, pi) for i in range(n)]
joints = [T.translation(0, 1)] * n
tool = fk(joints, q)
goal = V(0.9, 0.75) # Some arbitrary goal.
print "i Error"
for i in range(cnt):
tool = ccd_sweep(joints, tool, q, goal)
error = (tool.get_position() - goal).get_length()
print "%d %e" % (i, error)
我們可以嘗試求解器和比較收斂的不同數量的速度鏈接:
>>> ccd_demo(3, 7)
i Error
0 1.671521e-03
1 8.849190e-05
2 4.704854e-06
3 2.500868e-07
4 1.329354e-08
5 7.066271e-10
6 3.756145e-11
>>> ccd_demo(20, 7)
i Error
0 1.504538e-01
1 1.189107e-04
2 8.508951e-08
3 6.089372e-11
4 4.485040e-14
5 2.601336e-15
6 2.504777e-15