Is there some way for me to read the current (final/solved) value of the Jacobian that the iTaSC IK solver uses? My true objective is to measure the ‘kinematic coefficient’ of a mechanism in its current state, and thus estimate the mechanical advantage. My research thus far has not yielded any results:
old wiki page confirming there are jacobians under the hood
source code mirror really just reveals that I had forgotten about the RNA system and have no idea where to look to see if the code has hooks that the Python API somehow isn’t listing
WORKAROUND: Ended up not being able to figure out the real solution, so I made a quick and dirty script to get an approximation. Basically measures the current rotation between sequential bones, then shift the IK target slightly, measure angles again and find the delta. (then move the target object back, haha).
import bpy
from mathutils import Vector
from math import acos, fabs
# select the armature to work with
arm = bpy.data.objects['ArmatureObject'].pose
# set the object to move
control_object = bpy.data.objects['Empty']
# set the test direction to move (global)
delta_cart = Vector([0, 0, 1e-2])
# trace, for 3x3 matrices only. not sure why this isn't in mathutils?
def trace(matrix):
return matrix[0][0] + matrix[1][1] + matrix[2][2]
def getRotationRelativeToParent(bone):
if bone.parent == None:
return None
mat1 = bone.parent.matrix.to_3x3()
mat1.invert()
mat2 = bone.matrix.to_3x3()
relative = mat1 * mat2
# surely there is a better way to check the identity transform?
if relative[0][0] > .9999 and relative[1][1] > .9999 and relative[2][2] > .9999:
return 0
#print(relative)
# now use angle axis form to
# TODO might need some sign checking
assert (trace(relative) - 1)/2.0 > -1 and (trace(relative) - 1)/2.0 < 1, 'acos will fail'
theta = acos((trace(relative) - 1)/2.0)
return theta
# dictionary. key is name of bone. values are initial, final, delta
boneMetrics = {}
# for each bone... log its current rotation relative to parent
print('Performing Initial Pass...')
for bone in arm.bones: # each is an object
boneMetrics[bone.name] = {'initial':getRotationRelativeToParent(bone), 'final':0, 'delta': 0}
# move control_object a bit
control_object.location = control_object.location + delta_cart
# force a redraw to update positions
bpy.ops.wm.redraw_timer(type='DRAW', iterations=1)
# for each bone... log its new rotation relative to parent
print('Performing Second Pass...')
for bone in arm.bones:
boneMetrics[bone.name]['final'] = getRotationRelativeToParent(bone)
if boneMetrics[bone.name]['final'] is not None:
boneMetrics[bone.name]['delta'] = fabs(boneMetrics[bone.name]['final'] - boneMetrics[bone.name]['initial'])
if boneMetrics[bone.name]['delta'] > 1e-8:
print(bone.name, ' ... ', delta_cart.magnitude/boneMetrics[bone.name]['delta'], 'Nm / N')
# move the control object back
control_object.location = control_object.location - delta_cart