Another way to connect two rigid bodies together is to use a frame spring, which is a six dimensional spring that generates restoring forces and moments between coordinate frames.
The basic idea of a frame spring is shown in Figure 3.24. It generates restoring forces and moments on two frames C and D which are a function of and (the spatial velocity of frame D with respect to frame C).
Decomposing forces into stiffness and damping terms, the force and moment acting on C can be expressed as
where the translational and rotational forces , , , and are general functions of and .
The forces acting on D are equal and opposite, so that
If frames C and D are attached to a pair of rigid bodies A and B, then a frame spring can be used to connect them in a manner analogous to a joint. As with joints, C and D generally do not coincide with the body frames, and are instead offset from them by fixed transforms and (Figure 3.25).
The restoring forces (3.28) generated in a frame spring depend on the frame material associated with the spring. Frame materials are defined in the package artisynth.core.materials, and are subclassed from FrameMaterial. The most basic type of material is a LinearFrameMaterial, in which the restoring forces are determined from
where gives the small angle approximation of the rotational components of with respect to the , , and axes, and
are the stiffness and damping matrices. The diagonal values defining each matrix are stored in the 3-dimensional vectors , , , and which are exposed as the stiffness, rotaryStiffness, damping, and rotaryDamping properties of the material. Each of these specifies stiffness or damping values along or about a particular axis. Specifying different values for different axes will result in anisotropic behavior.
Other frame materials offering nonlinear behavior may be defined in artisynth.core.materials.
Frame springs are implemented by the class FrameSpring. Creating a frame spring generally involves instantiating this class, and then setting the material, the bodies A and B, and the transforms and .
A typical construction sequence might look like this:
The material is set using setMaterial(). The example above uses a LinearFrameMaterial, created with a constructor that sets , , , and to uniform Isotropic values specified by kt, kr, dt, and dr.
The bodies and transforms can be set in the same manner as for joints
(Section 3.3.3), with the
setFrames(bodyA,bodyB,TDW) and setFrames(bodyA,TCA,bodyB,TDB) assuming the role of the setBodies() methods used for joints. The former takes D specified in world coordinates and computes and assuming that there is no initial spring displacement (i.e., that ), while the latter allows and to be specified explicitly with assuming whatever value is implied.
Frame springs and joints are often placed together, using the same transforms and , with the spring providing restoring forces to help keep the joint within prescribed bounds.
As with joints, a frame spring can be connected to only a single body, by specifying frameB as null. Frame B is then taken to be the world coordinate frame W.
A simple model showing two simplified lumbar vertebrae, modeled as rigid bodies and connected by a frame spring, is defined in
The definition for the entire model class is shown here:
For convenience, the code to create and add each vertebrae is wrapped into the method addBone() defined at lines 27-32. This method takes two arguments: the MechModel to which the bone should be added, and the name of the bone. Surface meshes for the bones are located in .obj files located in the directory ../mech/geometry relative to the source directory for the model itself. PathFinder.getSourceRelativePath() is used to find a proper path to this directory (see Section 2.6) given the model class type (LumbarFrameSpring.class), and this is stored in the static string geometryDir. Within addBone(), the directory path and the bone name are used to create a path to the bone mesh itself, which is in turn used to create a PolygonalMesh (line 28). The mesh is then used in conjunction with a density to create a rigid body which is added to the MechModel (lines 29-30) and returned.
The build() method begins by creating and adding a MechModel, specifying a low value for gravity, and setting the rigid body damping properties frameDamping and rotaryDamping (lines 37-41). (The damping parameters are needed here because the frame spring itself is created with no damping.) Rigid bodies representing the vertebrae lumbar1 and lumbar2 are then created by calling addBone() (lines 44-45), lumbar1 is translated by setting the origin of its pose to , and lumbar2 is set to be fixed by making it non-dynamic (line 47).
At this point in the construction, if the model were to be loaded, it would appear as in Figure 3.27. To change the viewpoint to that seen in Figure 3.26, we rotate the entire model about the axis (line 50). This is done using transformGeometry(X), which transforms the geometry of an entire model using a rigid or affine transform. This method is described in more detail in Section 4.8.
The frame spring is created and added at lines 54-59, using the methods described in Section 3.5.3, with frame D set to the (initial) pose of lumbar1.
Render properties are set starting at line 62. By default, a frame spring renders as a pair of red, green, blue coordinate axes showing frames C and D, along with a line connecting them. The line width and the color of the connecting line are controlled by the line render properties lineWidth and lineColor, while the length of the coordinate axes is controlled by the special frame spring property axisLength.
To run this example in ArtiSynth, select All demos > tutorial > LumbarFrameSpring from the Models menu. The model should load and initially appear as in Figure 3.26. Running the model (Section 1.5.3) will cause lumbar1 to fall slightly under gravity until the frame spring arrests the motion. To get a sense of the spring’s behavior, one can interactively apply forces to lumbar1 using the pull tool (see the section “Pull Manipulation” in the ArtiSynth User Interface Guide).