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.