12 Importing OpenSim Models

12.4 Inspecting and tuning the model

12.4.1 Visualization

Figure 12.4: OpenSimArm26 with body coordinate frames visible.

ArtiSynth makes an attempt to preserve the visualization characteristics of the original OpenSim model, but this is not guaranteed. The rendering and appearance of all components can be customized, either interactively (as described under “Editing Properties” in the ArtiSynth User Interface Guide), or in code, as discussed in Section 4.3

The “frame geometry” associated with OpenSim bodies is not explicitly supported, because ArtiSynth rigid bodies and frames already have a visualization mechanism for their coordinate axes through their axisLength and axisDrawStyle properties. The same is true for wrap objects, which are realized as rigid bodies connected to their parent body via a frame attachment.

OpenSimParser does provide the following convenience methods to control the visibility and coordinate frame rendering of bodies and wrap objects:

void setBodyFramesVisible (AxisDrawStyle style, double axisLen)

Set axis rendering for bodies.

void setWrapObjectFramesVisible (AxisDrawStyle style, double axisLen)

Set axis rendering for wrap objects.

void setWrapObjectsVisible (boolean visible)

Set overall visibility of wrap objects.

A method is supplied to control the collective visibility of wrap objects since they are distributed among the bodies. (The collective visibility of bodies, joints, forces, etc. can be controlled by setting the visibility of the containers "bodyset", "jointset", "forceset", etc.)

Placing the following code at the end of the OpenSimArm26 example causes the model to appear as in Figure 12.4:

import maspack.render.Renderer.AxisDrawStyle;
...
   myParser.setWrapObjectsVisible(false);
   myParser.setBodyFramesVisible (AxisDrawStyle.ARROW, 0.12);

For observing the behavior of joints, it is often useful to display the axes of their C and D coordinates frames. As described in Section (3.4.10), this can be controlled by the joint properties drawFrameC, drawFrameD, and axisLength, which can be set either in code or interactively (by selecting the joint and choosing Edit properties ... from the context menu).

Finally, the coordinate system for OpenSim models is usually arranged with the y axis vertical, whereas ArtiSynth defaults to the z axis vertical. While OpenSimParser will set gravity to whatever is specified in the OpenSim file, the viewer orientation must be set by the application, using the root model method setDefaultViewOrientation():

import maspack.matrix.AxisAlignedRotation
...
   setDefaultViewOrientation (AxisAlignedRotation.X_Y);

12.4.2 Interactive contol panels

As discussed in Section 5.1, applications can create control panels to interactively adjust the values of various component properties. (Properties panels for one or more components can also be created on demand by selecting the components then choosing Edit properties ... from the context menu.)

Also, as shown in the example OpenSimArm26, OpenSimParser provides convenience methods to create two types of control panel:

  • CoordinatePanels (Section 5.2.3) to adjust joint coordinate values.

  • Control panels to adjust muscle excitations.

The methods are:

CoordinatePanel createCoordinatePanel()

Create panel for all coordinates.

CoordinatePanel createCoordinatePanel (Collection<JointBase> joints)

Create panel for specified joints.

CoordinatePanel createCoordinatePanel (String[] coordNames)

Create panel for named coordinates.

ControlPanel createExcitationPanel ()

Create panel for all muscles.

ControlPanel createExcitationPanel (String[] muscleNames)

Create panel for named muscles.

At present, CoordinatePanels do not support the adjustment of other coordinate properties, such as locking or ranges. However, these can set in code (Section 3.4.5), and some can also be set interactively by selecting the joint and opening its property panel.

12.4.3 Model tuning

We recommend testing the model by running it under forward simulation to ensure that it behaves as expected. Some parameters, particularly those relative to damping, may need to be adjusted. There are several steps to this process.

  • Check joint function and damping. Remove all force effectors and let the model fall under gravity. If the body is not connected to ground, select an appropriate body and set its dynamic property to false. Force effectors can be removed by selecting the "forceset" and choosing Delete in the context menu; this can also be done in code, as described in Section 12.5.2.

    With all force effectors removed, any model instability almost certainly reflects a problem involving either bodies with zero inertia, or bodies that are overconstrained. Both situations will result in a “perturbed pivots” message like the following appearing on the console output:

    Pardiso: num perturbed pivots=3
    

    If this occurs, try to isolate the problem by selectively removing joints and/or bodies. This can be done interactively by selecting them in the viewer or navigation panel and choosing Delete from the context menu. Components can also be removed in code, as per Section 12.5.2. Issues sometimes arise over the “ground” body, which OpenSim uses to represent ground (ArtiSynth does not require a ground body). In OpenSim 3 models, where ground is not an explicit component, one should ensure that its dynamic property is set to false.

    Stability issues may arise for bodies which are very small or can rotate quite quickly. This can sometimes be addressed by specifying more damping (and sometimes rotaryDamping specifically) for these bodies. Alternatively, one may wish to reduce the simulation step size, from the default of 0.01 sec down to values around 0.001 or possibly lower. This can be interactively adjusted in the GUI, and once a suitable value is found, set in code using the root model’s setMaxStepSizeMethod():

       setMaxStepSize (0.001);

    In addition to gravity, you can also use the pull controller (see “Pull manipulation” in the ArtiSynth User Interface Guide) to apply point forces to the various bodies. The main point of the exercise is to see how the model behaves under gravity-like loads, and adjust the damping parameters (Section 3.1.5) so that bodies come to rest after an appropriate time. Critical, or slightly sub-critical, damping can usually be achieved by setting the inertial damping for the MechModel to values in the range of 1-5, but sometimes one may want to adjust damping for specific bodies, or also specify rotary or frame damping.

    Damping in an ArtiSynth model is typically applied directly to its particles and rigid bodies, and reflects the fact that body poses and velocities, instead of joint coordinates and speeds, are the primary means of specifying the model’s dynamic state. OpenSim models often specify damping using CoordinateLimitForces; one may wish to remove or limit the use of those components in ArtiSynth. Likewise, one may wish to reduce the use of damping in the muscle forces.

  • Check the muscle wrapping. Using a coordinate panel (Section 12.4.2), adjust the coordinates and check that the muscle wrapping appears correct. Issues that can sometimes arise include the path failing to pass through small torus wrap objects, or not wrapping smoothly around cylinders with radii due to insufficient knots in the wrap path.

    Wrap path issues can usually be fixed by resetting the initialization points and/or number of knots for the problematic wrap segments, using the methods set/getNumKnots() and set/getInitializingPoints() described in Section 9.2. For example, the following code might fix wrapping for a certain segment:

      MultiPointSpring spr = (MultiPointSpring)myParser.findMuscle ("BIClong");
      int segIdx = 1;   // index of problematic segment
      Point3d xpnt;     // extra point to help initialize the wrap path
      ...
      spr.setNumKnots (segIdx, 30); // increase number of knots to 40
      spr.setInitializingPoints (   // reset initializing points
         segIdx, new Point3d[] { xpnt } );
      spr.updateWrapSegments();

    The code makes use of the parser method findMuscle(), described in Section 12.5.1.

  • Check quiescent and excited model behavior. Start by checking the behavior of the model with no loads or muscle excitations. The purpose of this exercise is to make sure that the model is stable in a “rest” state. Instability here will usually be the result of force effectors that are generating forces that are too large for some reason. As when inspecting the joint-body behavior, one should attempt to isolate the problem by selectively removing components.

    OpenSim muscles are sometimes configured with small non-zero initial excitations, in order to facilitate the solution of muscle-tendon equilibrium. ArtiSynth does not require this for its equilibrium muscle implementation, and so applications are free to set muscle excitations to zero. OpenSimParser supplies the convenience method zeroMuscleExcitations() for doing this.

    When forces are highly non-linear, stable model behavior may require reducing the step size, as already described for body-joint calibration. It may also be necessary to change the MechModel’s stabilization property from GlobalMass to GlobalStiffness:

    import artisynth.core.mechmodels.MechSystemSolver.PosStabilization;
    ...
       myMech.setStabilization (PosStabilization.GlobalStiffness);

    (GlobalStiffness is not the default setting because it increases computation time).

    A problem we have seen several times involves improper parameter settings for OpenSim’s CoordinateLimitForce component, which is used to implement both soft coordinate limits and coordinate damping. Rotational units for this component are given in degrees (vs. radians for most other OpenSim components). Modelers have sometimes not noticed this, leading to range limits that are either far too small, or stiffness and damping values that are far too high.

    After checking the quiescent behavior, use a muscle excitation panel to check that the model reacts as expected (and remains stable) when different muscles are excited. In some cases, it may be desirable to adjust the muscle parameters. For example, the rest lengths for a muscle implemented using Millard2012AxialMuscle could be adjusted as follows:

    import artisynth.core.materials.Millard2012AxialMuscle;
      ...
      double optl, tslack; // new opt fibre length and tendon slack length values
      ...
      // find problematic muscle
      MultiPointSpring spr = (MultiPointSpring)myParser.findMuscle ("BIClong");
      Millard2012AxialMuscle mmat = (Millard2012AxialMuscle)spr.getMaterial();
      // adjust the material
      mmat.setOptFibreLength (optl);
      mmat.setTendonSlackLength (tslack);

    Another useful debugging technique can be to temporarily replace the muscle material for problematic muscles with an instance of SimpleAxialMuscle (Section 4.5.1.1):

      // replace material for problematic muscle
      MultiPointSpring spr = (MultiPointSpring)myParser.findMuscle ("BIClong");
      spr.setMaterial (new SimpleAxialMuscle (
         /*stiffness*/0, /*damping*/0, /*maxForce*/100));

    If set with a given maxForce and 0 stiffness and damping, a SimpleAxialMuscle material configures a muscle so that its tension is simply equal to its excitation times maxForce.