Skip to content

Commit 3d29ea3

Browse files
authored
Merge branch 'main' into opensim_451
2 parents f47f2ec + 5c1f57d commit 3d29ea3

File tree

144 files changed

+4121
-98130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+4121
-98130
lines changed

Bindings/OpenSimHeaders_moco.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <OpenSim/Moco/MocoBounds.h>
88
#include <OpenSim/Moco/MocoCasADiSolver/MocoCasADiSolver.h>
99
#include <OpenSim/Moco/MocoControlBoundConstraint.h>
10+
#include <OpenSim/Moco/MocoOutputBoundConstraint.h>
11+
#include <OpenSim/Moco/MocoStateBoundConstraint.h>
1012
#include <OpenSim/Moco/MocoFrameDistanceConstraint.h>
1113
#include <OpenSim/Moco/MocoOutputConstraint.h>
1214
#include <OpenSim/Moco/MocoGoal/MocoAccelerationTrackingGoal.h>

Bindings/moco.i

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ namespace OpenSim {
4848
%include <OpenSim/Moco/MocoConstraint.h>
4949

5050
%include <OpenSim/Moco/MocoControlBoundConstraint.h>
51+
%include <OpenSim/Moco/MocoOutputBoundConstraint.h>
52+
%include <OpenSim/Moco/MocoStateBoundConstraint.h>
5153
%include <OpenSim/Moco/MocoFrameDistanceConstraint.h>
5254
%include <OpenSim/Moco/MocoOutputConstraint.h>
5355

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,24 @@ request related to the change, then we may provide the commit.
66

77
This is not a comprehensive list of changes but rather a hand-curated collection of the more notable ones. For a comprehensive history, see the [OpenSim Core GitHub repo](https://github.com/opensim-org/opensim-core).
88

9+
v4.6
10+
====
11+
- The performance of `getStateVariableValue`, `getStateVariableDerivativeValue`, and `getModelingOption` was improved in
12+
the case where provided string is just the name of the value, rather than a path to it (#3782)
13+
- Fixed bugs in `MocoStepTimeAsymmetryGoal::printDescriptionImpl()` where there were missing or incorrect values printed. (#3842)
14+
- Added `ModOpPrescribeCoordinateValues` which can prescribe motion of joints in a model given a table of data. (#3862)
15+
- Fixed bugs in `convertToMocoTrajectory()` and `MocoTrajectory::resampleWithFrequency()` by updating `interpolate()` to
16+
allow extrapolation using the `extrapolate` flag. Combined with the `ignoreNaNs` flag, this prevents NaNs from
17+
occurring in the output. (#3867)
18+
- Added `Output`s to `ExpressionBasedCoordinateForce`, `ExpressionBasedPointToPointForce`, and `ExpressionBasedBushingForce` for accessing force values. (#3872)
19+
920
v4.5.1
1021
======
1122
- Added support for list `Socket`s via the macro `OpenSim_DECLARE_LIST_SOCKET`. The macro-generated method
1223
`appendSocketConnectee_*` can be used to connect `Object`s to a list `Socket`. In addition, `Component` and Socket have
1324
new `getConnectee` overloads that take an index to a desired object in the list `Socket` (#3652).
1425
- Added `ComponentPath::root()`, which returns a `ComponentPath` equivalent to "/"
26+
- Added `ComponentPath::separator()`, which returns the separator that's placed between elements of the path (i.e. `'/'`)
1527
- `ComponentPath` is now less-than (`<`) comparable, making it usable in (e.g.) `std::map`
1628
- `ComponentPath` now has a `std::hash<T>` implementation, making it usable in (e.g.) `std::unordered_map`
1729
- Added `.clear()` and `.empty()` to `ComponentPath` for more parity with `std::string`'s semantics

CHANGELOG_MOCO.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
Moco Change Log
22
===============
33

4+
1.4.0
5+
-----
6+
- 2024-07-26: Added `MocoStateBoundConstraint` and `MocoOutputBoundConstraint` to enable bounding
7+
state variables or output values by one or two `Function`s, similar to
8+
`MocoControlBoundConstraint`.
9+
10+
- 2024-07-22: Added support for `MocoOutputGoal`s and `MocoOutputConstraint`s that are
11+
composed of two `Output`s. This applies to all types of Output goals
12+
(`MocoInitialOutputGoal`, `MocoFinalOutputGoal`, etc.). The two `Output`s
13+
can be combined by addition, subtraction, multiplication, or division.
14+
15+
- 2024-07-08: Fixed a bug in `DeGrooteFregly2016Muscle` where updates to properties
16+
`pennation_angle_at_optimal`, `optimal_fiber_length`, `max_contraction_velocity`,
17+
and `tendon_strain_at_one_norm_force` during parameter optimization did not
18+
affect certain model calculations, and as a result were not changing during
19+
optimization.
20+
421
1.3.1
522
-----
623
- 2024-07-08: Fixed a bug where deserialization of an OpenSim model with the `Bhargava2004SmoothedMuscleMetabolics`

OpenSim/Actuators/DeGrooteFregly2016Muscle.cpp

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,7 @@ void DeGrooteFregly2016Muscle::extendFinalizeFromProperties() {
152152
"Pennation angle at optimal fiber length must be in the range [0, "
153153
"Pi/2).");
154154

155-
using SimTK::square;
156-
const auto normFiberWidth = sin(get_pennation_angle_at_optimal());
157-
m_fiberWidth = get_optimal_fiber_length() * normFiberWidth;
158-
m_squareFiberWidth = square(m_fiberWidth);
159-
m_maxContractionVelocityInMetersPerSecond =
160-
get_max_contraction_velocity() * get_optimal_fiber_length();
161-
m_kT = log((1.0 + c3) / c1) /
162-
(1.0 + get_tendon_strain_at_one_norm_force() - c2);
163-
m_isTendonDynamicsExplicit =
164-
get_tendon_compliance_dynamics_mode() == "explicit";
155+
m_isTendonDynamicsExplicit = get_tendon_compliance_dynamics_mode() == "explicit";
165156
}
166157

167158
void DeGrooteFregly2016Muscle::extendAddToSystem(
@@ -279,13 +270,13 @@ void DeGrooteFregly2016Muscle::calcMuscleLengthInfoHelper(
279270
// ------
280271
mli.fiberLengthAlongTendon = muscleTendonLength - mli.tendonLength;
281272
mli.fiberLength = sqrt(
282-
SimTK::square(mli.fiberLengthAlongTendon) + m_squareFiberWidth);
273+
SimTK::square(mli.fiberLengthAlongTendon) + getSquareFiberWidth());
283274
mli.normFiberLength = mli.fiberLength / get_optimal_fiber_length();
284275

285276
// Pennation.
286277
// ----------
287278
mli.cosPennationAngle = mli.fiberLengthAlongTendon / mli.fiberLength;
288-
mli.sinPennationAngle = m_fiberWidth / mli.fiberLength;
279+
mli.sinPennationAngle = getFiberWidth() / mli.fiberLength;
289280
mli.pennationAngle = asin(mli.sinPennationAngle);
290281

291282
// Multipliers.
@@ -311,7 +302,7 @@ void DeGrooteFregly2016Muscle::calcFiberVelocityInfoHelper(
311302
fvi.normFiberVelocity =
312303
calcForceVelocityInverseCurve(fvi.fiberForceVelocityMultiplier);
313304
fvi.fiberVelocity = fvi.normFiberVelocity *
314-
m_maxContractionVelocityInMetersPerSecond;
305+
getMaxContractionVelocityInMetersPerSecond();
315306
fvi.fiberVelocityAlongTendon =
316307
fvi.fiberVelocity / mli.cosPennationAngle;
317308
fvi.tendonVelocity =
@@ -331,13 +322,13 @@ void DeGrooteFregly2016Muscle::calcFiberVelocityInfoHelper(
331322
fvi.fiberVelocity =
332323
fvi.fiberVelocityAlongTendon * mli.cosPennationAngle;
333324
fvi.normFiberVelocity =
334-
fvi.fiberVelocity / m_maxContractionVelocityInMetersPerSecond;
325+
fvi.fiberVelocity / getMaxContractionVelocityInMetersPerSecond();
335326
fvi.fiberForceVelocityMultiplier =
336327
calcForceVelocityMultiplier(fvi.normFiberVelocity);
337328
}
338329

339330
const SimTK::Real tanPennationAngle =
340-
m_fiberWidth / mli.fiberLengthAlongTendon;
331+
getFiberWidth() / mli.fiberLengthAlongTendon;
341332
fvi.pennationAngularVelocity =
342333
-fvi.fiberVelocity / mli.fiberLength * tanPennationAngle;
343334
}

OpenSim/Actuators/DeGrooteFregly2016Muscle.h

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,29 @@ that support implicit dynamics (i.e. Moco) and cannot be used to perform a
8080
time-stepping forward simulation with Manager; use explicit mode for
8181
time-stepping.
8282
83-
@note Normalized tendon force is bounded in the range [0, 5] in this class.
84-
The methods getMinNormalizedTendonForce() and
83+
@section property Property Bounds
84+
The acceptable bounds for each property are enforced at model initialization.
85+
These bounds are:
86+
- activation_time_constant: (0, inf]
87+
- deactivation_time_constant: (0, inf]
88+
- active_force_width_scale: [1, inf]
89+
- fiber_damping: [0, inf]
90+
- passive_fiber_strain_at_one_norm_force: (0, inf]
91+
- tendon_strain_at_one_norm_force: (0, inf]
92+
- pennation_angle_at_optimal: [0, Pi/2)
93+
- default_activation: (0, inf]
94+
- default_normalized_tendon_force: [0, 5]
95+
96+
@note The methods getMinNormalizedTendonForce() and
8597
getMaxNormalizedTendonForce() provide these bounds for use in custom solvers.
8698
99+
@note Muscle properties can be optimized using MocoParameter. The acceptable
100+
bounds for each property are **not** enforced during parameter optimization, so
101+
the user must supply these bounds to MocoParameter.
102+
103+
@note The properties `default_activation` and `default_normalized_tendon_force`
104+
cannot be optimized because they are applied during model initialization only.
105+
87106
@section departures Departures from the Muscle base class
88107
89108
The documentation for Muscle::MuscleLengthInfo states that the
@@ -107,32 +126,32 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
107126
public:
108127
OpenSim_DECLARE_PROPERTY(activation_time_constant, double,
109128
"Smaller value means activation can increase more rapidly. "
110-
"Default: 0.015 seconds.");
129+
"Default: 0.015 seconds. Bounds: (0, inf]");
111130
OpenSim_DECLARE_PROPERTY(deactivation_time_constant, double,
112131
"Smaller value means activation can decrease more rapidly. "
113-
"Default: 0.060 seconds.");
132+
"Default: 0.060 seconds. Bounds: (0, inf]");
114133
OpenSim_DECLARE_PROPERTY(default_activation, double,
115134
"Value of activation in the default state returned by "
116-
"initSystem(). Default: 0.5.");
135+
"initSystem(). Default: 0.5. Bounds: (0, inf]");
117136
OpenSim_DECLARE_PROPERTY(default_normalized_tendon_force, double,
118137
"Value of normalized tendon force in the default state returned by "
119-
"initSystem(). Default: 0.5.");
138+
"initSystem(). Default: 0.5. Bounds: [0, 5].");
120139
OpenSim_DECLARE_PROPERTY(active_force_width_scale, double,
121140
"Scale factor for the width of the active force-length curve. "
122-
"Larger values make the curve wider. Default: 1.0.");
141+
"Larger values make the curve wider. Default: 1.0. Bounds: [1, inf]");
123142
OpenSim_DECLARE_PROPERTY(fiber_damping, double,
124143
"Use this property to define the linear damping force that is "
125144
"added to the total muscle fiber force. It is computed by "
126145
"multiplying this damping parameter by the normalized fiber "
127-
"velocity and the max isometric force. Default: 0.");
146+
"velocity and the max isometric force. Default: 0. Bounds: [0, inf]");
128147
OpenSim_DECLARE_PROPERTY(ignore_passive_fiber_force, bool,
129148
"Make the passive fiber force 0. Default: false.");
130149
OpenSim_DECLARE_PROPERTY(passive_fiber_strain_at_one_norm_force, double,
131150
"Fiber strain when the passive fiber force is 1 normalized force. "
132-
"Default: 0.6.");
151+
"Default: 0.6. Bounds: (0, inf]");
133152
OpenSim_DECLARE_PROPERTY(tendon_strain_at_one_norm_force, double,
134153
"Tendon strain at a tension of 1 normalized force. "
135-
"Default: 0.049.");
154+
"Default: 0.049. Bounds: (0, inf]");
136155
OpenSim_DECLARE_PROPERTY(tendon_compliance_dynamics_mode, std::string,
137156
"The dynamics method used to enforce tendon compliance dynamics. "
138157
"Options: 'explicit' or 'implicit'. Default: 'explicit'. ");
@@ -156,6 +175,7 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
156175

157176
DeGrooteFregly2016Muscle() { constructProperties(); }
158177

178+
159179
protected:
160180
//--------------------------------------------------------------------------
161181
// COMPONENT INTERFACE
@@ -493,14 +513,14 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
493513
// TODO: In explicit mode, do not allow negative tendon forces?
494514
SimTK::Real calcTendonForceMultiplier(
495515
const SimTK::Real& normTendonLength) const {
496-
return c1 * exp(m_kT * (normTendonLength - c2)) - c3;
516+
return c1 * exp(getTendonStiffnessParameter() * (normTendonLength - c2)) - c3;
497517
}
498518

499519
/// This is the derivative of the tendon-force length curve with respect to
500520
/// normalized tendon length.
501521
SimTK::Real calcTendonForceMultiplierDerivative(
502522
const SimTK::Real& normTendonLength) const {
503-
return c1 * m_kT * exp(m_kT * (normTendonLength - c2));
523+
return c1 * getTendonStiffnessParameter() * exp(getTendonStiffnessParameter() * (normTendonLength - c2));
504524
}
505525

506526
/// This is the integral of the tendon-force length curve with respect to
@@ -513,8 +533,10 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
513533
const double minNormTendonLength =
514534
calcTendonForceLengthInverseCurve(m_minNormTendonForce);
515535
const double temp1 =
516-
exp(m_kT * normTendonLength) - exp(m_kT * minNormTendonLength);
517-
const double temp2 = c1 * exp(-c2 * m_kT) / m_kT;
536+
exp(getTendonStiffnessParameter() * normTendonLength)
537+
- exp(getTendonStiffnessParameter() * minNormTendonLength);
538+
const double temp2 = c1 * exp(-c2 * getTendonStiffnessParameter())
539+
/ getTendonStiffnessParameter();
518540
const double temp3 = c3 * (normTendonLength - minNormTendonLength);
519541
return temp1 * temp2 - temp3;
520542
}
@@ -523,7 +545,8 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
523545
/// normalized tendon length as a function of the normalized tendon force.
524546
SimTK::Real calcTendonForceLengthInverseCurve(
525547
const SimTK::Real& normTendonForce) const {
526-
return log((1.0 / c1) * (normTendonForce + c3)) / m_kT + c2;
548+
return log((1.0 / c1) * (normTendonForce + c3))
549+
/ getTendonStiffnessParameter() + c2;
527550
}
528551

529552
/// This returns normalized tendon velocity given the derivative of
@@ -534,8 +557,8 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
534557
SimTK::Real calcTendonForceLengthInverseCurveDerivative(
535558
const SimTK::Real& derivNormTendonForce,
536559
const SimTK::Real& normTendonLength) const {
537-
return derivNormTendonForce /
538-
(c1 * m_kT * exp(m_kT * (normTendonLength - c2)));
560+
return derivNormTendonForce / (c1 * getTendonStiffnessParameter()
561+
* exp(getTendonStiffnessParameter() * (normTendonLength - c2)));
539562
}
540563

541564
/// This computes both the total fiber force and the individual components
@@ -632,8 +655,8 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
632655
// pennationAngle = asin(fiberWidth/fiberLength)
633656
// d_pennationAngle/d_fiberLength =
634657
// d/d_fiberLength (asin(fiberWidth/fiberLength))
635-
return (-m_fiberWidth / square(fiberLength)) /
636-
sqrt(1.0 - square(m_fiberWidth / fiberLength));
658+
return (-getFiberWidth() / square(fiberLength)) /
659+
sqrt(1.0 - square(getFiberWidth() / fiberLength));
637660
}
638661

639662
/// The derivative of the fiber force along the tendon with respect to fiber
@@ -838,6 +861,23 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
838861
void calcMusclePotentialEnergyInfoHelper(const bool& ignoreTendonCompliance,
839862
const MuscleLengthInfo& mli, MusclePotentialEnergyInfo& mpei) const;
840863

864+
SimTK::Real getFiberWidth() const {
865+
const auto normFiberWidth = sin(get_pennation_angle_at_optimal());
866+
return get_optimal_fiber_length() * normFiberWidth;
867+
}
868+
SimTK::Real getSquareFiberWidth() const {
869+
return SimTK::square(getFiberWidth());
870+
}
871+
SimTK::Real getMaxContractionVelocityInMetersPerSecond() const {
872+
return get_max_contraction_velocity() * get_optimal_fiber_length();
873+
}
874+
/// Tendon stiffness parameter kT from De Groote et al., 2016. Instead of
875+
/// kT, users specify tendon strain at 1 norm force, which is more intuitive.
876+
SimTK::Real getTendonStiffnessParameter() const {
877+
return log((1.0 + c3) / c1) /
878+
(1.0 + get_tendon_strain_at_one_norm_force() - c2);
879+
}
880+
841881
/// This is a Gaussian-like function used in the active force-length curve.
842882
/// A proper Gaussian function does not have the variable in the denominator
843883
/// of the exponent.
@@ -949,14 +989,6 @@ class OSIMACTUATORS_API DeGrooteFregly2016Muscle : public Muscle {
949989

950990
// Computed from properties.
951991
// -------------------------
952-
953-
// The square of (fiber_width / optimal_fiber_length).
954-
SimTK::Real m_fiberWidth = SimTK::NaN;
955-
SimTK::Real m_squareFiberWidth = SimTK::NaN;
956-
SimTK::Real m_maxContractionVelocityInMetersPerSecond = SimTK::NaN;
957-
// Tendon stiffness parameter from De Groote et al., 2016. Instead of
958-
// kT, users specify tendon strain at 1 norm force, which is more intuitive.
959-
SimTK::Real m_kT = SimTK::NaN;
960992
bool m_isTendonDynamicsExplicit = true;
961993

962994
// Indices for MuscleDynamicsInfo::userDefinedDynamicsExtras.

OpenSim/Actuators/ModelOperators.h

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
* -------------------------------------------------------------------------- */
2020

2121
#include "ModelProcessor.h"
22-
2322
#include <OpenSim/Actuators/ModelFactory.h>
23+
#include <OpenSim/Common/GCVSplineSet.h>
2424
#include <OpenSim/Simulation/Model/ExternalLoads.h>
25+
#include "OpenSim/Simulation/TableProcessor.h"
2526

2627
namespace OpenSim {
2728

@@ -208,6 +209,47 @@ class OSIMACTUATORS_API ModOpReplacePathsWithFunctionBasedPaths
208209
}
209210
};
210211

212+
/// Prescribe motion to Coordinate%s in a model by providing a table containing
213+
/// time series data of Coordinate values. Any columns in the provided table
214+
/// (e.g., "/jointset/ankle_r/ankle_angle_r/value") that do not match a valid
215+
/// path to a Joint Coordinate value in the model will be ignored. A GCVSpline
216+
/// function is created for each column of Coordinate values and this function
217+
/// is assigned to the `prescribed_function` property for the matching Coordinate.
218+
/// In addition, the `prescribed` property for each matching Coordinate is set
219+
/// to "true".
220+
class OSIMACTUATORS_API ModOpPrescribeCoordinateValues : public ModelOperator {
221+
OpenSim_DECLARE_CONCRETE_OBJECT(
222+
ModOpPrescribeCoordinateValues, ModelOperator);
223+
OpenSim_DECLARE_PROPERTY(coordinate_values, TableProcessor,
224+
"The table of coordinate value data to prescribe to the model")
225+
226+
public:
227+
ModOpPrescribeCoordinateValues(TableProcessor table) {
228+
constructProperty_coordinate_values(table);
229+
}
230+
void operate(Model& model, const std::string&) const override {
231+
model.finalizeFromProperties();
232+
TimeSeriesTable table = get_coordinate_values().process();
233+
GCVSplineSet statesSpline(table);
234+
235+
for (const std::string& pathString: table.getColumnLabels()) {
236+
ComponentPath path = ComponentPath(pathString);
237+
if (path.getNumPathLevels() < 3) {
238+
continue;
239+
}
240+
std::string jointPath = path.getParentPath().getParentPath().toString();
241+
if (!model.hasComponent<Joint>(jointPath)) {
242+
log_warn("Found column label '{}', but it does not match a "
243+
"joint coordinate value in the model.", pathString);
244+
continue;
245+
}
246+
Coordinate& q = model.updComponent<Joint>(jointPath).updCoordinate();
247+
q.setPrescribedFunction(statesSpline.get(pathString));
248+
q.setDefaultIsPrescribed(true);
249+
}
250+
}
251+
};
252+
211253
} // namespace OpenSim
212254

213255
#endif // OPENSIM_MODELOPERATORS_H

0 commit comments

Comments
 (0)