Degrees of freedom in kinematic chain?

Provide easy-to-use, extensible software for modeling, simulating, controlling, and analyzing the neuromusculoskeletal system.
POST REPLY
User avatar
Jeff Barrett
Posts: 2
Joined: Sat Sep 07, 2019 8:16 am

Degrees of freedom in kinematic chain?

Post by Jeff Barrett » Wed May 06, 2020 7:58 am

Hi all!

I'm trying to make a kinematic chain of n-rigid bodies connected together by BallJoints with one segment held to the frame by a WeldJoint. I've got a script that assembles this kinematic chain with no issues, and I'm able to animate simulations with it, which is pretty cool. By my count, for a chain composed of n-rigid bodies, there will be (n-1) BallJoints, which means 3(n-1) degrees of freedom. This makes sense to me, but when I call s.getQ() I don't see that. Instead, there seems to be an extra degree of freedom for each joint in the kinematic chain. Weirder still the CoordinateSet ends up having the right number of degrees of freedom?

For example, for a chain with 8-segments in an model file called "kinematic_chain.osim":

Code: Select all

import opensim as osim

model = osim.Model('kinematic_chain.osim')


# disparate numbers of degrees of freedom
s = model.initSystem()
print(len(s.getQ()))
print(model.getCoordinateSet().getSize())

I get outputs:
>> 28
>> 21

Instead of 21 for both, which is what I would expect. My question is this: where are these extra degrees of freedom coming from? Selfishly, I'm asking this question since I've noticed that these phantom degrees-of-freedom end up throwing off inverse dynamics. I think this is because of InverseDynamicsSolver indexing through Q and assigning real data to these meaningless extra degrees of freedom.


Here's the script that I used to make the kinematic chain, please let me know if there's something I missed:

Code: Select all

import opensim as osim



# ---------------------------------------------------------------------------
# Details about the Kinetic Chain
# ---------------------------------------------------------------------------
# store all the osim.Body objects in a dictionary called seg_dict
# store all the osim.BallJoint objects in a dictionary called joint_dict
# specify the number of segments
n_segs = 8 # number of segments in the kinetic chain
seg_dict = dict()
joint_dict = dict()
seg_names = ["seg{0}".format(i) for i in range(n_segs)]
joint_names = ["{0}-{1}".format(x,y) for x,y in zip(seg_names[0:-1], seg_names[1:])]



# ---------------------------------------------------------------------------
# Initialize the Model
# ---------------------------------------------------------------------------
model = osim.Model()
model.setName("kinematic_chain")


# ---------------------------------------------------------------------------
# Create all the segments
# ---------------------------------------------------------------------------
# they all have a 10 kg mass, and identity for moment of inertia
# they look like ellispoids for this example
seg_mass = 10.0 # mass
seg_com = osim.Vec3(0.0, 0.0, 0.0) # vector to COM
seg_inertia = osim.Inertia(1.0, 1.0, 1.0)
for seg in seg_names:
    seg_dict[seg] = osim.Body(seg, seg_mass, seg_com, seg_inertia)
    seg_dict[seg].attachGeometry(osim.Ellipsoid(0.1, 0.1, 0.1))


# ---------------------------------------------------------------------------
# Create all the joints
# ---------------------------------------------------------------------------
# joints are named like seg_i-seg_j, where the seg_i sits above seg_j
# the center of rotation for the seg0-seg1 joint in seg0 is 10 cm below its CoM
# the center of rotation for the seg0-seg1 joint in seg1 is 10 cm above its CoM
sup_cor = osim.Vec3(0.0, -0.1, 0.0)
inf_cor = osim.Vec3(0.0, 0.1, 0.0) 
zero_vec = osim.Vec3(0.0, 0.0, 0.0)
for joint in joint_names:
    sup, inf = joint.split("-")
    joint_dict[joint] = osim.BallJoint(joint, seg_dict[inf], inf_cor, zero_vec, 
                                              seg_dict[sup], sup_cor, zero_vec)

# finally the mount, a weld-joint from seg(n-1) to global (the bottom top)
mount = osim.WeldJoint("{0}-Global".format(seg_names[0]),
                       model.getGround(), zero_vec, zero_vec,
                       seg_dict[seg_names[0]], zero_vec, zero_vec)

# ---------------------------------------------------------------------------
# Adding everything to the model
# ---------------------------------------------------------------------------
for seg in seg_names:
    model.addBody(seg_dict[seg])

for joint in joint_names:
    model.addJoint(joint_dict[joint])
model.addJoint(mount)

# ---------------------------------------------------------------------------
# Finalize Connections and Save
# ---------------------------------------------------------------------------
model.finalizeConnections()
model.printToXML('kinematic_chain.osim')


Thanks!
Jeff B.

Tags:

User avatar
Michael Sherman
Posts: 807
Joined: Fri Apr 01, 2005 6:05 pm

Re: Degrees of freedom in kinematic chain?

Post by Michael Sherman » Wed May 06, 2020 9:58 am

Ball joints use quaternions for singularity-free orientation. Those have 4 coordinates, but only 3 degrees of freedom (i.e. 4 position states q and 3 velocity states u).

If you aren't worried about singularities, you can use Gimbal joints instead of Ball joints. Those have 3 orientation coordinates but are subject to singularities when the middle coordinates approaches pi/2.

Sherm

User avatar
Jeff Barrett
Posts: 2
Joined: Sat Sep 07, 2019 8:16 am

Re: Degrees of freedom in kinematic chain?

Post by Jeff Barrett » Wed May 06, 2020 10:12 am

Oh that makes a lot of sense! Thank you!

POST REPLY