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