1 /* -------------------------------------------------------------------------- *
2 * OpenSim: OptimizationExample.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Samuel R. Hamner, Ajay Seth *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24 /*
25 * Example of an OpenSim program that optimizes the performance of a model.
26 * The main() loads the arm26 model and maximizes the forward velocity of
27 * the hand during a muscle-driven forward simulation by finding the set
28 * of (constant) controls.
29 */
30
31 //==============================================================================
32 //==============================================================================
33 #include <OpenSim/OpenSim.h>
34 #include "OpenSim/Common/STOFileAdapter.h"
35 #include <ctime> // clock(), clock_t, CLOCKS_PER_SEC
36
37 using namespace OpenSim;
38 using namespace SimTK;
39 using namespace std;
40
41 // Global variables to define integration time window, optimizer step count,
42 // the best solution.
43 int stepCount = 0;
44 const double initialTime = 0.0;
45 const double finalTime = 0.25;
46 const double desired_accuracy = 1.0e-5;
47 double bestSoFar = Infinity;
48
49 class ExampleOptimizationSystem : public OptimizerSystem {
50 public:
51 /* Constructor class. Parameters passed are accessed in the objectiveFunc() class. */
ExampleOptimizationSystem(int numParameters,State & s,Model & aModel)52 ExampleOptimizationSystem(int numParameters, State& s, Model& aModel):
53 OptimizerSystem(numParameters),
54 numControls(numParameters),
55 si(s),
56 osimModel(aModel)
57 {}
58
objectiveFunc(const Vector & newControls,bool new_coefficients,Real & f) const59 int objectiveFunc(const Vector &newControls,
60 bool new_coefficients, Real& f) const override {
61
62 // make a copy of the initial states
63 State s = si;
64
65 // Update the control values
66 osimModel.updDefaultControls() = newControls;
67
68 // Integrate from initial time to final time
69 Manager manager(osimModel);
70 manager.setIntegratorAccuracy(desired_accuracy);
71 s.setTime(initialTime);
72
73 osimModel.getMultibodySystem().realize(s, Stage::Acceleration);
74
75 manager.initialize(s);
76 s = manager.integrate(finalTime);
77
78 /* Calculate the scalar quantity we want to minimize or maximize.
79 * In this case, we�re maximizing forward velocity of the
80 * forearm/hand mass center, so to maximize, compute velocity
81 * and multiply it by -1.
82 */
83 const auto& hand = osimModel.getBodySet().get("r_ulna_radius_hand");
84 osimModel.getMultibodySystem().realize(s, Stage::Velocity);
85 Vec3 massCenter = hand.getMassCenter();
86 Vec3 velocity = hand.findStationVelocityInGround(s, massCenter);
87 f = -velocity[0];
88 stepCount++;
89
90 // Use an if statement to only store and print the results of an
91 // optimization step if it is better than a previous result.
92 if(f < bestSoFar) {
93 bestSoFar = f;
94 cout << "\nobjective evaluation #: " << stepCount << " controls = "
95 << newControls << " bestSoFar = " << f << std::endl;
96 }
97
98 return 0;
99
100 }
101
102 private:
103 int numControls;
104 State& si;
105 Model& osimModel;
106 SimTK::ReferencePtr<RungeKuttaMersonIntegrator> p_integrator;
107
108 };
109
110 //______________________________________________________________________________
111 /**
112 * Define an optimization problem that finds a set of muscle controls to maximize
113 * the forward velocity of the forearm/hand segment mass center.
114 */
main()115 int main()
116 {
117 try {
118 std::clock_t startTime = std::clock();
119
120 // Use Millard2012Equilibrium muscles with rigid tendons for better
121 // performance.
122 Object::renameType("Thelen2003Muscle", "Millard2012EquilibriumMuscle");
123
124 // Create a new OpenSim model. This model is similar to the arm26 model,
125 // but without wrapping surfaces for better performance.
126 Model osimModel("Arm26_Optimize.osim");
127
128 // Initialize the system and get the state representing the state system
129 State& si = osimModel.initSystem();
130
131 // Initialize the starting shoulder angle.
132 const CoordinateSet& coords = osimModel.getCoordinateSet();
133 coords.get("r_shoulder_elev").setValue(si, -1.57079633);
134
135 // Set the initial muscle activations and make all tendons rigid.
136 const Set<Muscle> &muscleSet = osimModel.getMuscles();
137 for(int i=0; i<muscleSet.getSize(); ++i) {
138 muscleSet[i].setActivation(si, 0.01);
139 muscleSet[i].setIgnoreTendonCompliance(si, true);
140 }
141
142 // Make sure the muscles states are in equilibrium
143 osimModel.equilibrateMuscles(si);
144
145 // The number of controls will equal the number of muscles in the model!
146 int numControls = osimModel.getNumControls();
147
148 // Initialize the optimizer system we've defined.
149 ExampleOptimizationSystem sys(numControls, si, osimModel);
150 Real f = NaN;
151
152 /* Define initial values and bounds for the controls to optimize */
153 Vector controls(numControls, 0.02);
154 controls[3] = 0.99;
155 controls[4] = 0.99;
156 controls[5] = 0.99;
157
158 Vector lower_bounds(numControls, 0.01);
159 Vector upper_bounds(numControls, 0.99);
160
161 sys.setParameterLimits( lower_bounds, upper_bounds );
162
163 // Create an optimizer. Pass in our OptimizerSystem
164 // and the name of the optimization algorithm.
165 Optimizer opt(sys, SimTK::LBFGSB);
166 //Optimizer opt(sys, InteriorPoint);
167
168 // Specify settings for the optimizer
169 opt.setConvergenceTolerance(0.1);
170 opt.useNumericalGradient(true, desired_accuracy);
171 opt.setMaxIterations(2);
172 opt.setLimitedMemoryHistory(500);
173
174 // Optimize it!
175 f = opt.optimize(controls);
176
177 cout << "Elapsed time = " << (std::clock()-startTime)/CLOCKS_PER_SEC << "s" << endl;
178
179 const Set<Actuator>& actuators = osimModel.getActuators();
180 for(int i=0; i<actuators.getSize(); ++i){
181 cout << actuators[i].getName() << " control value = " << controls[i] << endl;
182 }
183
184 cout << "\nMaximum hand velocity = " << -f << "m/s" << endl;
185
186 cout << "OpenSim example completed successfully." << endl;
187
188 // Dump out optimization results to a text file for testing
189 ofstream ofile;
190 ofile.open("Arm26_optimization_result");
191 for(int i=0; i<actuators.getSize(); ++i)
192 ofile << controls[i] << endl;
193 ofile << -f <<endl;
194 ofile.close();
195
196 // Re-run simulation with optimal controls.
197 Manager manager(osimModel);
198 manager.setIntegratorAccuracy(desired_accuracy);
199 osimModel.updDefaultControls() = controls;
200
201 // Integrate from initial time to final time.
202 si.setTime(initialTime);
203 osimModel.getMultibodySystem().realize(si, Stage::Acceleration);
204 manager.initialize(si);
205 si = manager.integrate(finalTime);
206
207 auto statesTable = manager.getStatesTable();
208 STOFileAdapter_<double>::write(statesTable,
209 "Arm26_optimized_states.sto");
210 }
211 catch (const std::exception& ex)
212 {
213 std::cout << ex.what() << std::endl;
214 return 1;
215 }
216
217 // End of main() routine.
218 return 0;
219 }
220