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