NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
Escape_T6Controller.cpp
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 "Escape_T6Controller.h"
29 // This application
30 #include "Escape_T6Model.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 Escape_T6Controller::Escape_T6Controller(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 // Set target length of each muscle, then move motors accordingly
101 void Escape_T6Controller::onStep(Escape_T6Model& subject, double dt)
102 {
103  if (dt <= 0.0) {
104  throw std::invalid_argument("dt is not positive");
105  }
106  m_totalTime+=dt;
107 
108  setPreferredMuscleLengths(subject, dt);
109  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
110 
111  //Move motors for all the muscles
112  for (size_t i = 0; i < muscles.size(); ++i) {
113  tgBasicActuator * const pMuscle = muscles[i];
114  assert(pMuscle != NULL);
115  pMuscle->moveMotors(dt);
116  }
117 
118  for(size_t i=0; i<muscles.size(); i++) {
119  std::vector<double> tmp;
120  for(int j=0;j<2;j++) {
121  tmp.push_back(0.5);
122  }
123  actions.push_back(tmp);
124  }
125 
126  /*static int count = 0;
127  if(count > 100) {
128  std::cout << m_totalTime << " ";
129  printCOM(subject);
130  printMuscleTensions(subject);
131  printMuscleLengths(subject);
132  count = 0;
133  }
134  else {
135  count++;
136  }*/
137 }
138 
139 // So far, only score used for eventual fitness calculation of an Escape Model
140 // is the maximum distance from the origin reached during that subject's episode
142  std::vector<double> scores; //scores[0] == displacement, scores[1] == energySpent
143  double distance = displacement(subject);
144  double energySpent = totalEnergySpent(subject);
145 
146  //Invariant: For now, scores must be of size 2 (as required by endEpisode())
147  scores.push_back(distance);
148  scores.push_back(energySpent);
149 
150  std::cout << "Tearing down" << std::endl;
151  evolutionAdapter.endEpisode(scores);
152 
153  // If any of subject's dynamic objects need to be freed, this is the place to do so
154 }
155 
162 std::vector< std::vector <double> > Escape_T6Controller::transformActions(std::vector< std::vector <double> > actions)
163 {
164  bool usingManualParams = false;
165  std::vector <double> manualParams(4 * nClusters, 1); // '4' for the number of sine wave parameters
166  if (usingManualParams) {
167  std::cout << "Using manually set parameters\n";
168  std::string filename = "logs/Jan242015/params.dat";
169  int lineNumber = 1;
170  manualParams = readManualParams(lineNumber, filename);
171  }
172 
173  double pretension = 0.90; // Tweak this value if need be
174  // Minimum amplitude, angularFrequency, phaseChange, and dcOffset
175  double mins[4] = {m_initialLengths * (pretension - maxStringLengthFactor),
176  0.3, //Hz
177  -1 * M_PI,
178  m_initialLengths};// * (1 - maxStringLengthFactor)};
179 
180  // Maximum amplitude, angularFrequency, phaseChange, and dcOffset
181  double maxes[4] = {m_initialLengths * (pretension + maxStringLengthFactor),
182  20, //Hz (can cheat to 50Hz, if feeling immoral)
183  M_PI,
184  m_initialLengths};// * (1 + maxStringLengthFactor)};
185  double ranges[4] = {maxes[0]-mins[0], maxes[1]-mins[1], maxes[2]-mins[2], maxes[3]-mins[3]};
186 
187  for(size_t i=0;i<actions.size();i++) { //8x
188  for (size_t j=0; j<actions[i].size(); j++) { //4x
189  if (usingManualParams) {
190  actions[i][j] = manualParams[i*actions[i].size() + j]*(ranges[j])+mins[j];
191  } else {
192  actions[i][j] = actions[i][j]*(ranges[j])+mins[j];
193  }
194  }
195  }
196  return actions;
197 }
198 
202 void Escape_T6Controller::applyActions(Escape_T6Model& subject, std::vector< std::vector <double> > actions) {
203  assert(actions.size() == clusters.size());
204 
205  // Apply actions by cluster
206  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
207  amplitude[cluster] = actions[cluster][0];
208  angularFrequency[cluster] = actions[cluster][1];
209  phaseChange[cluster] = actions[cluster][2];
210  dcOffset[cluster] = actions[cluster][3];
211  dcOffset[cluster] = 1;
212  }
213  //printSineParams();
214 }
215 
216 void Escape_T6Controller::setupAdapter() {
217  //std::string suffix = "_Escape";
218 
219  std::string path;
220  if (configPath != "") {
221  path = FileHelpers::getResourcePath(configPath);
222  }
223  else {
224  path = "";
225  }
226 
227  std::string configAnnealEvolution = path + configName;
228  AnnealEvolution* evo = new AnnealEvolution(suffix, configName, configPath);
229  bool isLearning = true;
230  configuration configEvolutionAdapter;
231  configEvolutionAdapter.readFile(configAnnealEvolution);
232 
233  evolutionAdapter.initialize(evo, isLearning, configEvolutionAdapter);
234 }
235 
236 double Escape_T6Controller::totalEnergySpent(Escape_T6Model& subject) {
237  double totalEnergySpent=0;
238 
239  std::vector<tgBasicActuator* > tmpStrings = subject.getAllMuscles();
240  for(size_t i=0; i<tmpStrings.size(); i++) {
241  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
242 
243  for(size_t j=1; j<stringHist.tensionHistory.size(); j++) {
244  const double previousTension = stringHist.tensionHistory[j-1];
245  const double previousLength = stringHist.restLengths[j-1];
246  const double currentLength = stringHist.restLengths[j];
247  //TODO: examine this assumption - free spinning motor may require more power
248  double motorSpeed = (currentLength-previousLength);
249  if(motorSpeed > 0) // Vestigial code
250  motorSpeed = 0;
251  const double workDone = previousTension * motorSpeed;
252  totalEnergySpent += workDone;
253  }
254  }
255  return totalEnergySpent;
256 }
257 
258 // Pre-condition: every element in muscles must be defined
259 // Post-condition: every muscle will have a new target length
260 void Escape_T6Controller::setPreferredMuscleLengths(Escape_T6Model& subject, double dt) {
261  double phase = 0; // Phase of cluster1
262 
263  int nMuscles = 24;
264  int oldCluster = 0;
265  int cluster = 0;
266 
267  for(int iMuscle=0; iMuscle < nMuscles; iMuscle++) {
268 
269  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
270  tgBasicActuator *const pMuscle = muscles[iMuscle];
271 
272  assert(pMuscle != NULL);
273 
274  // Determine cluster
275  oldCluster = cluster;
276  if (iMuscle < 4) {
277  cluster = 0;
278  } else if (iMuscle < 8) {
279  cluster = 1;
280  } else if (iMuscle < 12) {
281  cluster = 2;
282  } else if (iMuscle < 16 ) {
283  cluster = 3;
284  } else if (iMuscle < 18) {
285  cluster = 4;
286  } else if (iMuscle < 20) {
287  cluster = 5;
288  } else if (iMuscle < 22) {
289  cluster = 6;
290  } else { // iMuscle < 24
291  cluster = 7;
292  }
293 
294  double newLength = amplitude[cluster] * sin(angularFrequency[cluster] * m_totalTime + phase) + dcOffset[cluster];
295  double minLength = m_initialLengths * (1-maxStringLengthFactor);
296  double maxLength = m_initialLengths * (1+maxStringLengthFactor);
297  if (newLength <= minLength) {
298  newLength = minLength;
299  } else if (newLength >= maxLength) {
300  newLength = maxLength;
301  }
302  pMuscle->setControlInput(newLength, dt);
303  if (oldCluster != cluster) {
304  phase += phaseChange[cluster];
305  }
306  }
307 }
308 
309 void Escape_T6Controller::populateClusters(Escape_T6Model& subject) {
310  for(int cluster=0; cluster < nClusters; cluster++) {
311  std::ostringstream ss;
312  ss << (cluster + 1);
313  std::string suffix = ss.str();
314  std::vector <tgBasicActuator*> musclesInThisCluster = subject.find<tgBasicActuator>("muscle cluster" + suffix);
315  clusters[cluster] = std::vector<tgBasicActuator*>(musclesInThisCluster);
316  }
317 }
318 
319 void Escape_T6Controller::initializeSineWaves() {
320  amplitude = new double[nClusters];
321  angularFrequency = new double[nClusters];
322  phaseChange = new double[nClusters]; // Does not use last value stored in array
323  dcOffset = new double[nClusters];
324 }
325 
326 double Escape_T6Controller::displacement(Escape_T6Model& subject) {
327  std::vector<double> finalPosition = subject.getBallCOM();
328 
329  // 'X' and 'Z' are irrelevant. Both variables measure lateral direction
330  //assert(finalPosition[0] > 0); //Negative y-value indicates a flaw in the simulator that run (tensegrity went 'underground')
331 
332  const double newX = finalPosition[0];
333  const double newZ = finalPosition[2];
334  const double oldX = initPosition[0];
335  const double oldZ = initPosition[2];
336 
337  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
338  (newZ-oldZ) * (newZ-oldZ));
339  return distanceMoved;
340 }
341 
342 std::vector<double> Escape_T6Controller::readManualParams(int lineNumber, std::string filename) {
343  assert(lineNumber > 0);
344  std::vector<double> result(32, 1.0);
345  std::string line;
346  std::ifstream infile(filename.c_str(), std::ifstream::in);
347 
348  // Grab line from input file
349  if (infile.is_open()) {
350  std::cout << "OPENED FILE\n";
351  for (int i=0; i<lineNumber; i++) {
352  getline(infile, line);
353  }
354  infile.close();
355  } else {
356  std::cerr << "ERROR: Manual Parameters file not found\n";
357  exit(1);
358  }
359 
360  //cout << "Using: " << line << " as input for starting parameter values\n";
361 
362  // Split line into parameters
363  std::stringstream lineStream(line);
364  std::string cell;
365  int iCell = 0;
366  while(getline(lineStream,cell,',')) {
367  result[iCell] = atof(cell.c_str());
368  iCell++;
369  }
370 
371  bool tweaking = false;
372  if (tweaking) {
373  // Tweak each read-in parameter by as much as 0.5% (params range: [0,1])
374  for (size_t i=0; i < result.size(); i++) {
375  std::cout<<"Entered Cell " << i << ": " << result[i] << "\n";
376  double seed = ((double) (rand() % 100)) / 100;
377  result[i] += (0.01 * seed) - 0.005; // Value +/- 0.005 of original
378  //std::cout<<"Tweaked Cell " << i << ": " << result[i] << "\n";
379  }
380  } else {
381  std::cerr << "WARNING: Input parameters are manually set and constant. Expect the same results each iteration.\n";
382  }
383 
384  return result;
385 }
386 void Escape_T6Controller::printCOM(Escape_T6Model& subject) {
387  std::vector<double> currentPosition = subject.getBallCOM();
388  std::cout << currentPosition[0]/10 << " " << currentPosition[1]/10 << " " << currentPosition[2]/10 << " ";
389  std::cout << std::endl;
390 
391 }
392 
393 void Escape_T6Controller::printMuscleTensions(Escape_T6Model& subject) {
394  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
395  for(size_t i=0; i<muscles.size(); i++) {
396  std::cout << (muscles[i]->getTension())/10 << " ";
397  }
398  std::cout << "\n";
399 }
400 
401 void Escape_T6Controller::printMuscleLengths(Escape_T6Model& subject) {
402  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
403  for(size_t i=0; i<muscles.size(); i++) {
404  std::cout << (muscles[i]->getCurrentLength())/10 << " ";
405  }
406  std::cout << "\n";
407 }
408 
409 void Escape_T6Controller::printSineParams() {
410  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
411  std::cout << "amplitude[" << cluster << "]: " << amplitude[cluster] << std::endl;
412  std::cout << "angularFrequency[" << cluster << "]: " << angularFrequency[cluster] << std::endl;
413  std::cout << "phaseChange[" << cluster << "]: " << phaseChange[cluster] << std::endl;
414  std::cout << "dcOffset[" << cluster << "]: " << dcOffset[cluster] << std::endl;
415  }
416 }
417 
virtual void moveMotors(double dt)
virtual vector< vector< double > > transformActions(vector< vector< double > > act)
const std::vector< tgBasicActuator * > & getAllMuscles() const
virtual void setControlInput(double input)
Contains the definition of class Escape_T6Model. $Id$.
A class to read a learning configuration from a .ini file.
A series of functions to assist with file input/output.
static std::string getResourcePath(std::string relPath)
Definition: FileHelpers.cpp:40
Contains the definition of class AnnealEvolution. Adapting NeuroEvolution to do Simulated Annealing...
virtual void onSetup(Escape_T6Model &subject)
Contains the definition of class tgBasicActuator.
std::vector< double > getBallCOM()
std::vector< T * > find(const tgTagSearch &tagSearch)
Definition: tgModel.h:128
virtual void applyActions(Escape_T6Model &subject, vector< vector< double > > act)
Defines a class AnnealAdapter to pass parameters from AnnealEvolution to a controller. Adapting NeuroEvolution to do Simulated Annealing.
virtual void onStep(Escape_T6Model &subject, double dt)
void initialize(AnnealEvolution *evo, bool isLearning, configuration config)
virtual void onTeardown(Escape_T6Model &subject)