NTRT Simulator  v1.1
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
EscapeController.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2012, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
7  * under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * http://www.apache.org/licenses/LICENSE-2.0.
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15  * either express or implied. See the License for the specific language
16  * governing permissions and limitations under the License.
17  */
18 
27 // This module
28 #include "EscapeController.h"
29 // This application
30 #include "EscapeModel.h"
31 // This library
32 #include "core/tgBasicActuator.h"
33 // For AnnealEvolution
37 // File helpers to use resources folder
38 #include "helpers/FileHelpers.h"
39 // The C++ Standard Library
40 #include <cassert>
41 #include <cmath>
42 #include <stdexcept>
43 #include <vector>
44 #include <string>
45 
46 # define M_PI 3.14159265358979323846
47 
48 using namespace std;
49 
50 //Constructor using the model subject and a single pref length for all muscles.
51 //Currently calibrated to decimeters
52 EscapeController::EscapeController(const double initialLength,
53  std::string args,
54  std::string resourcePath,
55  std::string config) :
56  m_initialLengths(initialLength),
57  m_totalTime(0.0),
58  maxStringLengthFactor(0.50),
59  nClusters(8),
60  musclesPerCluster(3),
61  suffix(args),
62  configPath(resourcePath),
63  configName(config)
64 {
65  clusters.resize(nClusters);
66  for (int i=0; i<nClusters; i++) {
67  clusters[i].resize(musclesPerCluster);
68  }
69 }
70 
73 {
74  m_totalTime = 0;
75  double dt = 0.0001;
76 
77  //Set the initial length of every muscle in the subject
78  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
79  for (size_t i = 0; i < muscles.size(); ++i) {
80  tgBasicActuator * const pMuscle = muscles[i];
81  assert(pMuscle != NULL);
82  pMuscle->setControlInput(this->m_initialLengths, dt);
83  }
84 
85  populateClusters(subject);
86  initPosition = subject.getBallCOM();
87  setupAdapter();
88  initializeSineWaves(); // For muscle actuation
89 
90  vector<double> state; // For config file usage (including Monte Carlo simulations)
91 
92  //get the actions (between 0 and 1) from evolution (todo)
93  actions = evolutionAdapter.step(dt,state);
94 
95  //transform them to the size of the structure
96  actions = transformActions(actions);
97 
98  //apply these actions to the appropriate muscles according to the sensor values
99  applyActions(subject,actions);
100 }
101 
102 void EscapeController::onStep(EscapeModel& subject, double dt)
103 {
104  if (dt <= 0.0) {
105  throw std::invalid_argument("dt is not positive");
106  }
107  m_totalTime+=dt;
108 
109  setPreferredMuscleLengths(subject, dt);
110  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
111 
112  //Move motors for all the muscles
113  for (size_t i = 0; i < muscles.size(); ++i)
114  {
115  tgBasicActuator * const pMuscle = muscles[i];
116  assert(pMuscle != NULL);
117  pMuscle->moveMotors(dt);
118  }
119 }
120 
121 // So far, only score used for eventual fitness calculation of an Escape Model
122 // is the maximum distance from the origin reached during that subject's episode
124  std::vector<double> scores; //scores[0] == displacement, scores[1] == energySpent
125  double distance = displacement(subject);
126  double energySpent = totalEnergySpent(subject);
127 
128  //Invariant: For now, scores must be of size 2 (as required by endEpisode())
129  scores.push_back(distance);
130  scores.push_back(energySpent);
131 
132  std::cout << "Tearing down" << std::endl;
133  evolutionAdapter.endEpisode(scores);
134 
135  // If any of subject's dynamic objects need to be freed, this is the place to do so
136 }
137 
144 vector< vector <double> > EscapeController::transformActions(vector< vector <double> > actions)
145 {
146  bool usingManualParams = false;
147  vector <double> manualParams(4 * nClusters, 1); // '4' for the number of sine wave parameters
148  if (usingManualParams) {
149  std::cout << "Using manually set parameters\n";
150  string filename = "logs/paramSortedBestTrials.dat";
151  int lineNumber = 1;
152  manualParams = readManualParams(lineNumber, filename);
153  }
154 
155  double pretension = 0.90; // Tweak this value if need be
156  // Minimum amplitude, angularFrequency, phaseChange, and dcOffset
157  double mins[4] = {m_initialLengths * (pretension - maxStringLengthFactor),
158  0.3, //Hz
159  -1 * M_PI,
160  m_initialLengths};// * (1 - maxStringLengthFactor)};
161 
162  // Maximum amplitude, angularFrequency, phaseChange, and dcOffset
163  double maxes[4] = {m_initialLengths * (pretension + maxStringLengthFactor),
164  20, //Hz (can cheat to 50Hz, if feeling immoral)
165  M_PI,
166  m_initialLengths};// * (1 + maxStringLengthFactor)};
167  double ranges[4] = {maxes[0]-mins[0], maxes[1]-mins[1], maxes[2]-mins[2], maxes[3]-mins[3]};
168 
169  for(int i=0;i<actions.size();i++) { //8x
170  for (int j=0; j<actions[i].size(); j++) { //4x
171  if (usingManualParams) {
172  actions[i][j] = manualParams[i*actions[i].size() + j]*(ranges[j])+mins[j];
173  } else {
174  actions[i][j] = actions[i][j]*(ranges[j])+mins[j];
175  }
176  }
177  }
178  return actions;
179 }
180 
184 void EscapeController::applyActions(EscapeModel& subject, vector< vector <double> > actions)
185 {
186  assert(actions.size() == clusters.size());
187 
188  // Apply actions by cluster
189  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
190  amplitude[cluster] = actions[cluster][0];
191  angularFrequency[cluster] = actions[cluster][1];
192  phaseChange[cluster] = actions[cluster][2];
193  dcOffset[cluster] = actions[cluster][3];
194  dcOffset[cluster] = 1;
195  }
196  //printSineParams();
197 }
198 
199 void EscapeController::setupAdapter() {
200  //string suffix = "_Escape";
201 
202  std::string path;
203  if (configPath != "")
204  {
205  path = FileHelpers::getResourcePath(configPath);
206  }
207  else
208  {
209  path = "";
210  }
211 
212  string configAnnealEvolution = path + configName;
213  AnnealEvolution* evo = new AnnealEvolution(suffix, configName, configPath);
214  bool isLearning = true;
215  configuration configEvolutionAdapter;
216  configEvolutionAdapter.readFile(configAnnealEvolution);
217 
218  evolutionAdapter.initialize(evo, isLearning, configEvolutionAdapter);
219 }
220 
221 double EscapeController::totalEnergySpent(EscapeModel& subject) {
222  double totalEnergySpent=0;
223 
224  vector<tgBasicActuator* > tmpStrings = subject.getAllMuscles();
225  for(int i=0; i<tmpStrings.size(); i++)
226  {
227  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
228 
229  for(int j=1; j<stringHist.tensionHistory.size(); j++)
230  {
231  const double previousTension = stringHist.tensionHistory[j-1];
232  const double previousLength = stringHist.restLengths[j-1];
233  const double currentLength = stringHist.restLengths[j];
234  //TODO: examine this assumption - free spinning motor may require more power
235  double motorSpeed = (currentLength-previousLength);
236  if(motorSpeed > 0) // Vestigial code
237  motorSpeed = 0;
238  const double workDone = previousTension * motorSpeed;
239  totalEnergySpent += workDone;
240  }
241  }
242  return totalEnergySpent;
243 }
244 
245 // Pre-condition: every element in muscles must be defined
246 // Post-condition: every muscle will have a new target length
247 void EscapeController::setPreferredMuscleLengths(EscapeModel& subject, double dt) {
248  double phase = 0; // Phase of cluster1
249 
250  int nMuscles = 24;
251  int oldCluster = 0;
252  int cluster = 0;
253 
254  for(int iMuscle=0; iMuscle < nMuscles; iMuscle++) {
255 
256  const vector<tgBasicActuator*> muscles = subject.getAllMuscles();
257  tgBasicActuator *const pMuscle = muscles[iMuscle];
258 
259  assert(pMuscle != NULL);
260 
261  // Determine cluster
262  oldCluster = cluster;
263  if (iMuscle < 4) {
264  cluster = 0;
265  } else if (iMuscle < 8) {
266  cluster = 1;
267  } else if (iMuscle < 12) {
268  cluster = 2;
269  } else if (iMuscle < 16 ) {
270  cluster = 3;
271  } else if (iMuscle < 18) {
272  cluster = 4;
273  } else if (iMuscle < 20) {
274  cluster = 5;
275  } else if (iMuscle < 22) {
276  cluster = 6;
277  } else { // iMuscle < 24
278  cluster = 7;
279  }
280 
281  double newLength = amplitude[cluster] * sin(angularFrequency[cluster] * m_totalTime + phase) + dcOffset[cluster];
282  double minLength = m_initialLengths * (1-maxStringLengthFactor);
283  double maxLength = m_initialLengths * (1+maxStringLengthFactor);
284  if (newLength <= minLength) {
285  newLength = minLength;
286  } else if (newLength >= maxLength) {
287  newLength = maxLength;
288  }
289  pMuscle->setControlInput(newLength, dt);
290  if (oldCluster != cluster) {
291  phase += phaseChange[cluster];
292  }
293  }
294 
295  /*
296  for(int cluster=0; cluster<nClusters; cluster++) {
297  for(int node=0; node<musclesPerCluster; node++) {
298  tgBasicActuator *const pMuscle = clusters[cluster][node];
299  assert(pMuscle != NULL);
300  double newLength = amplitude[cluster] * sin(angularFrequency[cluster] * m_totalTime + phase) + dcOffset[cluster];
301  double minLength = m_initialLengths * (1-maxStringLengthFactor);
302  double maxLength = m_initialLengths * (1+maxStringLengthFactor);
303  if (newLength <= minLength) {
304  newLength = minLength;
305  } else if (newLength >= maxLength) {
306  newLength = maxLength;
307  }
308  pMuscle->setControlInput(newLength, dt);
309  }
310  phase += phaseChange[cluster];
311  }*/
312 }
313 
314 void EscapeController::populateClusters(EscapeModel& subject) {
315  for(int cluster=0; cluster < nClusters; cluster++) {
316  ostringstream ss;
317  ss << (cluster + 1);
318  string suffix = ss.str();
319  std::vector <tgBasicActuator*> musclesInThisCluster = subject.find<tgBasicActuator>("muscle cluster" + suffix);
320  clusters[cluster] = std::vector<tgBasicActuator*>(musclesInThisCluster);
321  }
322 }
323 
324 void EscapeController::initializeSineWaves() {
325  amplitude = new double[nClusters];
326  angularFrequency = new double[nClusters];
327  phaseChange = new double[nClusters]; // Does not use last value stored in array
328  dcOffset = new double[nClusters];
329 }
330 
331 double EscapeController::displacement(EscapeModel& subject) {
332  vector<double> finalPosition = subject.getBallCOM();
333 
334  // 'X' and 'Z' are irrelevant. Both variables measure lateral direction
335  //assert(finalPosition[0] > 0); //Negative y-value indicates a flaw in the simulator that run (tensegrity went 'underground')
336 
337  const double newX = finalPosition[0];
338  const double newZ = finalPosition[2];
339  const double oldX = initPosition[0];
340  const double oldZ = initPosition[2];
341 
342  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
343  (newZ-oldZ) * (newZ-oldZ));
344  return distanceMoved;
345 }
346 
347 std::vector<double> EscapeController::readManualParams(int lineNumber, string filename) {
348  assert(lineNumber > 0);
349  vector<double> result(32, 1.0);
350  string line;
351  ifstream infile(filename.c_str(), ifstream::in);
352 
353  // Grab line from input file
354  if (infile.is_open()) {
355  cout << "OPENED FILE\n";
356  for (int i=0; i<lineNumber; i++) {
357  getline(infile, line);
358  }
359  infile.close();
360  } else {
361  cerr << "Error: Manual Parameters file not found\n";
362  exit(1);
363  }
364 
365  //cout << "Using: " << line << " as input for starting parameter values\n";
366 
367  // Split line into parameters
368  stringstream lineStream(line);
369  string cell;
370  int iCell = 0;
371  while(getline(lineStream,cell,',')) {
372  result[iCell] = atof(cell.c_str());
373  iCell++;
374  }
375 
376  bool tweaking = false;
377  if (tweaking) {
378  // Tweak each read-in parameter by as much as 0.5% (params range: [0,1])
379  for (int i=0; i < result.size(); i++) {
380  std::cout<<"Entered Cell " << i << ": " << result[i] << "\n";
381  double seed = ((double) (rand() % 100)) / 100;
382  result[i] += (0.01 * seed) - 0.005; // Value +/- 0.005 of original
383  }
384  } else {
385  cerr << "WARNING: Not changing manual input parameters\n";
386  }
387 
388  return result;
389 }
390 
391 void EscapeController::printSineParams() {
392  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
393  std::cout << "amplitude[" << cluster << "]: " << amplitude[cluster] << std::endl;
394  std::cout << "angularFrequency[" << cluster << "]: " << angularFrequency[cluster] << std::endl;
395  std::cout << "phaseChange[" << cluster << "]: " << phaseChange[cluster] << std::endl;
396  std::cout << "dcOffset[" << cluster << "]: " << dcOffset[cluster] << std::endl;
397  }
398 }
399 
Contains the definition of class EscapeModel. $Id$.
virtual void moveMotors(double dt)
virtual void onSetup(EscapeModel &subject)
virtual void onTeardown(EscapeModel &subject)
virtual void applyActions(EscapeModel &subject, vector< vector< double > > act)
virtual void setControlInput(double input)
A class to read a learning configuration from a .ini file.
A series of functions to assist with file input/output.
Contains the definition of class AnnealEvolution. Adapting NeuroEvolution to do Simulated Annealing...
Contains the definition of class tgBasicActuator.
virtual vector< vector< double > > transformActions(vector< vector< double > > act)
const std::vector< tgBasicActuator * > & getAllMuscles() const
std::vector< T * > find(const tgTagSearch &tagSearch)
Definition: tgModel.h:125
Defines a class AnnealAdapter to pass parameters from AnnealEvolution to a controller. Adapting NeuroEvolution to do Simulated Annealing.
virtual void onStep(EscapeModel &subject, double dt)
void initialize(AnnealEvolution *evo, bool isLearning, configuration config)
Contains the definition of class EscapeController.
std::vector< double > getBallCOM()