10 Inverse Simulation

10.2 Tracking controller components

This section describes in greater detail how exciters, motion and force sources, and other cost terms can be specified for the controller.

10.2.1 Exciters

An exciter can be any model component that implements ExcitationComponent. These include point-to-point Muscles (Section 4.5), muscle bundles in FEM muscle models (Section 6.9.1.1), and MuscleExciters (Section 6.9.1.2). Each exciter exports an excitation property which accepts a scalar input signal (usually in the range [0,1]) that drives the exciter’s active force (or fans it out to other exciters in the case of a MuscleExciter).

The set of excitation components used by a tracking controller is managed by the following methods:

void addExciter(ExcitationComponent ex)

Adds an exciter with default weight 1.0.

void addExciter(double w, ExcitationComponent ex)

Adds an exciter with weight w.

void addExciters(Collection<ExcitationComponent> exciter)

Adds a collection of exciters with weights 1.0.

int numExciters()

Returns the number of exciters.

ExcitationComponent getExciter (int idx)

Returns the idx-th exciter.

ListView<ExcitationComponent> getExciters()

Returns a list of all exciters.

void clearExciters()

Remove all exciters.

An exciter’s weight forms its corresponding entry in the diagonal matrix {\bf W}_{a} used by the quadratic program ((10.10 and (10.12)), with a smaller weight allowing the exciter to assume greater prominence in the solution.

By default, the computed excitations are bounded by the interval [0,1], as indicated in Section 10.1.3. However, these bounds can be altered, either collectively using the tracking controller property excitationBounds, or individually for specific exciters:

void setExcitationBounds(DoubleInterval range)

Sets the excitationBounds property.

DoubleInterval getExcitationBounds()

Queries the excitationBounds property.

void setExcitationBounds(ExcitationComponent ex, Mdouble low, double high)

Sets excitation bounds for exciter ex.

DoubleInterval getExcitationBounds(ExcitationComponent ex)

Queries excitation bounds for exciter ex.

The excitation values themselves can also be queried and set with the following methods:

void getExcitations(VectorNd values)

Returns excitations in values.

double getExcitation(int idx)

Returns the excitation of the idx-th exciter.

By default, the controller initializes excitations to zero and then updates them at the beginning of each time step. However, when excitations are being computed incrementally (Section 10.1.5), it may be desirable to start with non-zero excitation values. In that case, the following methods may be useful:

void initializeExcitations()

Sets excitations to the current exciter values.

void setExcitations(VectorNd values)

Sets excitations to values.

The first sets the controller’s internal excitation values to those stored in the exciters, while the second sets both the internal values and the values in the exciters.

10.2.1.1 Excitation coloring

Some exciters (such as Muscle and MultiPointMuscle) support the ability to change color in proportion to their excitation value. This makes it easier to visualize the extent to which exciters are being activated within the model. Exciters which support this capability export the properties excitationColor, which is the color the exciter should transition to as excitation is increased, and maxColoredExcitation, which the excitation value at which the color transition is complete. In other words, the exciter’s color will vary from its default color to excitationColor as its excitation varies from 0 to maxColoredExcitation.

Excitation coloring does not occur if the exciter’s excitationColor is set to null.

The tracking controller exports a property, configExcitationColoring, which if true enables the automatic configuration of excitation coloring for exciters that support this capability. If enabled, then as these exciters are added to the controller, their excitationColor is inspected. If it has not been set (i.e., if it is null), then it is set to red and the nominal color for the exciter is set to white, enabling a white-to-red color transition for the exciter as it is activated.

10.2.2 Motion targets

As indicated above, motion source components can be specified to the controller using the addPointTarget() and addFrameTarget() methods. Source components may be instances of Point or Frame, and thereby may include both FEM nodes and rigid bodies. The methods for managing motion targets include:

TargetPoint addPointTarget(Point source)

Adds point source with weight 1.0.

TargetPoint addPointTarget(Point source, double w)

Adds point source with weight w.

TargetFrame addFrameTarget(Frame source)

Adds frame source with weight 1.0.

TargetFrame addFrameTarget(Frame source, double w)

Adds frame source with weight w.

int numMotionTargets()

Number of motion targets.

void removeMotionTarget(MotionTargetComponent source)

Removes motion source.

The addPointTarget() and addFrameTarget() methods each create and return a target component, allocated and contained within the controller, that mirrors the type of source component. These target components are either TargetPoint for point sources or TargetFrame for frame sources. Both TargetPoint and TargetFrame, together with the source components Point and Frame, are instances of MotionTargetComponent.

As simulation proceeds, the desired trajectory position {\bf x}_{t} for each source component is specified by setting the target component’s position property (or position, orientation and/or pose properties for frame targets). As described elsewhere, this can be done using either probes or other controller objects.

Likewise, a corresponding velocity trajectory {\bf v}_{t} can also be specified by setting the velocity property of the target object. If this is not set, {\bf v}_{t} defaults to 0, which will introduce a small lag into the tracking behavior. If {\bf v}_{t} is set, care should be taken the ensure that it is consistent with the actual time derivative of {\bf x}_{t}.

Applications typically do not need to specify {\bf v}_{t}. However, doing so may improve tracking performance, particularly when using PD control (Section 10.1.2).

Each target component also implements the interface TrackingTarget, described in Section 10.2.8, which supplies methods to specify the weights in the weighting matrix {\bf W}_{m} of the motion tracking cost term \phi_{m}({\bf a}) (10.9).

Lists of all the motion source components and their associated target components (i.e., those returned by the addXXXTarget() methods) can be obtained using

ArrayList<MotionTargetComponent> getMotionSources()

Returns motion source components.

ArrayList<MotionTargetComponent> getMotionTargets()

Returns motion target components.

10.2.2.1 Motion target term

The cost term \phi_{m}({\bf a}) that is responsible for tracking motion targets is contained within a tracking controller subcomponent that is named "motionTerm" and which is an instance of MotionTargetTerm. Users may access this component directly using

MotionTargetTerm getMotionTargetTerm()

Returns the controller’s motion target term.

and then use it to set motion tracking properties such as those described in Section 10.3.3.

The motion tracking weight w_{m} in (10.12) is given by the weight property of the motion target term, which can also be accessed directly by the controller methods

double getMotionTargetTermWeight()

Returns the motion target term weight.

void setMotionTargetTermWeight(double w)

Sets the motion target term weight.

10.2.2.2 Motion target rendering

The motion target components, which are allocated and maintained by the controller, are rendered by default, which makes it easy to visualize significant errors between the target positions and the tracked source positions. Target points are drawn as cyan colored spheres. For target frames, if the source component corresponds to a RigidBody, the target is rendered using a cyan colored wireframe copy of the source’s surface mesh.

Rendering of targets can be enabled or disabled by setting the tracking controller’s targetsVisible property. Otherwise, the application is free to set the render properties of individual target components, or their containing lists within the MotionTargetTerm, which can be accessed by the methods

PointList<TargetPoint> getTargetPoints()

Return all point motion target components.

RenderableComponentList<TargetFrame> getTargetFrames()

Return all frame motion target components.

Other methods of MotionTargetTerm allow the render properties for both the point and frame target lists to be set together:

RenderProps getTargetRenderProps()

Return render properties for the motion target lists.

void setTargetRenderProps (RenderProps props)

Set render properties for the motion target lists.

void setTargetsPointRadius (double rad)

Set pointRadius target render property to rad.

10.2.3 Regularization

10.2.3.1 L2 Regularization

L2 regularization attempts to minimize the weighted square of the excitation values, as described by

\frac{w_{a}}{2}\,{\bf a}^{T}{\bf W}_{a}\,{\bf a} (10.14)

in (10.12), and so provides a way to resolve redundancy when the number of exciters is greater than needed to perform the required tracking. It can be enabled or disabled by adding or removing an L2 regularization term from the controller, as managed by the following methods:

void setL2Regularization()

Enables L2 regularization with default weight 0.0001.

void setL2Regularization(double w)

Enables L2 regularization with weight w.

double getL2RegularizationWeight()

Returns regularization weight, or 0 if not enabled.

boolean removeL2Regularization()

Removes L2 regularization.

The weight associated with the above methods corresponds to w_{a} in (10.14) and (10.12). The entries in the diagonal matrix {\bf W}_{a} are given by the weights associated with the exciters themselves, as described in Section 10.2.1.

L2 regularization is implemented using a controller subcomponent of type L2RegularizationTerm, which is added or removed from the controller as required and can be accessed using

L2RegularizationTerm getL2RegularizationTerm()

Returns the controller’s L2 regularization term

which will return null if regularization is not being applied.

Because the L2 regularizer tries to reduce the excitations {\bf a}, its use will reduce the controller’s tracking accuracy. Therefore, the regularizer is often employed with a weight value w_{a} well below 1.0, with values around 0.1 or 0.01 being common. The best weight choice will of coarse depend on the application.

10.2.3.2 Excitation damping

The controller also provides a damping term that serves to minimize the time derivative of {\bf a}, or more precisely,

\frac{1}{2}\dot{\bf a}^{T}{\bf W}_{a}\dot{\bf a},

where {\bf W}_{a} is the diagonal excitation weight matrix used for L2 regularization (10.14). Letting {\bf a}_{0} denote the excitations at the beginning of the time step, and approximating \dot{\bf a} by

\dot{\bf a}\approx\frac{{\bf a}-{\bf a}_{0}}{h},

where h is the time step size, the damping cost term \phi_{d}({\bf a}) becomes

\phi_{d}({\bf a})=\frac{1}{2h^{2}}\left({\bf a}^{T}{\bf W}_{a}{\bf a}-2{\bf a}%
_{0}^{T}{\bf W}_{a}{\bf a}\right).

Excitation damping can be managed by the following methods:

void setExcitationDamping()

Enable excitation damping with default weight 10^{-5}.

void setExcitationDamping(double w)

Enable excitation damping with weight w.

double getExcitationDampingWeight()

Returns damping weight, or 0 if not enabled.

void removeExcitationDamping()

Removes excitation damping.

Excitation damping is implemented using a controller subcomponent of type DampingTerm, which is added or removed from the controller as required and can be accessed using

DampingTerm getDampingTerm()

Returns the controller’s excitation damping.

which will return null if damping is not being applied.

10.2.4 Example: controlling ToyMuscleArm

Figure 10.5: InverseMuscleArm when first loaded into ArtiSynth.

A good tracking controller example is given by the model artisynth.demos.tutorial.InverseMuscleArm, which computes the muscle excitations needed to make the tip marker of the ToyMuscleArm demo (described in Section 9.5.2) follow a prescribed trajectory. The model extends artisynth.demos.tutorial.ToyMuscleArm, and then adds the tracking controller and probes needed to move the tip marker, as shown in the code below:

1 public class InverseMuscleArm extends ToyMuscleArm {
2
3    public void build (String[] args) throws IOException {
4       super.build(args); // create ToyMuscleArm
5
6       // move the model into a non-singular position so it can track a target
7       // trajectory more easily
8       myHinge0.setTheta (-20);
9       myHinge1.setTheta (38.4);
10       myMech.updateWrapSegments(); // update muscle wrapping for new config
11
12       // Create a tracking controller
13       TrackingController tcon = new TrackingController (myMech, "tcon");
14       addController (tcon);
15       // For each muscle, reinitialize its rest length for the new
16       // configuration and add it to the controller as an exciter
17       for (AxialSpring spr : myMech.axialSprings()) {
18          spr.setRestLength (spr.getLength());
19          tcon.addExciter ((Muscle)spr);
20       }
21       for (MultiPointSpring spr : myMech.multiPointSprings()) {
22          spr.setRestLength (spr.getLength());
23          tcon.addExciter ((MultiPointMuscle)spr);
24       }
25
26       // Add the tip marker to the controller as a motion target
27       TargetPoint target = tcon.addPointTarget (myTipMkr);
28       // add an L-2 regularization term to handle exciter redundancy
29       tcon.setL2Regularization(/*weight=*/0.1);
30
31       double startTime = 0; // probe start times
32       double stopTime = 5;  // probe stop times
33       // Specify a target trajectory for the tip marker using an input probe.
34       NumericInputProbe targetprobe = new NumericInputProbe (
35          target, "position", startTime, stopTime);
36       targetprobe.setName ("target positions");
37       double x0 = 0;      // initial x coordinate of the marker
38       double z0 = 1.1806; // initial z coordinate of the marker
39       double xmax = 0.6;  // max x coordinate of the trajectory
40       // Trajectory data: five cubically interpolated knot points, running for
41       // 5 seconds, giving a closed loop shape:
42       targetprobe.addData (new double[] {
43          x0,0,z0,  xmax,0,z0-0.2,  x0,0,z0-0.4,  -xmax,0,z0-0.2,  x0,0,z0},
44          /*timestep=*/stopTime/4);
45       targetprobe.setInterpolationOrder (Interpolation.Order.Cubic);
46       addInputProbe (targetprobe);
47
48       // add an output probe to record the excitations:
49       NumericOutputProbe exprobe = InverseManager.createOutputProbe (
50          tcon, ProbeID.COMPUTED_EXCITATIONS, /*fileName=*/null,
51          startTime, stopTime, /*interval=*/-1);
52       addOutputProbe (exprobe);
53
54       // add tracing probes to view both the tracking target (in cyan) and the
55       // actual tracked position (in red).
56       TracingProbe tprobe;
57       tprobe = addTracingProbe (target, "position", startTime, stopTime);
58       tprobe.setName ("target tracing");
59       RenderProps.setLineColor (tprobe, Color.CYAN);
60       tprobe = addTracingProbe (myTipMkr, "position", startTime, stopTime);
61       tprobe.setName ("source tracing");
62       RenderProps.setLineColor (tprobe, Color.RED);
63
64       // add inverse control panel
65       InverseManager.addInversePanel (this, tcon);
66       // settings to allow probe management by InvereManager:
67       tcon.setProbeDuration (stopTime); // default probe duration
68       // set working folder for probe files
69       ArtisynthPath.setWorkingFolder (
70          new File (PathFinder.getSourceRelativePath (this, "inverseMuscleArm")));
71    }
72 }

As mentioned above, this model extends ToyMuscleArm (line 1), and so calls super.build(args) in the build() method to create all the components of ToyMuscleArm. Once this is done, the link positions are adjusted by setting the hinge joint angles (lines 8-9); this is done to move the arm away from the kinematic singularity at full extension and thus make it easier for the tip to follow a prescribed path. After the links are moved, the all wrap paths are updated by calling the MechModel method updateWrapPath() (line 10).

A tracking controller is then created (lines 13-14), and every Muscle and MultiPointMuscle is added to it as an exciter (lines 17-24). When iterating through the muscles, their rest lengths are reinitialized to their current lengths (which changed when the links were repositioned) to ensure that passive muscle forces are zero in the initial position.

Next, the marker at the tip of link1 (referenced by the inherited attribute myTipMkr) is added to the controller as a motion source (line 27) and the returned target object is stored in target. An L2 regularization term is added to the controller with weight 0.01 (line 29), and an input probe is created to provide the marker trajectory by setting the target’s position property (lines 31-46). The probe has a duration of 5 seconds, and the trajectory is a closed path, specified by 5 cubically interpolated knot points (line 43), that lie in the x-z plane and start at and return to the marker’s initial position x0, z0.

Probes are created to record the computed excitations and trace the desired and actual trajectories in the viewer. The first, exprobe, is created by the utility method createOutputProbe() supplied by the InverseManager class (lines 49-52, Section 10.4.1). The tracing probes are created by the RootModel method addTracingProbe() (lines 56-62), and two are created: one to trace the desired target by recording the position property of target, and another to trace the actual tracked (source) position by recording the position property of myTipMkr. The line colors of these are set to cyan and red, respectively, which specifies their display color in the viewer.

Lastly, an inverse control panel (Section 10.4.3) is created to manage controller properties (line 65), and some settings are made to allow for probe management by the InverseManager class, as discussed in Section 10.4.1 (lines 67-70); this includes setting the default probe duration to stopTime and the working folder to inverseMuscleArm located under the source folder.

Figure 10.6: InverseMuscleArm run with L2 regularization weights of 0.1 (left) and 10 (right). Traces of the desired and actual trajectories are shown in cyan and red, respectively, and computed excitation values are shown in graphs below. A regularization weight of 10results in a larger tracking error.

To run this example, select All demos > tutorial > InverseMuscleArm from the Models menu. The model should load and initially appear as in Figure 10.5. When run, the controller will compute excitations to make the tip marker trace the desired trajectory, as shown in Figure 10.6 (left), where the tracing probe shows the target trajectory in cyan; the source trajectory appears beneath it in red but is largely invisible because it follows the target quite accurately. Computed excitation values are shown below. The target component created for the tip marker is rendered as a cyan-colored sphere (Section 10.2.2.2).

The muscles in InverseMuscleArm are initially colored white instead of red (as they are for ToyMuscleArm). That is because if a muscle’s excitationColor property is null, the controller automatically configures the muscle to vary in color from white to red as the muscle is activated, as described in Section 10.2.1.1. This behavior can be disabled by setting the controller property configExcitationColoring to false.

To illustrate how the L2 regularization weight can affect the tracking error, 10.6 (right) shows the model run with a regularization weight of 10 instead of 0.1. The tracking error is now visible, with the red source trace probe clearly distinct from the cyan target trace. The computed muscle excitations are also noticeably different.

10.2.5 Example: controlling an FEM muscle model

Figure 10.7: InverseMuscleFem when first loaded into ArtiSynth (left), and after executing in the inverse simulation (right).

Another example involving an FEM muscle model is given by artisynth.demos.tutorial.InverseMuscleFem, which computes the muscle excitations needed to make an attached frame of the ToyMuscleFem demo (described in Section 6.9.4) follow a prescribed trajectory.

The model extends artisynth.demos.tutorial.ToyMuscleFem, and then adds the tracking controller and probes needed to move the tip marker, as shown in the code below:

1 public class InverseMuscleFem extends ToyMuscleFem {
2
3    protected String dataDir = PathFinder.getSourceRelativePath (this, "data/");
4
5    public void build (String[] args) throws IOException {
6       super.build (args);  // build the underlying ToyMuscleFem model
7
8       // create a tracking controller
9       TrackingController tcon = new TrackingController (myMech, "tcon");
10       addController (tcon);
11       // add each FEM muscle bundle to it as an exciter
12       for (MuscleBundle b : myFem.getMuscleBundles()) {
13          tcon.addExciter (b);
14       }
15       // add the frame attached to the FEM as a motion target
16       TargetFrame target = tcon.addFrameTarget (myFrame);
17
18       // add an L-2 regularization term to handle exciter redundancy
19       tcon.setL2Regularization(/*weight=*/0.1);
20       // set the controller motion term to use PD control
21       tcon.getMotionTargetTerm().setUsePDControl (true);
22       tcon.getMotionTargetTerm().setKp (1000);
23       tcon.getMotionTargetTerm().setKd (100);
24
25       // add input probes specifying the desired position and orientation
26       // trajectory of the target:
27       double startTime = 0;
28       double stopTime = 5;
29       NumericInputProbe tprobePos =
30          new NumericInputProbe (
31             target, "position", dataDir+"inverseFemFramePos.txt");
32       tprobePos.setName ("target frame position");
33       addInputProbe (tprobePos);
34       NumericInputProbe tprobeRot =
35          new NumericInputProbe (
36             target, "orientation", dataDir+"inverseFemFrameRot.txt");
37       tprobeRot.setName ("target frame orientation");
38       addInputProbe (tprobeRot);
39
40       // add output probes showing the tracked position and orientation of the
41       // target frame source:
42       NumericOutputProbe sprobePos =
43          new NumericOutputProbe (
44             myFrame,"position", startTime, stopTime, /*interval*/-1);
45       sprobePos.setName ("source frame position");
46       addOutputProbe (sprobePos);
47       NumericOutputProbe sprobeRot =
48          new NumericOutputProbe (
49             myFrame,"orientation", startTime, stopTime, /*interval*/-1);
50       sprobeRot.setName ("source frame orientation");
51       addOutputProbe (sprobeRot);
52
53       // add an output probe to record the excitations:
54       NumericOutputProbe exprobe = InverseManager.createOutputProbe (
55          tcon, ProbeID.COMPUTED_EXCITATIONS, /*fileName=*/null,
56          startTime, stopTime, /*interval=*/-1);
57       addOutputProbe (exprobe);
58       // create a control panel for the controller
59       InverseManager.addInversePanel (this, tcon);
60    }
61 }

Since the model extends ToyMuscleFem (line 1), the build() method begins by calling super.build(args) to create the model defined by the superclass. A tracking controller is then created and added to the root model’s controller set, all the muscle bundles in the FEM muscle model are added to it as exciters, and the attached frame is added to it as a motion source using addFrameTarget (lines 9-16).

An L2 regularization term is added, and then the motion tracking is set to use PD control (Section 10.1.2), using gains of K_{p}=1000 and K_{d}=100 (line 19-23).

To control position and orientation of the frame, two input probes are created (lines 27-38), one attached to the target’s position and the other to its orientation. (While it is possible to use a single probe to control both of these properties, or the single pose property, separate probes are used here to provide easier visualization.) Their data and settings are read from the probe files inverseFemFramePos.txt and inverseFemFrameRot.txt, located in the folder data beneath the application model source folder. Each specifies a probe in the time range from 0 to 5, with 26 knot points and cubic interpolation.

Figure 10.8: Probe data generated by InverseMuscleFem. Top: target trajectory for the frame position (26 knot points, cubically interpolated). Middle: actual position tracked by the controller. Bottom: computed muscle excitations.

To monitor the controller’s tracking performance, two output probes are created and attached to the position and orientation properties of the source component myFrame (lines 58-67). Setting the interval argument to -1 in the probes’ constructors causes them to use the model’s current step size as the update interval. Finally, the utility class InverseManager (Section 10.4) is used to create an output probe for the computed excitations, as well as a control panel giving access to the various controller properties (lines 70-75).

To run this example, select All demos > tutorial > InverseMuscleFem from the Models menu. The model should load and initially appear as in Figure 10.7 (left). When run, the controller will compute excitations to move the attached frame along the specified position/orientation trajectory, reaching the final pose shown in Figure 10.7 (right). The target and actual source trajectories for the frame’s position (i.e., its origin) is shown in Figure 10.8, together with the computed excitations.

10.2.6 Force effector targets

The controller can also be asked to track a force trajectory for a set of force effector components; the excitations will then be computed to try to generate the prescribed forces. Any force component that implements the interface ForceTargetComponent can be used as a force effector source; this interface extends ForceEffector to supply several additional methods including:

interface ForceTargetComponent extends ForceEffector, ModelComponent {
   // gets the dimension of the generated force
   public int getForceSize();
   // returns the current force value
   public void getForce (VectorNd minf, boolean staticOnly);
   ...
}

At the time of this writing, ForceTargetComponents include:

Component Force size Force description
AxialSpring 1 Tension in the spring
Muscle 1 Tension in the muscle
FrameSpring 6 Spring wrench as seen in body A (world coordinates)

Controller methods for adding and managing force effector targets include:

ForceEffectorTarget addForceEffectorTarget ( MForceTargetComponent source)

Adds static-only force source with weight 1.0

ForceEffectorTarget addForceEffectorTarget ( MForceTargetComponent source, double w)

Adds static-only force source with weight w.

ForceEffectorTarget addForceEffectorTarget ( MForceTargetComponent source, double w, boolean staticOnly)

Adds force source with weight w and static-only specified.

int numForceEffectorTargets()

Number of force effector targets.

boolean removeForceEffectorTarget( MForceTargetComponent source)

Removes force source.

The addForceEffectorTarget() methods each create and return a ForceEffectorTarget component. As simulation proceeds, the desired target force for the source component is specified by setting the target component’s targetForce property. As described elsewhere, this can be done using either probes or other controller objects. The target component also implements the interface TrackingTarget, described in Section 10.2.8, which supplies methods to specify the weighting matrix {\bf W}_{e} in the force effector cost term \phi_{e}({\bf a}) (10.11).

By default, force effector tracking is static-only, meaning that only static forces (i.e., forces that are not velocity dependent, such as damping) are considered. However, the third addForceEffectorTarget() method allows this to be explicitly specified.

Lists of all the force effector source components and their associated targets can be obtained using the methods:

ArrayList<ForceEffectorTarget> getForceEffectorSources()

Returns force effector source components.

ArrayList<ForceEffectorTarget> getForceEffectorTargets()

Returns force effector target components.

Specifying a force effector targetForce of 0 will have the same effect as minimizing the force associated with that force effector.

10.2.6.1 Force effector term

The cost term \phi_{e}({\bf a}) that is responsible for tracking force effector targets is contained within a tracking controller subcomponent named "forceEffectorTerm" and which is an instance of ForceEffectorTerm. This is added to the controller automatically whenever force effector tracking is requested, and may accessed directly using

ForceEffectorTerm getForceEffectorTerm()

Returns the force effector term.

The method will return null if the force effector term is not present.

The force effector tracking weight w_{e} in (10.12) is given by the weight property of the force effector term, which can also be accessed directly by the controller methods

double getForceEffectorTermWeight()

Returns the force effector term weight.

void setForceEffectorTermWeight(double w)

Sets the force effector term weight.

10.2.7 Example: controlling tension in a spring

Figure 10.9: InverseSpringForce when first loaded (left), and during simulation with the lower muscles fully activated to control the tension in the upper passive spring (cyan).

A simple example of force effector tracking is given by

  artisynth.demos.tutorial.InverseSpringForce

which uses two point-to-point muscles to control the tension in a passive spring. The initial part of the code is the same as that for InverseParticle (Section 10.1.7), except for different parameter definitions,

   int numMuscles = 3; // num radial muscles surrounding the dynamic particle
   double muscleStiffness = 200; // passive muscle stiffness
   double muscleDamping = 0.1; // passive muscle damping
   double muscleFmax = 200; // max active force at excitation = 1
   double dist = 1.0; // distance of anchor point from world origin

which give the muscles different strengths and cause only 3 to be created instead of 16, and the fact that the "center" particle is initially placed at (0,0,0.33) instead of the origin. The rest of the code diverges after the tracking controller is created, as shown below:

17       // create the tracking controller and add it to the root model
18       TrackingController tcon = new TrackingController(mech, "tcon");
19       addController(tcon);
20       // set all muscles but the first to be "exciters" for the controller
21       for (int i=1; i<numMuscles; i++) {
22          tcon.addExciter((Muscle)mech.axialSprings().get(i));
23       }
24       // set the first muscle to be the force effector target. This
25       // will be unactivated and will simple serve as a passive spring
26       AxialSpring passiveSpring = mech.axialSprings().get(0);
27       ForceEffectorTarget target =
28          tcon.addForceEffectorTarget(passiveSpring);
29       // add an L-2 regularization term, since there are more muscles than
30       // target degrees-of-freedom
31       tcon.setL2Regularization(/*weight=*/0.1);
32
33       // Render properties: make points gray spheres, central particle white,
34       // muscles as blue spindles, and passive spring as a cyan spindle.
35       RenderProps.setSphericalPoints (this, dist/25, Color.LIGHT_GRAY);
36       RenderProps.setPointColor (part, Color.WHITE);
37       RenderProps.setSpindleLines (mech, dist/25, Color.BLUE.darker ());
38       RenderProps.setLineColor (passiveSpring, Color.CYAN);
39
40       // add an input probe to control the desired target tension:
41       NumericInputProbe targetprobe = new NumericInputProbe (
42          target, "targetForce", /*startTime=*/0, /*stopTime=*/1);
43       targetprobe.setName ("target tension");
44       targetprobe.addData (
45          new double[] {0d, 120d, 0d}, // three knot points
46          /*timestep=*/0.5);
47       targetprobe.setInterpolationOrder (Interpolation.Order.Cubic);
48       addInputProbe (targetprobe);
49
50       // add an output probe to show both the target tension ("targetForce"
51       // of target) and actual tension ("forceNorm" of passiveSpring)
52       Property[] props = new Property[] {
53          target.getProperty ("targetForce"),
54          passiveSpring.getProperty ("forceNorm"),
55       };
56       NumericOutputProbe trackingProbe =
57          new NumericOutputProbe (props, /*interval=*/-1);
58       trackingProbe.setName ("target and source tension");
59       trackingProbe.setStartTime (0);
60       trackingProbe.setStopTime (1);
61       addOutputProbe (trackingProbe);
62
63       // add an output probe to record the excitations:
64       NumericOutputProbe exprobe = InverseManager.createOutputProbe (
65          tcon, ProbeID.COMPUTED_EXCITATIONS, /*fileName*/null,
66          /*startTime*/0, /*stopTime*/1, /*interval*/-1);
67       addOutputProbe (exprobe);

After the tracking controller is created (lines 18-19), the last two muscles are added to it as exciters (lines 21-23). The first muscle is then added as the force effector sources using addForceEffectorTarget() (lines 26-28), which returns a target object. (The first muscle will be unactuated and so will behave as a passive spring.) Since the number of exciters exceeds the degrees-of-freedom of the target space, an L2-regularization term is added (line 31).

Rendering properties are set at lines 35-38: points are rendered as gray spheres, except for the center particle which is white, and muscles are drawn as spindles, with the exciter muscles blue and the passive target cyan.

Lastly, probes are created: an input probe to specify the target trajectory, an output probe to record target and tracked tensions, and an output probe to record the computed excitations. The first, targetprobe, is attached to the targetForce property of the target component and runs from time 0 to 1 (lines 41-48). Its data is specified in code using addData(), which specifies three knot points with a time step of 0.5. Interpolation set to cubic (line 47) for greater smoothness. The second probe, trackingProbe, records both the target and source tension from the targetForce property of target and the forceNorm property of the passive spring (lines 52-61). The excitation probe is created by the utility method createOutputProbe() supplied by the InverseManager class (lines 64-67, Section 10.4).

Figure 10.10: Probes showing the combined target and source tension (top) and the computed excitations (bottom) for InverseSpringForce.

To run this example, select All demos > tutorial > InverseSpringForce from the Models menu. The model should load and initially appear as in Figure 10.9 (left). When run, the controller computes excitations to generate the requested tension in the passive spring; this causes the center particle to be pulled down (Figure 10.3, right). Both the target-source and computed excitation probes are shown in Figure 10.10.

For the middle third of the trajectory, the excitation values have reached their threshold value of 1 and so are unable to deliver more force. This in turn causes the source tension (green line) to deviate from the target tension (red line) in the target-source probe.

10.2.8 Target components

As described above, when a motion or force source is added to the controller (using methods such as addPointTarget() or addForceEffectorTarget()), the controller creates and returns a target component that is used for specifying the desired position or force trajectory. Each target term also contains a scalar weight and a vector of subweights, described by the properties weight and subWeights, all of which have default values of 1.0. These weights are used to manage the priority of the target within the tracking computation; the subWeights vector has a size equal to the number of degrees of freedom (DOF) in the target (3 for points and 6 for frames), and permits fine-grained weighting for each of the targets DOFs. The product of the weight with each subweight produces the entries for the diagonal weighting matrix that appears in the various cost terms for motion and force tracking (e.g., {\bf W}_{m} for the motion tracking cost function \phi_{m}({\bf a}) in (10.9) and {\bf W}_{e} for the force effector cost function \phi_{e}({\bf a}) in (10.11)). Targets (or specific DOFs) with higher weights will be tracked more accurately, while weights of 0 effectively disable tracking.

Target components vary depending on the source component whose position or force is being tracking, but each implements the interface TrackingTarget, which supplies the following methods for controlling the weights and subweights and querying the target’s source component:

ModelComponent getSourceComp()

Returns the target’s source component.

int getTargetSize()

Returns number of target DOFs, which equals the number of subweights.

double getWeight()

Returns the target component weight property.

void setWeight(double w)

Sets the target component weight property.

Vector getSubWeights()

Returns the target component subweights property.

void setSubWeights(VectorNd subw)

Sets the target component subweights property.

10.2.9 Point and frame exciters

In addition to muscle components, exciters may also include PointExciters and FrameExciters, which can be used to apply forces directly to Point or Frame components. This effectively gives the inverse controller the ability to do direct inverse simulation. These exciter components can also assume a role similar to that of the “reserve actuators” used in OpenSim [7], augmenting a model’s tracking capabilities to account for force sources that are not explicitly supplied by the model.

Each exciter applies a force along (or about) a single degree-of-freedom, as specified by the enumerated types PointExciter.ForceDof or FrameExciter.WrenchDof and described in the following table:

Enum field Description
ForceDof.FX force along the point x axis
ForceDof.FY force along the point y axis
ForceDof.FZ force along the point z axis
WrenchDof.FX force along the frame x axis (world coordinates)
WrenchDof.FY force along the frame y axis (world coordinates)
WrenchDof.FZ force along the frame z axis (world coordinates)
WrenchDof.MX moment about the frame x axis (world coordinates)
WrenchDof.MY moment about the frame y axis (world coordinates)
WrenchDof.MZ moment about the frame z axis (world coordinates)

Point or frame exciters may be created with the following constructors:

PointExciter(Point point, FrameDof dof, double maxForce)

Exciter for point with dof and maxForce.

PointExciter(String name, Point point, FrameDof dof, Mdouble maxForce)

Named exciter for point with dof and maxForce.

FrameExciter(Frame frame, WrenchDof dof, double maxForce)

Exciter for frame with dof and maxForce.

FrameExciter(String name, Frame frame, WrenchDof dof, Mdouble maxForce)

Named exciter for frame with dof and maxForce.

The maxForce argument specifies the maximum force (or moment) that the exciter supplies at an excitation value of 1.0.

If an exciter does not have sufficient strength to facilitate tracking, its excitation value is likely to saturate. This can often be solved by simply increasing the maxForce value.

To allow it to produce negative forces or moments along/about its specified degree of freedom, the excitation bounds for a point or frame exciter must be set to [-1,1]. The TrackingController does this automatically whenever a point or frame exciter is added to it.

For convenience, PointExciter and FrameExciter provide static methods for creating sets of exciters to control all the translational forces and/or moments on a given point or frame:

ArrayList<PointExciter> createPointExciters (MechModel mech, MPoint point, double maxForce, boolean createNames)

Create three exciters to control all forces on a point.

ArrayList<FrameExciter> createFrameExciters ( MMechModel mech, Frame frame, double maxForce, Mdouble maxMoment, boolean createNames)

Create six exciters to control all forces & and moments on a frame.

ArrayList<FrameExciter> createForceExciters (MechModel mech, MFrame frame, double maxForce, boolean createNames)

Create three exciters to control all forces on a frame.

ArrayList<FrameExciter> createMomentExciters ( MMechModel mech, Frame frame, Mdouble maxMoment, boolean createNames)

Create three exciters to control all moments on a frame.

If the (optional) mech argument in these methods is is non-null, the exciters are added to the MechModel’s forceEffectors list. If the argument createNames is true, and the point or frame itself has a non-null name, then the exciter is assigned a name based on the point/frame name and the degree of freedom.

10.2.10 Example: controlling ToyMuscleArm with FrameExciters

The InverseMuscleArm example of Section 10.2.4 can be modified to use frame exciters in place of its muscles, allowing link forces to be controlled directly to make the marker follow the specified path. The modified model is artisynth.demos.tutorial.InverseFrameExcitereArm, and the sections of code where it differs from InverseMuscleArm:sec are listed below:

27    /**
28     * Creates a frame exciter for a rigid body, controlling the force DOF
29     * described by ’dof’ with a maximum activation for of ’maxf’, and adds it
30     * to both the mech model and the tracking controller ’tcon’.
31     */
32    void addFrameExciter (
33       TrackingController tcon, RigidBody body, WrenchDof dof, double maxf) {
34       FrameExciter fex = new FrameExciter (null, body, dof, maxf);
35       myMech.addForceEffector (fex);
36       tcon.addExciter (fex);
37    }
48       TrackingController tcon = new TrackingController (myMech, "tcon");
49       addController (tcon);
50       // For each muscle, reinitialize its rest length for the new
51       // configuration
52       for (AxialSpring spr : myMech.axialSprings()) {
53          spr.setRestLength (spr.getLength());
54       }
55       for (MultiPointSpring spr : myMech.multiPointSprings()) {
56          spr.setRestLength (spr.getLength());
57       }
58
59       // For each link, add two frame exciters to give the controller access to
60       // translational forces in the x-z plane
61       addFrameExciter (tcon, myLink0, WrenchDof.FX, 200);
62       addFrameExciter (tcon, myLink0, WrenchDof.FZ, 200);
63       addFrameExciter (tcon, myLink1, WrenchDof.FX, 200);
64       addFrameExciter (tcon, myLink1, WrenchDof.FZ, 200);

After the controller is created (lines 48-49), the muscle rest lengths are reset, as they are for InverseMuscleArm, but they are not added to controller as exciters. Instead, four frame exciters are created to generate translational forces on each link in the x-z plane, up to a maximum force of 200 (lines 61-64). These exciters are created using the method addFrameExciter() (lines 32-37), which creates the exciter and adds it to the MechModel and to the controller’s exciter list.

Instead of calling the method addFrameExciter(), the frame exciters could have been generated using the following code fragment:

   tcon.addExciters (
      FrameExciter.createForceExciters (
         myMech, myLink0, 200, /*createNames*/false));
   tcon.addExciters (
      FrameExciter.createForceExciters (
         myMech, myLink1, 200, /*createNames*/false));

This would create six exciters instead of four (three forces per link, instead of limiting forces to x-z plane), but the additional forces would simply not be recruited by the controller.

Figure 10.11: Excitations computed for InverseFrameExciterArm.

To run this example, select All demos > tutorial > InverseFrameExciterArm from the Models menu. The excitations generated by running the model are shown in Figure 10.11.