Fabrik Inverse Kinematics solverΒΆ
(Taken from the ganja.js coffeeshop)
In the inverse kinematics problem we need to calculate joint angles of a kinematic chain so its base remains fixed and its tip reaches a given target. This is a highly non-linear problem with many solutions. We implement a solver that tries to minimize the differences on all remaining degrees of freedom.
This algorithm readily translates to 3D, is efficient and very well received by artists in a cg animation context.
First we create an algebra. Feel free to try 3D instead of 2D!
[1]:
from kingdon import Algebra
import numpy as np
from timeit import default_timer
from functools import wraps
def tonp(func):
@wraps
def wrapped_func(*args, **kwargs):
return np.array(func(*args, **kwargs))
return func
alg = Algebra(2, 0, 1, wrapper=tonp)
We then need to set the number of points in the chain, and we initialize points equally along the chain.
[2]:
l = 6
d = 3 / l
points = [alg.vector(e0=1, e1=i * d - 1.5, e2=0, e3=0).dual()
for i in range(l + 1)]
Now we define the actual IK solver. Last point in the chain c
is the target. We set the tip as the target, then cycle to the base and back restoring original lengths.
[3]:
def translator(line, dist):
""" Translate along the line `line` by a distance `dist`. """
e, e0 = alg.blades.e, alg.blades.e0
return 1 - 0.5 * dist * (e0 * line.normalized()*e0.dual())
def inverse_kinematics(c):
# Run four relaxation steps
for j in range(4):
# Set the tip to the target. (this will change the length of the last segment.)
c[-2] = c[-1]
# Run backwards to the base and restore the lengths along the chain.
for i in range(l-2, 0, -1):
c[i] = translator(c[i] & c[i + 1], d) >> c[i + 1]
# Loop the other way from base to tip again restoring all lengths.
for i in range(1, l):
c[i] = translator(c[i - 1] & c[i], -d) >> c[i - 1]
[4]:
def graph_func():
inverse_kinematics(points)
return [
0x224488, f"Inverse Kinematics",
0x008844, *zip(points[1:-1], points[:-2]), # Render line segments [[A,B],[B,C],..]
0x880088, points[0], "Base", # Render base
0x00DD88, *points[1:l], # Render joint points. [A,B,C,..]
0x880088, points[l], "Target", # Render target in purple
]
g = alg.graph(
graph_func, grid=True, lineWidth=6, labels=True,
)
g
[4]:
[ ]: