July 17, 2024
ArtiSynth supports an inverse simulation capability that allows the computation of the excitation signals (for muscles and other actuators) needed to track target trajectories for a set of source components within the model (typically points or frames). This is achieved using a specialized controller component known as a TrackingController. An application model creates the tracking controller and configures it with the source components, the exciter components needed to effect the tracking, and probes or controllers to specify the target trajectories. Then at each simulation time step the controller computes the excitations (i.e., signals to the exciter components) that allow the trajectories to be followed as closely as possible, in a manner conceptually similar to computed muscle control in OpenSim [delp2007opensim].
Let ,
and
be composite vectors describing the positions,
velocities and forces of all the components in the application model,
and let
be a vector of excitation signals for all the excitation
components available to the controller.
can be decomposed into
![]() |
(1.1) |
where are the passive forces corresponding to zero excitation, and
are the active forces. During simulation, the controller computes
values for
to best track the target trajectories.
In the case of motion tracking, let and
be the subvectors of
and
containing the positions and velocities of the source components, and let
and
be the corresponding target trajectories. At the beginning of
each time step, the controller compares the current source velocity state
with the desired target state
and uses this to
determine a desired source velocity
for the next time step;
this computation is done by the motion tracker (Figure
1.1, Section 1.1.2).
An excitation generator then computes
in order to try and make
match
(Section 1.1.3).
The excitation generator accepts a desired velocity
instead of a desired acceleration because ArtiSynth’s physics engine computes velocity changes directly.
As mentioned above, the motion tracker computes a desired source velocity
for the next time step based on the current source state
and
desired target state
. This can be done in two ways: chase control (the default), and PD control.
Chase control simply sets to
, plus an additional velocity that
tries to reduce the position error
over a specified time interval
, called the chase time:
![]() |
(1.2) |
In general, should be greater than or equal to the simulation step size
. If it is greater, then
will tend to lag behind
, but this will
also reduce the potential for overshoot due to system
nonlinearities. Conversely, if
if less than
, then
is much
more likely to overshoot. The default value of
is 0.01.
PD control computes a desired source acceleration based on
![]() |
(1.3) |
and then integrates this to determine :
![]() |
(1.4) |
where is the simulation step size. PD control offers a greater ability to
adjust the tracking behavior than chase control, but it is often necessary to
tune the gain parameters
and
. One rule of thumb is to
set their initial values such that (1.2) and
(1.4) are equal, which leads to
![]() |
The default values for and
are
and
, corresponding to
and
both equaling
. Lowering the value of
will reduce
overshoot but increase tracking lag.
PD control is enabled and adjusted by setting the usePDControl, Kp and Kd properties of the motion target term to true (Section 1.3.3).
Given , the excitation generator computes
to try and ensure that the
velocity at the end of the subsequent time step,
, satisfies
![]() |
(1.5) |
This is accomplished using a quadratic program.
First, for a broad class of problems, is linearly related to
,
so that (1.1) simplifies to
![]() |
(1.6) |
where is an excitation response matrix. Given (1.6),
it is possible to show (section “Inverse modeling”
in [lloyd2012artisynth]) that
![]() |
where is the velocity with zero excitations and
is a
motion excitation response matrix. To best follow the trajectory, we
can compute
to minimize the quadratic cost function
![]() |
(1.7) |
If we add in the constraint that excitations lie in the range , the problem takes the form of a quadratic program (QP)
![]() |
|||
![]() |
(1.8) |
In order to prioritize some target terms over others,
can be modified to include weights, according to
![]() |
(1.9) |
where is a diagonal weighting matrix.
To handle excitation redundancy, where the size of
exceeds the
size of
, we can add a regularization term
, where
is a diagonal weight matrix that
can be used to adjust the relative importance of specific excitations:
![]() |
|||
![]() |
(1.10) |
The matrix
described here is the inverse of the matrix
presented in [lloyd2012artisynth].
Other cost terms can be added to the tracking controller. For
instance, we can request a force trajectory for a selected set
of force effectors. As with motions, the forces of these effectors
at the end of step
are linearly related to
via
![]() |
where is the force with zero excitations and
is
the force excitation response matrix. The force trajectory can
then be tracking by minimizing
![]() |
(1.11) |
where is a diagonal weighting matrix.
When tracking force targets, the target force trajectory
is fed
directly into the excitation generator (Figure 1.2); there
is no equivalent of the motion tracker.
To balance the effect of different cost terms, each is associated with
a weight (e.g., ,
, and
for the motion, force and
regularization terms), so that the QP takes a form such as
![]() |
|||
![]() |
(1.12) |
In some cases, the system forces are not linear with respect to the excitations (i.e., equation (1.6) is not valid). One situitaton where this occurs is when equilbrium muscle models are used (Section LABEL:equilibriumMuscles:sec).
When forces are not linear in , the force relationship can be linearized
and the quadratic program can be reformulated in terms of excitation changes
; for example,
![]() |
|||
![]() |
(1.13) |
where denotes the excitation values at the beginning of
the time step. Excitations are then updated
according to
![]() |
Incremental computation can be enabled, at the expense of slower computation, by setting the tracking controller property computeIncrementally to true (Section 1.3.2).
Applications will generally set up a tracking controller using the following steps inside the application’s build() method:
Create an instance of TrackingController and add it to the application using the root model’s addController() method.
Configure the controller with the available excitation components using its addExciter(ex) and addExciter(weight,ex) methods.
Configure the controller to track position or force trajectories for selected source components, which may include Point, Frame, or ForceEffector components. These are specified to the controller using methods such as addPointTarget(point), addFrameTarget(frame), and addForceEffectorTarget(forceEffector). These methods return a target object, which is allocated and maintained by the controller, and which is used for specifying the target positions or forces.
Create input probes or controllers to specify the desired trajectories for the sources added in Step 3. Trajectories are specified by using probes or controllers to set the appropriate target properties of the target objects returned by the addXXXTarget() methods. Likewise, output probes or monitors can be created to record the excitations and the tracked positions or forces of the sources.
Add other cost terms as needed. These may include an L2 regularization term, added using the controller’s addL2RegularizationTerm() method. Regularization attempts to minimze the norm of the excitation values and is needed to resolve redundancy if the excitation set has more degrees of freedom that the target space.
Set controller configuration parameters.
![]() |
![]() |
An simple example illustrating the steps of Section 1.1.6 is given by
artisynth.demos.tutorial.InverseParticle
which uses a set of point-to-point muscles to drive the position of a single particle. The model consists of a single dynamic particle, initially located at the origin, attached to a set of 16 point-to-point muscles arranged radially around it. A tracking controller is created to control the excitations of these muscles so as to enable the particle to follow a trajectory specified by a probe. The code for the model, without the package and include directives, is listed below:
The build() method begins by creating a MechModel in the usual way
and disabling gravity (lines 35-37). The particle to be controlled is then
created and placed at the origin and with a damping factor of 0.1 (lines
40-42). Next, the particle is attached to a set of point-to-point muscles that
are arranged radially around it (lines 45-48). These are created using the
method createMuscle() (lines 13-31), which attaches each to a fixed
non-dynamic particle located a distance of dist from the origin, and sets
its material to a SimpleAxialMuscle (Section LABEL:SimpleAxialMuscle:sec)
with a passive stiffness, damping and maximum force (at excitation 1) defined
by muscleStiffness, muscleDamping, and muscleFmax (lines
4-6). Each muscle’s excitationColor and maxColoredExcitation
property is set so that its color transitions to red as its excitation value
varies from to
(lines 28-29, Section 1.2.1.1)),
and its rest length is initialized to its current length (line 30).
After the model components have been built, a TrackingController is created and added to the root model’s controller set (lines 51-52). All of the muscles are then added to the controller as exciters to be controlled (lines 54-58). The particle is added to the controller as a motion source using the addPointTarget() method (line 60), which returns a target object in the form of a TargetPoint. Since the number of exciters exceeds the degrees-of-freedom of the target space, an L2-regularization term is added (line 63) to resolve the redundancy.
Rendering properties are set at lines 67-69: points in the model are rendered as gray spheres, except for the dynamic particle which is colored white, and the muscles are drawn as blue cylinders. (The target particle, which is contained within the controller, is displayed using its default color of cyan.)
Probes are created to specify the target trajectory and record the computed excitations. The first, targetprobe, is an input probe attached to the position property of the target component, running from time 0 to 1 (lines 72-79). Its data is specified in code using addData(), which specifies three knot points with a time step of 0.5. Interpolation is set to cubic (line 78) for greater smoothness. The second, exprobe, is a probe that records all the excitations computed by the controller (lines 82-85). It is created by the utility method createOutputProbe() supplied by the InverseManager class (Section 1.4.1).
To run this example, select All demos > tutorial > InverseParticle from the Models menu. The model should load and initially appear as in Figure 1.3 (left). When run, the controller computes excitations to move the particle along the trajectory specified by the probe, which is upward and to the right (Figure 1.3, right) and back again.
The target point created by the controller is rendered separately from the source point as a cyan-colored sphere (see Section 1.2.2.2). As the simulation proceeds and certain muscles are excited, their coloring changes from blue to red, in proportion to their excitation, in accordance with the properties excitationColor and maxColoredExcitation. Recorded data for both the trajectory and the computed excitation probes are shown in Figure 1.4.
This section describes in greater detail how exciters, motion and force sources, and other cost terms can be specified for the controller.
An exciter can be any model component that implements
ExcitationComponent. These include
point-to-point Muscles (Section LABEL:PointToPointMuscles:sec), muscle
bundles in FEM muscle models (Section LABEL:MuscleBundles:sec), and MuscleExciters (Section LABEL:MuscleExciters:sec). Each exciter exports an
excitation property which accepts a scaler input signal (usually in the
range ) 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. |
ArrayList<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
used by the quadratic program ((1.10 and
(1.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 , as
indicated in Section 1.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 1.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.
Some exciters (such as Muscle and MultiPointMuscle) support the ability to change color in proportion to their excitation value. This makes it easier to visualze 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.
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 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 can also be specified by
setting the velocity property of the target object. If this is not set,
defaults to 0, which will introduce a small lag into the tracking
behavior. If
is set, care should be taken the ensure that it is
consistent with the actual time derivative of
.
Applications typically do not need to specify
. However, doing so may improve tracking performance, particularly when using PD control (Section 1.1.2).
Each target component also implements the interface
TrackingTarget, described in
Section 1.2.8, which supplies methods to specify the
weights in the weighting matrix of the motion tracking cost term
(1.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. |
The cost term 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 1.3.3.
The motion tracking weight in (1.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. |
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 trackes 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 properites for the motion target lists. |
void setTargetRenderProps (RenderProps props) |
Set render properites for the motion target lists. |
void setTargetsPointRadius (double rad) |
Set pointRadius target render property to rad. |
L2 regularization attempts to minimize the weighted square of the excitation values, as described by
![]() |
(1.14) |
in (1.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 addL2RegularizationTerm() |
Add L2 regularization with weight 1.0. |
void addL2RegularizationTerm(double w) |
Add L2 regularization with weight w. |
void removeL2RegularizationTerm() |
Remove L2 regularization. |
The weight associated with the above methods corresponds to in
(1.14) and (1.12). The entries in the
diagonal matrix
are given by the weights associated with the exciters
themselves, as described in Section 1.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 , its use will
reduce the controller’s tracking accuracy. Therefore, the regularizer is often
employed with a weight value
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.
The controller also provides a damping term that serves to minimize the time
derivative of , or more precisely,
![]() |
where is the diagonal excitation weight matrix used for L2
regularization (1.14). Letting
denote
the excitations at the beginning of the time step, and approximating
by
![]() |
where is the time step size, the damping cost term
becomes
![]() |
Excitation damping can be managed by the following methods:
void addDampingTerm() |
Add excitation damping with weight 1.0. |
void addDampingTerm(double w) |
Add excitation damping with weight w. |
void removeDampingTerm() |
Remove 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.
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 LABEL:ToyMuscleArm:sec) follow a presribed 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:
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 myTipMarker) 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 1.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 1.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 1.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.
![]() |
![]() |
![]() |
![]() |
To run this example, select All demos > tutorial > InverseMuscleArm from the Models menu. The model should load and initially appear as in Figure 1.5. When run, the controller will compute excitations to make the tip marker trace the desired trajectory, as shown in Figure 1.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 1.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 1.2.1.1. This behaviour can be disabled by setting the controller property configExcitationColoring to false.
To illustrate how the L2 regularization weight can affect the tracking error, 1.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 noticably different.
![]() ![]() |
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 LABEL:ToyMuscleFem:sec) follow a presribed 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:
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 1.1.2), using gains of and
(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 tne time range from 0 to 5, with 26 knot points and cubic interpolation.
![]() |
![]() |
![]() |
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 1.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 1.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 1.7 (right). The target and actual source trajectories for the frame’s position (i.e., its origin) is shown in Figure 1.8, together with the computed excitations.
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:
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 1.2.8, which supplies methods to specify the
weighting matrix in the force effector cost term
(1.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 explictly 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.
The cost term 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 automaticlly 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 effectior tracking weight in (1.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. |
![]() |
![]() |
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 1.1.7), except for different parameter defintions,
which give the muscles different strengths and cause only 3 to be created
instead of 16, and the fact that the "center" particle is initialy placed
at instead of the origin. The rest of the code diverges after
the tracking controller is created, as shown below:
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 1.4).
To run this example, select All demos > tutorial > InverseSpringForce from the Models menu. The model should load and initially appear as in Figure 1.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 1.3, right). Both the target-source and computed excitation probes are shown in Figure 1.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.
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
priorty 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’s 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., for the motion tracking cost
function
in (1.9) and
for the force
effector cost function
in (1.11)). Targets
(or specific DOFs) with higher weights will be tracked more accurately, while
weights of 0 effectively disble 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. |
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 [delp2007opensim], 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 . 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.
The InverseMuscleArm example of Section 1.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:
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:
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.
To run this example, select All demos > tutorial > InverseFrameExciterArm from the Models menu. The excitations generated by running the model are shown in Figure 1.11.
The tracking controller is a composite component, which contains the exciter list and the various cost and constraint terms as subcomponents. Some of these components are added as needed. They include the following, as described by their names:
A list of references to the exciters which have been added to the controller. Note that this is a list of references; the exciters themselves exist elsewhere in the model. Always present.
The MotionTargetTerm detailed in Section 1.2.2.1.
Implements the motion cost term defined in
(1.7), and the motion tracker of
Figure 1.1. Allocates and maintains target components
for motion sources, storing the targets in the subcomponent lists targetPoints and targetFrames, and references to the sources in the
reference lists sourcePoints and sourceFrames. Always present.
Implements the actuator bounds ,
where
and
are nominally
and
. Always present.
Implements the cost term for L2 regularization (Section 1.2.3.1). Added on demand.
Implements the cost term for excitation damping (Section 1.2.3.2). Added on demand.
The controller and its cost terms have a number of properties that can be used to adjust the controller’s behavior. They can be set interactively, either through the inverse controller panel created by the InverseManager (Section 1.4.3), or by selecting the relevant component in the navigation panel and choosing "Edit properties ..." from the context menu. The properties can also be set in code, using the accessor methods supplied by their host component. These methods will usually be named getXxx() and setXxx() for property xxx.Properties of the tracking controller itself include:
Controls whether or not the controller is active. Setting this to false turns the controller off. Default value is true.
Controls whether or not the cost terms are normalized. Normalizing the terms
helps ensure that their weights (e.g., ,
, and
in
(1.12) more accurately control the tradeoffs between terms.
Default value is true.
Controls whether excitation values should be computed incrementally, as described in Section 1.1.5. Default value is false.
Specifies the default excitation bounds for exciter components. Default value
is .
Controls the visibility of the motion target terms contained within the controller, as described in Section 1.2.2.2. Default value is true.
If enabled, automatically configures exciters which support excitation coloring to vary from white to red as their excitation increases (Section 1.2.1.1). Default value is true.
Default duration of probes managed by the InverseManager class (Section 1.4). Default value is 1.0.
Default update interval of output probes managed by the InverseManager class (Section 1.4). Default value is -1, which means that the interval will default to the simulation step size.
Uses a more computationally expensive but possibly more accurate method to
compute the excitation response matrices matrices such as and
(Section 1.1.3). Default value is false.
Experimental property that limits the change in excitation durig any time step. Default value is false.
Experimental property which enables scaling of all the cost terms by the simulation time step. Default value is false.
Enabled debugging messages. Default value is false.
The motion target term (Section 1.2.2.1) exports a number of properties that control motion tracking:
Controls whether or not this term is enabled. Default value is true.
Specifies the weight of this term relative to the other cost
terms. Default value is 1.0.
Specifies the chase time used for chase control
(Section 1.1.2.1). Default value is 0.01.
Controls whether or not PD control is used (Section 1.1.2.2). Default value is false.
Specifies the proportional terms for PD control (Section 1.1.2.2). Default value is 10000.
Specifies the derivative terms for PD control (Section 1.1.2.2). Default value is 100.
Controls whether or not legacy control is used. Default value is false.
All other cost terms, including the L2 regularization (Section 1.2.3.1), export the following properties:
Controls whether or not the term is enabled. Default value is true.
Specifies the weight of this term relative to the other cost terms. Default value is 1.0.
The tracking controller package provides the helper class InverseManager to aid with the creation of probes and control panels that are useful in for inverse simulation applications. The functionality provided by InverseManager is described in this section, and additional documentation may be found in its API documentation.
ProbeID | I/O type | Probe name | Default filename |
---|---|---|---|
TARGET_POSITIONS | input | "target positions" | target_positions.txt |
INPUT_EXCITATIONS | input | "input excitations" | input_excitations.txt |
TRACKED_POSITIONS | output | "tracked positions" | tracked_positions.txt |
SOURCE_POSITIONS | output | "source positions" | source_positions.txt |
COMPUTED_EXCITATIONS | output | "computed excitations" | computed_excitations.txt |
InverseManager contains static methods that support the creation and management of a variety of probes related to inverse simulation. These probes are defined by the enumerated type InverseManager.ProbeID, which includes the following identifiers:
An input probe which provides a trajectory for all motion
targets. Its input values are attached to the position property (3 values)
of each point target and the position and orientation properties of
each frame target (7 values), in the order the targets appear in the
controller.
An input probe which provides input excitation values for all the controller’s exciters, in the order they appear in the controller. This probe can be used for testing purposes, in order to see what trajectory can be expected to arise from a given excitation input.
An output probe which records the positions of all motion targets as they are set by the controller. Its output values are extracted from the same component properties used by TARGET_POSITIONS.
An output probe which records that actual position tracjectory followed
by all motion souces. Its output values are extracted from the same component
properties used by TARGET_POSITIONS.
An output probe which records the computed excitation values for all the controller’s exciters, in the order they appear in the controller.
Each of these probes is associated with both a name and a default file name (Table 1.1), where the default file name may be assigned if another file path is not explcitly specified.
When a default file name is used, it is assumed to be relative to the ArtiSynth working folder, as described in Section LABEL:Probes:sec.
These probes may be created, for a given tracking controller, with the following (static) InverseManager methods:
NumericInputProbe createInputProbe (TrackingController tcon MProbeID pid, String filePath, double start, double stop) |
Create input probe specified by pid. |
NumericOutputProbe createOutputProbe (TrackingController tcon MProbeID pid, String filePath, double start, double stop, Mdouble interval) |
Create output probe specified by pid. |
Here pid specifies the probe type, filePath an (optional) file path, start and stop the start and stop times, and interval the output probe update interval. Input probes are initialzed to use Interpolation.Order#CubicStep interpolation.
Created probes may be added to the application root model as needed.
Most of the examples above use createOutputProbe() to create a computed excitations probe. In InverseMuscleFem (Section 1.2.5), the two output probes used to display the tracked position and orientation of myFrame, created at lines (42-51), could be replaced by a single probe of type SOURCE_POSITIONS, using a code fragment like this:
Likewise, a probe showing the target position and orientation, for purposes of comparing it against SOURCE_POSITIONS and evaluating the tracking error, could be created using
For both these method calls, the source and target components (myFrame and target in the example) are automatically located within the tracking controller.
One reason to create a TRACKED_POSITIONS probe, instead of comparing SOURCE_POSITIONS directly against the trajectory input probe, is that the former is guaranteed to have the same number of data points as SOURCE_POSITIONS, whereas a trajectory probe, being an input probe, may not. Moreover, if the trajectory is being produced using a controller, a trajectory probe may not even exist.
For convenience, InverseManager also provides methods to add a complete set of default probes to the root model:
void addProbes (RootModel root, TrackingController tcon, Mdouble duration, double interval) |
Add default probes to root. |
void resetProbes (RootModel root, TrackingController tcon, Mdouble duration, double interval) |
Clear probes and add default probes to root. |
addProbes() creates a COMPUTED_EXCITATIONS probe and an INPUT_EXCITATIONS probe, with the latter deactivated by default. If the controller has any motion targets, it will also create TARGET_POSITIONS, TRACKED_POSITIONS and SOURCE_POSITIONS probes. Probe start times are set to 0, stop times are set to duration, and the output probe update interval is set to interval, with -1 causing the interval to default to the simulation step size. Probe file names are set to the default file names described in Table 1.1. Since these file names are relative, the files themselves are assumed to exist in the ArtiSynth working folder, as described in Section LABEL:Probes:sec. Input probes are initialzed to use Interpolation.Order#CubicStep interpolation. If the file for an input probe actually exists, it is used to initialize the probe’s data; otherwise, the probe data is initialized to 0 and the probe is deactivated.
resetProbes() removes all existing probes and then behaves identically to addProbes().
InverseManager also supplies methods to locate and adjust the probes that it has created:
NumericInputProbe findInputProbe (RootModel root, ProbeID pid) |
Find specified input probe. |
NumericOutputProbe findOutputProbe (RootModel root, ProbeID pid) |
Find specified output probe. |
Probe findProbe (RootModel root, ProbeID pid) |
Find specified input or output probe. |
boolean setInputProbeData (RootModel root, MProbeID pid, double[] data, double timeStep) |
Set input probe data. |
boolean setProbeFileName (RootModel root, MProbeID pid, String filePath) |
Set probe file path. |
The findProbe() methods search for the probe specified by pid within a root model, using the probe’s file name (Table 1.1) as a search key. The method setInputProbeData() searches for a specified probe, and if it is found, sets its data using the probe’s setData(double[],double) method and returns true. Likewise, setProbeFileName() searches for a specified probe and sets its file path.
Since probes maintained by InverseManager use probe names as a search key, care should be taken to ensure that these names do not conflict with those of other application probes.
The example InverseMuscleArm (Section 1.2.4) has been configured for use with the InverseManager by setting its working folder to inverseMuscleArm located beneath its source folder. If instead of creating the target and output excitations probes (lines 34-52), addProbes() is simply called at the end of the build() method (after the working folder has been set), as in
then a set of probes will be created as shown in Figure 1.13. The tracing probes ("target tracing" and "source tracing") are still present, in addition to five default probes. TARGET_POSTIONS and INPUT_EXCITATIONS are expanded to show the data that has been automatically loaded from the files target_positions.txt and input_excitations.txt located in the working folder. By default, the former probe is enabled and the latter is disabled. If the model is run, inverse simulation will proceed as before.
After loading the probes, the application could disable the tracking controller and enable the input excitations:
This will cause the simulation to run “open loop” in response to the excitations. However, because the input excitations are slightly different than those that were computed by the controller, there is a now a considerable tracking error (Figure 1.14, left).
After the open loop run, one can save the resulting SOURCE_POSITIONS probe, which will write the file "target_positions.txt'' into the working folder. This can then be used to generate a new target trajectory, by copying it to "target_positions.txt". If the model is reloaded, the new trajectory will now appear in the TARGET_POSITIONS probe (Figure 1.15). Deactivating the INPUT_EXCITATIONS probe and reactivating the tracking contoller will cause this new trajectoty to be tracked properly (Figure 1.14, right).
In ways such as this, the probes may be used to examine the behavior and performance of the tracking controller.
![]() ![]() |
InverseManager also provide methods to create and manage an inverse control panel, which allows interactive editing of the properties of the tracking controller and its various cost terms, as described in Section 1.3. Applications can create an instance of this panel in their build() method, as the InverseMuscleArm example (Section 1.2.4) does at line 65:
The panel is automatically configured to display the properties of the cost terms with which the controller is currently configured. Figure 1.16 shows the panel for InverseManager.
The following methods are supplied by InverseManager() to manage inverse control panels:
ControlPanel createInversePanel (TrackingController tcon) |
Create inverse control panel for tcon. |
ControlPanel createInversePanel (RootModel root, MTrackingController tcon) |
Create and add inverse control panel to root. |
ControlPanel findInversePanel (RootModel root) |
Find inverse control panel in root. |
At the top of the inverse panel are three buttons: "reset probes", "sync excitations", and "sync targets". The first uses InverseManager.resetProbes() to clear all probes and recreate the default probes, using the duration and update interval specified by the tracking controller properties probeDuration and probeUpdateInterval. The second button copies the contents of the COMPUTED_EXCITATIONS probe into the INPUT_EXCITATIONS probe. If this is done after excitations have been computed, then running the simulation again with the controller deactived and INPUT_EXCITATIONS activated should produce an identical motion. The third button copies the contents of the SOURCE_POSITIONS probe into the TARGET_POSITIONS. This can be used to verify that a source trajectory created open loop with a prescribed set of input excitations can in fact be tracked by the controller.
The probe copy methods used by the “sync” buttons are also available to the application code:
boolean syncTargetProbes (RootModel root) |
Copy SOURCE_POSITIONS into TARGET_POSITIONS. |
boolean syncExcitationProbes (RootModel root) |
Copy COMPUTED_EXCITATIONS into INPUT_EXCITATIONS. |
Use of the inverse controller is subject to some caveats.
Tracked components must be dynamic, in that their motion is controlled by forces. This means that their dynamic property must be set to true or they must be attached to other bodies that are dynamic.
The algorithm which computes the excitations currently
has a complexity in the number of excitations
. This is due both
to the use of numeric differentation for determining excitation response
matrices such as
and
(Section
1.1.3), and also the fact that the QP solver
is at present a dense solver. This means that applications with large numbers
of excitations may require considerably longer to simulate than those with
fewer excitations.
The controller is not designed to work with unilateral constraints (the most common examples of which are constraint-based contact and joint limits). For models containing these behaviors, inverse simulation may produce excitations that jump around erratically. In the case of collisions, one solution may be to use force-based collisions, implemented with either PointPlaneForce or PointMeshForce, as described in Section LABEL:OtherPointForces:sec. Setting these components to use a quadratic force function may improve performace even more. Another possible solution, for any kind of unilateral constraint, may be to make it compliant (Sections LABEL:JointCompliance:sec and LABEL:ContactRegularization:sec), since this has the same effect as making the constraint force-based.
Prescribed motion trajectories may need to be sufficiently smooth the avoid large transient excitations, particularly at the start and end of motions when velocities transition from and to 0. Transient effects are often more pronounced for models where inertial effects are high. Cubic interpolation is useful for creating smoother trajectories, particularly with a limited number of data points. It may also be necessary to expliclty smooth an input trajectory with additional points.