3 Mechanical Models I

3.6 Frame springs

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.

3.6.1 Frame spring coordinate frames

Figure 3.28: A frame spring connecting two coordinate frames D and C.

The basic idea of a frame spring is shown in Figure 3.28. It generates restoring forces and moments on two frames C and D which are a function of {\bf T}_{DC} and \hat{\bf v}_{DC} (the spatial velocity of frame D with respect to frame C).

Decomposing forces into stiffness and damping terms, the force {\bf f}_{C} and moment \boldsymbol{\tau}_{C} acting on C can be expressed as

\displaystyle{\bf f}_{C} \displaystyle={\bf f}_{k}({\bf T}_{DC})+{\bf f}_{d}(\hat{\bf v}_{DC})
\displaystyle\boldsymbol{\tau}_{C} \displaystyle=\boldsymbol{\tau}_{k}({\bf T}_{DC})+\boldsymbol{\tau}_{d}(\hat{%
\bf v}_{DC}). (3.30)

where the translational and rotational forces {\bf f}_{k}, {\bf f}_{d}, \boldsymbol{\tau}_{k}, and \boldsymbol{\tau}_{d} are general functions of {\bf T}_{DC} and \hat{\bf v}_{DC}.

The forces acting on D are equal and opposite, so that

\displaystyle{\bf f}_{D} \displaystyle=-{\bf f}_{C},
\displaystyle\boldsymbol{\tau}_{D} \displaystyle=-\boldsymbol{\tau}_{C}. (3.31)
Figure 3.29: A frame spring connecting two rigid bodies A and B.

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 {\bf T}_{CA} and {\bf T}_{DB} (Figure 3.29).


For historical reasons, the default behavior for frame springs is to base the restoring forces on {\bf T}_{DC} and \hat{\bf v}_{DC}, as per (3.30). However, it may be more convenient to instead use {\bf T}_{CD} and \hat{\bf v}_{CD} and compute the force {\bf f}_{D} and moment \boldsymbol{\tau}_{D} acting on frame D:

\displaystyle{\bf f}_{D} \displaystyle={\bf f}_{k}({\bf T}_{CD})+{\bf f}_{d}(\hat{\bf v}_{CD})
\displaystyle\boldsymbol{\tau}_{D} \displaystyle=\boldsymbol{\tau}_{k}({\bf T}_{CD})+\boldsymbol{\tau}_{d}(\hat{%
\bf v}_{CD}). (3.32)

For example, using {\bf T}_{CD} is consistent with the usual coordinate frame conventions for joints, and so may be preferable when a spring is being used as a “soft” replacement for a joint. To use {\bf T}_{CD} instead of {\bf T}_{DC}, the application should set the spring’s useTransformDC property to false.

3.6.2 Frame materials

The restoring forces (3.30) 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

\displaystyle{\bf f}_{C} \displaystyle={\bf K}_{t}\,{\bf x}_{DC}+{\bf D}_{t}\,{\bf v}_{DC}
\displaystyle\boldsymbol{\tau}_{C} \displaystyle={\bf K}_{r}\,\hat{\boldsymbol{\theta}}_{DC}+{\bf D}_{r}\,%
\boldsymbol{\omega}_{DC}

where \hat{\boldsymbol{\theta}}_{DC} gives the small angle approximation of the rotational components of {\bf T}_{DC} with respect to the x, y, and z axes, and

\displaystyle{\bf K}_{t}\equiv\left(\begin{matrix}k_{tx}&0&0\\
0&k_{ty}&0\\
0&0&k_{tz}\end{matrix}\right),\;{\bf D}_{t}\equiv\left(\begin{matrix}d_{tx}&0&%
0\\
0&d_{ty}&0\\
0&0&d_{tz}\end{matrix}\right),
\displaystyle{\bf K}_{r}\equiv\left(\begin{matrix}k_{rx}&0&0\\
0&k_{ry}&0\\
0&0&k_{rz}\end{matrix}\right),\;{\bf D}_{r}\equiv\left(\begin{matrix}d_{rx}&0&%
0\\
0&d_{ry}&0\\
0&0&d_{rz}\end{matrix}\right).

are the stiffness and damping matrices. The diagonal values defining each matrix are stored in the 3-dimensional vectors {\bf k}_{t}, {\bf k}_{r}, {\bf d}_{t}, and {\bf d}_{r} 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.

3.6.2.1 PowerFrameMaterial

An useful alternative to LinearFrameMaterial is PowerFrameMaterial, which computes restoring forces along each of the translational and rotational directions according to a power law of the form

f=k\,\text{sgn}(x)|x|^{n} (3.33)

where k is a stiffness, x is displacement, and n is an exponent in the range n\in\{1,2,3\}. It is also possible to specify both upper and lower deadbands, b_{u} and b_{l}, such that if x_{t} is the true displacement, x is computed according to

x=\begin{cases}x_{t}-b_{l}&x_{t}<b_{l},\\
x_{t}-b_{u}&x_{t}>b_{u},\\
0&\text{otherwise}\end{cases}. (3.34)

PowerFrameMaterial effects the same damping behavior as LinearFrameMaterial.

If rotational deadbands are specified, the rotational displacement may be large enough that the the small angle approximation used for LinearFrameMaterial no longer applies. Therefore, rotational displacements are computed as the three x-y-z Tait-Bryan angles of {\bf R}_{DC}. The use of these finite displacement angles introduces a coupling such that if \boldsymbol{\tau} is the nominal moment produced by applying (3.33) and (3.34) to each of the three rotational angles, the actual resulting moment \boldsymbol{\tau}_{C} is

\boldsymbol{\tau}_{C}=\left(\begin{matrix}1&0&0\\
\frac{s_{y}s_{x}}{c_{y}}&c_{x}&-\frac{s_{x}}{c_{y}}\\
-\frac{s_{y}c_{x}}{c_{y}}&s_{x}&\frac{c_{x}}{c_{y}}\end{matrix}\right)\,%
\boldsymbol{\tau}, (3.35)

where s_{x}, c_{x}, s_{y}, c_{y} are the sines and cosines of the x and y rotations. The coupling is associated with the non-commutativity of rotations and the fact that the x-y-z angles have a singularity when the y rotation approaches \pm\pi/2. This appears as the division by c_{y} in (3.35), and so care should be taken to avoid such orientations.

The parameters controlling the behavior of PowerFrameMaterial can be set independently for each of the six translational and rotational displacements, using the following properties, each of which is described by a three-vector:

Property Description
{\bf k}_{t} stiffness translational stiffness
{\bf n}_{t} exponents translational exponents
{\bf b}_{ut} upperDeadband upper deadband for translations
{\bf b}_{lt} lowerDeadband lower deadband for translations
{\bf d}_{t} damping translational damping
{\bf k}_{r} rotaryStiffness rotational stiffness
{\bf n}_{r} rotaryExponents rotational exponents
{\bf b}_{ur} upperRotaryDeadband upper deadband for rotations
{\bf b}_{lr} lowerRotaryDeadband lower deadband for rotations
{\bf d}_{r} rotaryDamping rotational damping

Each of these has a default value of \mathbf{0}, except for the exponents, which have a default value of (\mathbf{1}.

Figure 3.30: Example force-displacement curves produced by PowerFrameMaterial. Left: linear behavior with k=20, n=1, b_{l}=-0.5, and b_{u}=0.25; middle: quadratic behavior with k=20, n=2, and b_{l}=b_{u}=0; right: cubic behavior with k=80, n=3, b_{l}=0, and b_{u}=0.5.

If used with exponents of 1 and no deadbands, PowerFrameMaterial behaves like LinearFrameMaterial with finite rotation displacements. Otherwise, Figure 3.30 illustrates some of the force-displacement curves that can be produced. Using a power n>1 (rightmost panels) has the advantage of producing a curve that is differentiable. However, the forces grow more slowly for smaller displacements; a cubic force (right panel) almost behaves as if it has a small deadband, even with b_{l}=b_{u}=0.

An important application of PowerFrameMaterial is creating one-sided force behaviors by setting b_{l} or b_{u} to sufficiently low or high values.

3.6.3 Creating frame springs

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 {\bf T}_{CA} and {\bf T}_{DB}.

A typical construction sequence might look like this:

  FrameSpring spring = new FrameSpring ("springA");
  spring.setMaterial (new LinearFrameMaterial (kt, kr, dt, dr));
  spring.setFrames (bodyA, bodyB, TDW);

The material is set using setMaterial(). The example above uses a LinearFrameMaterial, created with a constructor that sets {\bf k}_{t}, {\bf k}_{r}, {\bf d}_{t}, and {\bf d}_{r} 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.4.3), with the methods
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 {\bf T}_{CA} and {\bf T}_{DB} assuming that there is no initial spring displacement (i.e., that {\bf T}_{DC}={\bf I}), while the latter allows {\bf T}_{CA} and {\bf T}_{DB} to be specified explicitly with {\bf T}_{DC} assuming whatever value is implied.

Frame springs and joints are often placed together, using the same transforms {\bf T}_{CA} and {\bf T}_{DB}, 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.

3.6.4 Example: two bodies connected by a frame spring

Figure 3.31: LumbarFrameSpring model loaded into ArtiSynth.

A simple model showing two simplified lumbar vertebrae, modeled as rigid bodies and connected by a frame spring, is defined in

  artisynth.demos.tutorial.LumbarFrameSpring

The definition for the entire model class is shown here:

1 package artisynth.demos.tutorial;
2
3 import java.io.IOException;
4 import java.io.File;
5 import java.awt.Color;
6 import artisynth.core.modelbase.*;
7 import artisynth.core.mechmodels.*;
8 import artisynth.core.materials.*;
9 import artisynth.core.workspace.RootModel;
10 import maspack.matrix.*;
11 import maspack.geometry.*;
12 import maspack.render.*;
13 import maspack.util.PathFinder;
14
15 /**
16  * Demo of two rigid bodies connected by a 6 DOF frame spring
17  */
18 public class LumbarFrameSpring extends RootModel {
19
20    double density = 1500;
21
22    // path from which meshes will be read
23    private String geometryDir = PathFinder.getSourceRelativePath (
24       LumbarFrameSpring.class, "../mech/geometry/");
25
26    // create and add a rigid body from a mesh
27    public RigidBody addBone (MechModel mech, String name) throws IOException {
28       PolygonalMesh mesh = new PolygonalMesh (new File (geometryDir+name+".obj"));
29       RigidBody rb = RigidBody.createFromMesh (name, mesh, density, /*scale=*/1);
30       mech.addRigidBody (rb);
31       return rb;
32    }
33
34    public void build (String[] args) throws IOException {
35
36       // create mech model and set it’s properties
37       MechModel mech = new MechModel ("mech");
38       mech.setGravity (0, 0, -1.0);
39       mech.setFrameDamping (0.10);
40       mech.setRotaryDamping (0.001);
41       addModel (mech);
42
43       // create two rigid bodies and second one to be fixed
44       RigidBody lumbar1 = addBone (mech, "lumbar1");
45       RigidBody lumbar2 = addBone (mech, "lumbar2");
46       lumbar1.setPose (new RigidTransform3d (-0.016, 0.039, 0));
47       lumbar2.setDynamic (false);
48
49       // flip entire mech model around
50       mech.transformGeometry (
51          new RigidTransform3d (0, 0, 0, 0, 0, Math.toRadians (90)));
52
53       //create and add the frame spring
54       FrameSpring spring = new FrameSpring (null);
55       spring.setMaterial (
56          new LinearFrameMaterial (
57             /*ktrans=*/100, /*krot=*/0.01, /*dtrans=*/0, /*drot=*/0));
58       spring.setFrames (lumbar1, lumbar2, lumbar1.getPose());
59       mech.addFrameSpring (spring);
60
61       // set render properties for components
62       RenderProps.setLineColor (spring, Color.RED);
63       RenderProps.setLineWidth (spring, 3);
64       spring.setAxisLength (0.02);
65       RenderProps.setFaceColor (mech, new Color (238, 232, 170)); // bone color
66    }
67 }

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 (-0.016,0.039,0)^{T}, and lumbar2 is set to be fixed by making it non-dynamic (line 47).

Figure 3.32: LumbarFrameSpring model as it would appear if not rotated about the x axis.

At this point in the construction, if the model were to be loaded, it would appear as in Figure 3.32. To change the viewpoint to that seen in Figure 3.31, we rotate the entire model about the x 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.7.

The frame spring is created and added at lines 54-59, using the methods described in Section 3.6.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.31. 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).