I’m new to blender and to python, so please go easy on me!
I’ve built a human model, whose bones I move via mocap , and I want to get the angle values (X,Y, Z) between body limbs.
For example, I want the 3D orientation of the hand in reference to the forearm coordinate axis system.
I’ve searched for days and I have tried several methods, like “joint_rotation” and ‘‘rotation_difference’’, but I don’t get the desired result.
I think Blender gives me the angles in reference to the global axis, or other axis system.
How can I do this properly? It seemed to be simple and straightforward, but i’m finding a lot of difficulty here.
Hmmmm, rotation is pretty much always tricky(at least for me : P), especially when computing them somehow.
If I recall correctly, the BGE uses quaternions to represent orientation, so you’ll have to get those(for the bones being measured between) and number crunch 'em a little to get your desired results.
I’ve done this before, but I won’t have access to that data for at least half a year(I’m away from home at the moment).
However, I’m not sure that’s really necessary - directly interacting with bones in the BGE is very expensive computationally, but since this is the BGE we have lots of options.
What do you intend to use this for? The more we know about your specific problem, the easier it is for us to help ; )
I’m getting the ABSOLUTE orientation of the bones (in quaternions) through some imu sensors, and with that quaternions the bones move to the desired orientation using rotation_quaternion (motion capture).
The purpose of the project is to measure the angles (X,Y,Z) between body limbs. I manage to capture the movements correctly, but I’m having trouble to display this angles. I used “joint_rotation” and ‘‘rotation_difference’’, but I didn’t get the desired result, and I think that’s because of the coordinate system.
I’d like to get the angles of one limb (for example a hand) in reference to the local axis of his parent (in this case, the forearm)
As far as I know the rotation is relative to the parent bone. I do not understand the exact issue. Maybe you show some of your results (demo .blend, the values you get and the values you expect).
I have the absolute orientation of the body limbs (hand, forearm, arm, etc) in quaternions, given by inertial sensors put in these limbs. I want to get the orientation of each limb in respect to its parent. The blender armature moves correctly, so I KNOW the data is being transferred right. I’m not being able to get the relative orientation (in EULER angles) in real time during Blender Game.
I have tried “joint rotation” and “rotation difference” functions. The values I get are correct till I rotate the limb around Y axis. At that moment, Z and X values for orientation start to get wrong, so I think it’s a problem with coordinate systems.
What do you mean with “absolute orientation”? Absolute typically mean it is relative to the world (scene). Then you talk about “orientation … in respect to its parent” which is the local orientation. This is what you already get when reading the channels. The channels define the transformation from parent space.
I run a test and I can confirm that changing the rotation of a parent bone (channel) does not change the rotation of the child bone (channel).
import math
def toDegree(vectorInRadians):
vector = vectorInRadians.copy()
for i in range(len(vector)):
vector[i] = math.degrees(vector[i])
return vector
evidently i m almost the unique to know(/experiment) the API of armature
bone.rotation_quaternion is the rotation relative to the bone itself in rest pose
you can get the matrix4x4 of the bone(in armature space), convert in quaternion and get the difference
import bge
def get_angles_difference(bone1, bone2):
q1,q2 = [b.pose_matrix.to_quaternion() for b in [bone1,bone2]]
return q1.rotation_difference(q2).to_euler()
cont = bge.logic.getCurrentController()
arm = cont.owner
b1 = arm.channels["hand"]
b2 = b1.parent
x,y,z = get_angles_difference(b1,b2)
print(x,y,z)
I tried MarcoIT’s code but the results are the same. It measures the angles X and Z correctly, but when the limb rotates around Y axis, it doesn’t show the expected values for X and Z (I rotate around X and it changes the values for X and Z)
i not think is wrong, instead is not what you want to know, you do nothing with the rotation difference with the parent, im pretty sure.
i suppose you use constraint (copy trasform, world->world) to move the bones.
since you say you had already all quaternions of the bone :
I have the absolute orientation of the body limbs (hand, forearm, arm, etc) in quaternions, given by inertial sensors put in these limbs. I want to get the orientation of each limb in respect to its parent.
so basically the thing you have to do (to save the animation) should be:
remove all flags of “inerit Rotation” otherwise each bone “parent” force the childrens with it rotations and do a mess.
with constraint is not a problem since constraints overrite all.
after that , -i had made a bit of tests- you can read the offset correct (bone.rotation_quaternion) and is this that you should save(not the rotation difference with parent)
that is the code:
import bge
cont = bge.logic.getCurrentController()
arm = cont.owner
if not "action_quaternion" in arm:
arm["action_quaternion"] = []
for b in arm.channels:
m4 = (b.channel_matrix.inverted()*b.pose_matrix).inverted() *b.pose_matrix
q_offset = m4.to_quaternion()
arm["action_quaternion"].append(tuple(b.name, tuple(q_offset))) # this should be written on a file i guess
x,y,z = [round(v,2) for v in q_offset.to_euler()]
print(b.name, x,y,z)
arm.update()
i not understand fully this code, but simply work
but, remember, remove “inherit Rotation” to all bones
i made some changes “last minute” to the code that make it pretty hard to understand.you can do a test very simple (as was the original)use the original armature(ARMATURE MASTER) as is without change nothingand animate it normally as you do already(i guess with constraints)then do a duplicated armature using ALT+D.(ARMATURE SIM)on the duplicated armature remove the flags “inerit Rotation” and remove all constraints.then put an always true and this script on ARMATURE SIMthe armature sim read the armature masterand (if the script work) should animate in the same way as the master
the correct code (this time tested without changes!)
this code run on the “armatureMocap” only, the one that run already well ,
it write on a property of itself the quaternions “ready to use” for other armatures.
it not modificate the behaviour of the armature, is just a “translation of rotations” for other armatures.
this can have or not the inerit rotation, no matter
import bge
cont = bge.logic.getCurrentController()
own = cont.owner
if not "init" in own:
own["last_frame_quaternion"] = []
own["action_quaternion"] = []
own["init"] = 1
own["last_frame_quaternion"] = []
for b in own.channels:
m4 = (b.channel_matrix.inverted()*b.pose_matrix).inverted()*b.pose_matrix
q_offset = m4.to_quaternion()
own["last_frame_quaternion"].append((b.name, tuple(q_offset)))
own["action_quaternion"].append(own["last_frame_quaternion"])
then, this is the code that run on the “others armatures” that want mimic the armatureMocap
the armatures need to be similar , the names of the bones have to match with the names of bones of armatureMocap (where i added the try/except) , and the “inerit Rotation” must be removed in the UI otherwise the rotations are “multiplicated”
import bge
import mathutils
cont = bge.logic.getCurrentController()
own = cont.owner
if not "arm_mocap" in own:
arm_mocap = own.scene.objects["Armature_mocap"]
own["arm_mocap"] = own.scene.objects["Armature_mocap"]
else:
arm_mocap = own["arm_mocap"]
if not arm_mocap.invalid:
if "last_frame_quaternion" in arm_mocap:
for keyframe in arm_mocap["last_frame_quaternion"]:
bone_name, quaternion_offset = keyframe
try:
bone = own.channels[bone_name]
except:
print(bone_name, " not found")
continue
bone.rotation_quaternion = mathutils.Quaternion(quaternion_offset)
own.update()
if is not right this time i need to some more clarifications , because to me seem that work perfectly.
anyway keep in count that for trasformations global->global you can use constraints of the bone (as copy rotation(world->world or also world -> local, can be useful depend))
KeyError: “CList[key]: “Armature_mocap” key not in list”
I am probably doing one or more things wrong, because I don’t understand that ‘Armature mocap’ thing.(I’m a total newby with blender, never used it before and I don’t think I will use it much in the future)