This example optimizes the motion of a point mass actuated by a single muscle and shows you how to use the AnalyzeTool and a metabolic probe with a MocoSolution.
#include <OpenSim/Analyses/MuscleAnalysis.h>
#include <OpenSim/Analyses/ProbeReporter.h>
#include <OpenSim/Common/STOFileAdapter.h>
#include <OpenSim/Moco/osimMoco.h>
#include <OpenSim/Simulation/SimbodyEngine/PinJoint.h>
#include <OpenSim/Tools/AnalyzeTool.h>
Model createHangingMuscleModel(bool ignoreActivationDynamics,
bool ignoreTendonCompliance) {
Model model;
model.setName("hanging_muscle");
model.set_gravity(SimTK::Vec3(9.81, 0, 0));
auto* body = new Body("body", 0.5, SimTK::Vec3(0), SimTK::Inertia(0));
model.addComponent(body);
auto* joint = new SliderJoint("joint", model.getGround(), *body);
coord.setName("height");
model.addComponent(joint);
auto* actu = new DeGrooteFregly2016Muscle();
actu->setName("muscle");
actu->set_max_isometric_force(30.0);
actu->set_optimal_fiber_length(0.10);
actu->set_tendon_slack_length(0.05);
actu->set_tendon_strain_at_one_norm_force(0.10);
actu->set_ignore_activation_dynamics(ignoreActivationDynamics);
actu->set_ignore_tendon_compliance(ignoreTendonCompliance);
actu->set_fiber_damping(0.01);
actu->set_tendon_compliance_dynamics_mode("implicit");
actu->set_max_contraction_velocity(10);
actu->set_pennation_angle_at_optimal(0.10);
actu->addNewPathPoint("origin", model.updGround(), SimTK::Vec3(0));
actu->addNewPathPoint("insertion", *body, SimTK::Vec3(0));
model.addForce(actu);
{
auto* probe = new Umberger2010MuscleMetabolicsProbe();
probe->setName("metabolics");
probe->addMuscle("muscle", 0.5);
model.addProbe(probe);
}
{
auto* probe = new Umberger2010MuscleMetabolicsProbe();
probe->setName("activation_maintenance_rate");
probe->set_activation_maintenance_rate_on(true);
probe->set_shortening_rate_on(false);
probe->set_basal_rate_on(false);
probe->set_mechanical_work_rate_on(false);
probe->addMuscle("muscle", 0.5);
model.addProbe(probe);
}
{
auto* probe = new Umberger2010MuscleMetabolicsProbe();
probe->setName("shortening_rate");
probe->set_activation_maintenance_rate_on(false);
probe->set_shortening_rate_on(true);
probe->set_basal_rate_on(false);
probe->set_mechanical_work_rate_on(false);
probe->addMuscle("muscle", 0.5);
model.addProbe(probe);
}
{
auto* probe = new Umberger2010MuscleMetabolicsProbe();
probe->setName("basal_rate");
probe->set_activation_maintenance_rate_on(false);
probe->set_shortening_rate_on(false);
probe->set_basal_rate_on(true);
probe->set_mechanical_work_rate_on(false);
probe->addMuscle("muscle", 0.5);
model.addProbe(probe);
}
{
auto* probe = new Umberger2010MuscleMetabolicsProbe();
probe->setName("mechanical_work_rate");
probe->set_activation_maintenance_rate_on(false);
probe->set_shortening_rate_on(false);
probe->set_basal_rate_on(false);
probe->set_mechanical_work_rate_on(true);
probe->addMuscle("muscle", 0.5);
model.addProbe(probe);
}
body->attachGeometry(new Sphere(0.05));
model.finalizeConnections();
return model;
}
int main() {
const bool ignoreActivationDynamics = false;
const bool ignoreTendonCompliance = false;
Model model = createHangingMuscleModel(ignoreActivationDynamics,
ignoreTendonCompliance);
model.print("hanging_muscle.osim");
MocoStudy study;
MocoProblem& problem = study.updProblem();
problem.setModelAsCopy(model);
problem.setTimeBounds(0, {0.05, 1.0});
problem.setStateInfo("/joint/height/value", {0.14, 0.16}, 0.15, 0.14);
problem.setStateInfo("/joint/height/speed", {-1, 1}, 0, 0);
problem.setControlInfo("/forceset/muscle", {0.01, 1});
if (!ignoreActivationDynamics) {
auto* initial_activation = problem.addGoal<MocoInitialActivationGoal>();
initial_activation->setName("initial_activation");
}
if (!ignoreTendonCompliance) {
auto* initial_equilibrium =
problem.addGoal<MocoInitialVelocityEquilibriumDGFGoal>();
initial_equilibrium->setName("initial_velocity_equilibrium");
initial_equilibrium->setMode("cost");
initial_equilibrium->setWeight(0.001);
}
problem.addGoal<MocoFinalTimeGoal>();
auto& solver = study.initCasADiSolver();
solver.set_num_mesh_intervals(50);
solver.set_multibody_dynamics_mode("implicit");
solver.set_optim_convergence_tolerance(1e-4);
solver.set_optim_constraint_tolerance(1e-4);
MocoSolution solution = study.solve();
"exampleHangingMuscle_states.sto");
"exampleHangingMuscle_controls.sto");
{
analyze.setModelFilename(
"hanging_muscle.osim");
analyze.setStatesFileName(
"exampleHangingMuscle_states.sto");
analyze.updAnalysisSet().adoptAndAppend(
new MuscleAnalysis());
analyze.updAnalysisSet().adoptAndAppend(
new ProbeReporter());
analyze.updControllerSet().adoptAndAppend(
new PrescribedController("exampleHangingMuscle_controls.sto"));
analyze.print(
"exampleHangingMuscle_AnalyzeTool_setup.xml");
}
AnalyzeTool
analyze(
"exampleHangingMuscle_AnalyzeTool_setup.xml");
return EXIT_SUCCESS;
}
static void write(const TimeSeriesTable_< T > &table, const std::string &fileName)
Write a STO file.
Definition: STOFileAdapter.h:139
TimeSeriesTable_< T > analyze(Model model, const TimeSeriesTable &statesTable, const TimeSeriesTable &controlsTable, const std::vector< std::string > &outputPaths)
Calculate the requested outputs using the model in the problem and the provided states and controls t...
Definition: SimulationUtilities.h:216
The Moco interface is contained within the OpenSim namespace.
Definition: ActivationCoordinateActuator.h:30