NTRT Simulator  v1.1
 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
35 // The C++ Standard Library
36 #include <cassert>
37 #include <cmath>
38 #include <stdexcept>
39 #include <vector>
40 #include <string>
41 
42 # define M_PI 3.14159265358979323846
43 
44 using namespace std;
45 
46 //Constructor using the model subject and a single pref length for all muscles.
47 //Currently calibrated to decimeters
48 Escape_T6Controller::Escape_T6Controller(const double initialLength) :
49  m_initialLengths(initialLength),
50  m_totalTime(0.0),
51  maxStringLengthFactor(0.50),
52  nClusters(8),
53  musclesPerCluster(3)
54 {
55  clusters.resize(nClusters);
56  for (int i=0; i<nClusters; i++) {
57  clusters[i].resize(musclesPerCluster);
58  }
59 }
60 
63 {
64  m_totalTime = 0;
65  double dt = 0.0001;
66 
67  //Set the initial length of every muscle in the subject
68  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
69  for (size_t i = 0; i < muscles.size(); ++i) {
70  tgBasicActuator * const pMuscle = muscles[i];
71  assert(pMuscle != NULL);
72  pMuscle->setControlInput(this->m_initialLengths, dt);
73  }
74 
75  populateClusters(subject);
76  initPosition = subject.getBallCOM();
77  setupAdapter();
78  initializeSineWaves(); // For muscle actuation
79 
80  vector<double> state; // For config file usage (including Monte Carlo simulations)
81 
82  //get the actions (between 0 and 1) from evolution (todo)
83  actions = evolutionAdapter.step(dt,state);
84 
85  //transform them to the size of the structure
86  actions = transformActions(actions);
87 
88  //apply these actions to the appropriate muscles according to the sensor values
89  applyActions(subject,actions);
90 }
91 
92 void Escape_T6Controller::onStep(Escape_T6Model& subject, double dt)
93 {
94  if (dt <= 0.0) {
95  throw std::invalid_argument("dt is not positive");
96  }
97  m_totalTime+=dt;
98 
99  setPreferredMuscleLengths(subject, dt);
100  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
101 
102  //Move motors for all the muscles
103  for (size_t i = 0; i < muscles.size(); ++i)
104  {
105  tgBasicActuator * const pMuscle = muscles[i];
106  assert(pMuscle != NULL);
107  pMuscle->moveMotors(dt);
108  }
109 
110  //instead, generate it here for now!
111  for(int i=0; i<muscles.size(); i++)
112  {
113  vector<double> tmp;
114  for(int j=0;j<2;j++)
115  {
116  tmp.push_back(0.5);
117  }
118  actions.push_back(tmp);
119  }
120 
121  // Start J Bruce code
122  static int count = 0;
123 
124  if(count > 100)
125  {
126  for(size_t i=0; i<muscles.size(); i++)
127  {
128  std::cout << (muscles[i]->getTension())/10 << "\t";
129  }
130  std::cout << "\n";
131  for(size_t i=0; i<muscles.size(); i++)
132  {
133  std::cout << (muscles[i]->getCurrentLength())/10 << "\t";
134  }
135  std::cout << "\n";
136  count = 0;
137  }
138  else
139  {
140  count++;
141  }
142 }
143 
144 // So far, only score used for eventual fitness calculation of an Escape Model
145 // is the maximum distance from the origin reached during that subject's episode
147  std::vector<double> scores; //scores[0] == displacement, scores[1] == energySpent
148  double distance = displacement(subject);
149  double energySpent = totalEnergySpent(subject);
150 
151  //Invariant: For now, scores must be of size 2 (as required by endEpisode())
152  scores.push_back(distance);
153  scores.push_back(energySpent);
154 
155  std::cout << "Tearing down" << std::endl;
156  evolutionAdapter.endEpisode(scores);
157 
158  // If any of subject's dynamic objects need to be freed, this is the place to do so
159 }
160 
167 vector< vector <double> > Escape_T6Controller::transformActions(vector< vector <double> > actions)
168 {
169  bool usingManualParams = false;
170  vector <double> manualParams(4 * nClusters, 1); // '4' for the number of sine wave parameters
171  if (usingManualParams) {
172  std::cout << "Using manually set parameters\n";
173  string filename = "logs/paramSortedBestTrials.dat";
174  int lineNumber = 1;
175  manualParams = readManualParams(lineNumber, filename);
176  }
177 
178  double pretension = 0.90; // Tweak this value if need be
179  // Minimum amplitude, angularFrequency, phaseChange, and dcOffset
180  double mins[4] = {m_initialLengths * (pretension - maxStringLengthFactor),
181  0.3, //Hz
182  -1 * M_PI,
183  m_initialLengths};// * (1 - maxStringLengthFactor)};
184 
185  // Maximum amplitude, angularFrequency, phaseChange, and dcOffset
186  double maxes[4] = {m_initialLengths * (pretension + maxStringLengthFactor),
187  20, //Hz (can cheat to 50Hz, if feeling immoral)
188  M_PI,
189  m_initialLengths};// * (1 + maxStringLengthFactor)};
190  double ranges[4] = {maxes[0]-mins[0], maxes[1]-mins[1], maxes[2]-mins[2], maxes[3]-mins[3]};
191 
192  for(int i=0;i<actions.size();i++) { //8x
193  for (int j=0; j<actions[i].size(); j++) { //4x
194  if (usingManualParams) {
195  actions[i][j] = manualParams[i*actions[i].size() + j]*(ranges[j])+mins[j];
196  } else {
197  actions[i][j] = actions[i][j]*(ranges[j])+mins[j];
198  }
199  }
200  }
201  return actions;
202 }
203 
207 void Escape_T6Controller::applyActions(Escape_T6Model& subject, vector< vector <double> > actions)
208 {
209  assert(actions.size() == clusters.size());
210 
211  // Apply actions by cluster
212  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
213  amplitude[cluster] = actions[cluster][0];
214  angularFrequency[cluster] = actions[cluster][1];
215  phaseChange[cluster] = actions[cluster][2];
216  dcOffset[cluster] = actions[cluster][3];
217  dcOffset[cluster] = 1;
218  }
219  //printSineParams();
220 }
221 
222 void Escape_T6Controller::setupAdapter() {
223  string suffix = "_Escape";
224  string configAnnealEvolution = "Config.ini";
225  AnnealEvolution* evo = new AnnealEvolution(suffix, configAnnealEvolution);
226  bool isLearning = true;
227  configuration configEvolutionAdapter;
228  configEvolutionAdapter.readFile(configAnnealEvolution);
229 
230  evolutionAdapter.initialize(evo, isLearning, configEvolutionAdapter);
231 }
232 
233 //TODO: Doesn't seem to correctly calculate energy spent by tensegrity
234 double Escape_T6Controller::totalEnergySpent(Escape_T6Model& subject) {
235  double totalEnergySpent=0;
236 
237  vector<tgBasicActuator* > tmpStrings = subject.getAllMuscles();
238  for(int i=0; i<tmpStrings.size(); i++)
239  {
240  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
241 
242  for(int j=1; j<stringHist.tensionHistory.size(); j++)
243  {
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 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  for(int cluster=0; cluster<nClusters; cluster++) {
310  for(int node=0; node<musclesPerCluster; node++) {
311  tgBasicActuator *const pMuscle = clusters[cluster][node];
312  assert(pMuscle != NULL);
313  double newLength = amplitude[cluster] * sin(angularFrequency[cluster] * m_totalTime + phase) + dcOffset[cluster];
314  double minLength = m_initialLengths * (1-maxStringLengthFactor);
315  double maxLength = m_initialLengths * (1+maxStringLengthFactor);
316  if (newLength <= minLength) {
317  newLength = minLength;
318  } else if (newLength >= maxLength) {
319  newLength = maxLength;
320  }
321  pMuscle->setControlInput(newLength, dt);
322  }
323  phase += phaseChange[cluster];
324  }*/
325 }
326 
327 void Escape_T6Controller::populateClusters(Escape_T6Model& subject) {
328  for(int cluster=0; cluster < nClusters; cluster++) {
329  ostringstream ss;
330  ss << (cluster + 1);
331  string suffix = ss.str();
332  std::vector <tgBasicActuator*> musclesInThisCluster = subject.find<tgBasicActuator>("muscle cluster" + suffix);
333  clusters[cluster] = std::vector<tgBasicActuator*>(musclesInThisCluster);
334  }
335 }
336 
337 void Escape_T6Controller::initializeSineWaves() {
338  amplitude = new double[nClusters];
339  angularFrequency = new double[nClusters];
340  phaseChange = new double[nClusters]; // Does not use last value stored in array
341  dcOffset = new double[nClusters];
342 }
343 
344 double Escape_T6Controller::displacement(Escape_T6Model& subject) {
345  vector<double> finalPosition = subject.getBallCOM();
346 
347  // 'X' and 'Z' are irrelevant. Both variables measure lateral direction
348  //assert(finalPosition[0] > 0); //Negative y-value indicates a flaw in the simulator that run (tensegrity went 'underground')
349 
350  const double newX = finalPosition[0];
351  const double newZ = finalPosition[2];
352  const double oldX = initPosition[0];
353  const double oldZ = initPosition[2];
354 
355  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
356  (newZ-oldZ) * (newZ-oldZ));
357  return distanceMoved;
358 }
359 
360 std::vector<double> Escape_T6Controller::readManualParams(int lineNumber, string filename) {
361  assert(lineNumber > 0);
362  vector<double> result(32, 1.0);
363  string line;
364  ifstream infile(filename.c_str(), ifstream::in);
365 
366  // Grab line from input file
367  if (infile.is_open()) {
368  cout << "OPENED FILE\n";
369  for (int i=0; i<lineNumber; i++) {
370  getline(infile, line);
371  }
372  infile.close();
373  } else {
374  cerr << "Error: Manual Parameters file not found\n";
375  exit(1);
376  }
377 
378  //cout << "Using: " << line << " as input for starting parameter values\n";
379 
380  // Split line into parameters
381  stringstream lineStream(line);
382  string cell;
383  int iCell = 0;
384  while(getline(lineStream,cell,',')) {
385  result[iCell] = atof(cell.c_str());
386  iCell++;
387  }
388 
389  bool tweaking = false;
390  if (tweaking) {
391  // Tweak each read-in parameter by as much as 0.5% (params range: [0,1])
392  for (int i=0; i < result.size(); i++) {
393  std::cout<<"Entered Cell " << i << ": " << result[i] << "\n";
394  double seed = ((double) (rand() % 100)) / 100;
395  result[i] += (0.01 * seed) - 0.005; // Value +/- 0.005 of original
396  //std::cout<<"Tweaked Cell " << i << ": " << result[i] << "\n";
397  }
398  } else {
399  cerr << "WARNING: Not changing manual input parameters\n";
400  }
401 
402  return result;
403 }
404 
405 void Escape_T6Controller::printSineParams() {
406  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
407  std::cout << "amplitude[" << cluster << "]: " << amplitude[cluster] << std::endl;
408  std::cout << "angularFrequency[" << cluster << "]: " << angularFrequency[cluster] << std::endl;
409  std::cout << "phaseChange[" << cluster << "]: " << phaseChange[cluster] << std::endl;
410  std::cout << "dcOffset[" << cluster << "]: " << dcOffset[cluster] << std::endl;
411  }
412 }
413 
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.
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:125
virtual void applyActions(Escape_T6Model &subject, vector< vector< double > > act)
virtual void onStep(Escape_T6Model &subject, double dt)
void initialize(AnnealEvolution *evo, bool isLearning, configuration config)
virtual void onTeardown(Escape_T6Model &subject)