1 /* -------------------------------------------------------------------------- *
2  *                     OpenSim:  PrescribedController.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): 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 // INCLUDES
26 //=============================================================================
27 #include "PrescribedController.h"
28 #include <OpenSim/Common/Storage.h>
29 #include <OpenSim/Common/GCVSpline.h>
30 #include <OpenSim/Common/PiecewiseConstantFunction.h>
31 #include <OpenSim/Simulation/Model/Model.h>
32 #include <OpenSim/Simulation/Model/Actuator.h>
33 
34 //=============================================================================
35 // STATICS
36 //=============================================================================
37 
38 // This command indicates that any identifier (class, variable, method, etc.)
39 // defined within the OpenSim namespace can be used in this file without the
40 // "OpenSim::" prefix.
41 using namespace OpenSim;
42 using namespace std;
43 
44 
45 
46 //=============================================================================
47 // CONSTRUCTOR(S) AND DESTRUCTOR
48 //=============================================================================
49 /*
50  * Default constructor.
51  */
PrescribedController()52 PrescribedController::PrescribedController() :
53     Controller()
54 {
55     setNull();
56     constructProperties();
57 }
58 
59 /*
60  * Convenience constructor.
61  */
62 PrescribedController::
PrescribedController(const std::string & controlsFileName,int interpMethodType)63     PrescribedController(const std::string& controlsFileName,
64                          int interpMethodType) : Controller()
65 {
66     setNull();
67     constructProperties();
68     set_controls_file(controlsFileName);
69     set_interpolation_method(interpMethodType);
70 }
71 
72 /*
73  * Destructor.
74  */
~PrescribedController()75 PrescribedController::~PrescribedController()
76 {
77 }
78 
79 /*
80  * Set NULL values for all member variables.
81  */
setNull()82 void PrescribedController::setNull()
83 {
84     setAuthors("Ajay Seth");
85 }
86 
87 //_____________________________________________________________________________
88 /**
89  * Connect properties to local pointers.
90  */
constructProperties()91 void PrescribedController::constructProperties()
92 {
93     constructProperty_ControlFunctions(FunctionSet());
94     constructProperty_controls_file();
95     constructProperty_interpolation_method();
96 }
97 
98 
extendConnectToModel(Model & model)99 void PrescribedController::extendConnectToModel(Model& model)
100 {
101     Super::extendConnectToModel(model);
102     if(!getProperty_controls_file().empty()){
103         Storage controls(get_controls_file());
104         const Array<string>& columns = controls.getColumnLabels();
105 
106         int ncols = columns.getSize();
107 
108         int tcol = columns.findIndex("time");
109         if(tcol < 0){
110             tcol = columns.findIndex("t");
111             if(tcol < 0){
112                 throw Exception("PrescribedController::connectToModel prescribed "
113                 "controls file was not specified as functions of time.",
114                     __FILE__, __LINE__);
115             }
116         }
117         int nrows = controls.getSize();
118         Array<double> time(0.0, nrows);
119         Array<double> data(0.0, nrows);
120         controls.getTimeColumn(time);
121 
122         FunctionSet& controlFuncs = upd_ControlFunctions();
123         const Set<Actuator>& modelActuators = getModel().getActuators();
124 
125         Set<const Actuator>& controllerActuators = updActuators();
126 
127         for(int i=0; i<ncols; ++i){
128             if(i == tcol) continue;
129             const string& actName = columns[i];
130             int found = controlFuncs.getIndex(actName);
131             // if the columns is for a control already part of the set,
132             // or is time, ignore it.
133             if(found < 0){  // not found in the controllers set of functions
134                 // find a corresponding actuator in the model
135                 found = modelActuators.getIndex(actName);
136                 if(found >= 0){ // found a corresponding actuator
137                     controls.getDataColumn(controls.getStateIndex(actName),
138                                             data);
139                     Function* pfunc=createFunctionFromData(actName, time, data);
140                     //if not already assigned to this controller, assign it
141                     int inC = controllerActuators.getIndex(actName);
142                     if(inC >= 0)
143                         prescribeControlForActuator(inC, pfunc);
144                     else{ // add the actuator to the controller's list
145                         updProperty_actuator_list().appendValue(actName);
146                         controllerActuators.adoptAndAppend(&modelActuators[found]);
147                         prescribeControlForActuator(actName, pfunc);
148                     }
149                 }
150                 else{
151                     cout << "PrescribedController::extendConnectToModel() could not "
152                         "find actuator '" << actName << "' in the model." <<endl;
153                 }
154             }// if found in functions, it has already been prescribed
155         }// end looping through columns
156     }// if no controls storage specified, do nothing
157 }
158 
159 
160 // compute the control value for an actuator
computeControls(const SimTK::State & s,SimTK::Vector & controls) const161 void PrescribedController::computeControls(const SimTK::State& s, SimTK::Vector& controls) const
162 {
163     SimTK::Vector actControls(1, 0.0);
164     SimTK::Vector time(1, s.getTime());
165 
166     for(int i=0; i<getActuatorSet().getSize(); i++){
167         actControls[0] = get_ControlFunctions()[i].calcValue(time);
168         getActuatorSet()[i].addInControls(actControls, controls);
169     }
170 }
171 
172 
173 //=============================================================================
174 // GET AND SET
175 //=============================================================================
176 
177 void PrescribedController::
prescribeControlForActuator(int index,Function * prescribedFunction)178     prescribeControlForActuator(int index, Function *prescribedFunction)
179 {
180     OPENSIM_THROW_IF_FRMOBJ(index < 0,
181             Exception, "Index was " + std::to_string(index) +
182                        " but must be nonnegative." );
183     OPENSIM_THROW_IF(index >= getActuatorSet().getSize(),
184             IndexOutOfRange, (size_t)index, 0,
185             (size_t)getActuatorSet().getSize() - 1);
186 
187     if(index >= get_ControlFunctions().getSize())
188         upd_ControlFunctions().setSize(index+1);
189     upd_ControlFunctions().set(index, prescribedFunction);
190 }
191 
192 void PrescribedController::
prescribeControlForActuator(const std::string actName,Function * prescribedFunction)193     prescribeControlForActuator(const std::string actName,
194                                 Function *prescribedFunction)
195 {
196     int index = getProperty_actuator_list().findIndex(actName);
197     if(index < 0 )
198         throw Exception("PrescribedController does not have "+actName+" in its list of actuators to control.");
199     prescribeControlForActuator(index, prescribedFunction);
200 }
201 
202 // utility
createFunctionFromData(const std::string & name,const Array<double> & time,const Array<double> & data)203 Function* PrescribedController::createFunctionFromData(const std::string& name,
204                         const Array<double>& time, const Array<double>& data)
205 {
206     int method = 1;
207     if(!getProperty_interpolation_method().empty())
208         method = get_interpolation_method();
209 
210     if(method > 0)
211         return new GCVSpline(method, time.getSize(), &time[0], &data[0], name);
212     else if(method ==0)
213         return new PiecewiseConstantFunction(time.getSize(),
214                                                     &time[0], &data[0], name);
215     else
216         throw Exception("PrescribedController- Invalid interpolation method.");
217 }
218