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
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  double dt = 0.0001;
65 
66  //Set the initial length of every muscle in the subject
67  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
68  for (size_t i = 0; i < muscles.size(); ++i) {
69  tgBasicActuator * const pMuscle = muscles[i];
70  assert(pMuscle != NULL);
71  pMuscle->setControlInput(this->m_initialLengths, dt);
72  }
73 
74  populateClusters(subject);
75  initPosition = subject.getBallCOM();
76  setupAdapter();
77  initializeSineWaves(); // For muscle actuation
78 
79  vector<double> state; // For config file usage (including Monte Carlo simulations)
80 
81  //get the actions (between 0 and 1) from evolution (todo)
82  actions = evolutionAdapter.step(dt,state);
83 
84  //transform them to the size of the structure
85  actions = transformActions(actions);
86 
87  //apply these actions to the appropriate muscles according to the sensor values
88  applyActions(subject,actions);
89 }
90 
92 {
93  if (dt <= 0.0) {
94  throw std::invalid_argument("dt is not positive");
95  }
96  m_totalTime+=dt;
97 
98  setPreferredMuscleLengths(subject, dt);
99  const std::vector<tgBasicActuator*> muscles = subject.getAllMuscles();
100 
101  //Move motors for all the muscles
102  for (size_t i = 0; i < muscles.size(); ++i)
103  {
104  tgBasicActuator * const pMuscle = muscles[i];
105  assert(pMuscle != NULL);
106  pMuscle->moveMotors(dt);
107  }
108 
109  //instead, generate it here for now!
110  for(int i=0; i<muscles.size(); i++)
111  {
112  vector<double> tmp;
113  for(int j=0;j<2;j++)
114  {
115  tmp.push_back(0.5);
116  }
117  actions.push_back(tmp);
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> > Escape_T6Controller::transformActions(vector< vector <double> > actions)
145 {
146  bool usingManualParams = true;
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/trial_7/bestParametersNoOutliersSorted.dat";
151  string filename = "logs/trial_8/bestParamsSorted.dat";
152  int lineNumber = 1;
153  manualParams = readManualParams(lineNumber, filename);
154  }
155 
156  double pretension = 0.90; // Tweak this value if need be
157  // Minimum amplitude, angularFrequency, phaseChange, and dcOffset
158  double mins[4] = {m_initialLengths * (pretension - maxStringLengthFactor),
159  0.3, //Hz
160  -1 * M_PI,
161  m_initialLengths};// * (1 - maxStringLengthFactor)};
162 
163  // Maximum amplitude, angularFrequency, phaseChange, and dcOffset
164  double maxes[4] = {m_initialLengths * (pretension + maxStringLengthFactor),
165  20, //Hz (can cheat to 50Hz, if feeling immoral)
166  M_PI,
167  m_initialLengths};// * (1 + maxStringLengthFactor)};
168  double ranges[4] = {maxes[0]-mins[0], maxes[1]-mins[1], maxes[2]-mins[2], maxes[3]-mins[3]};
169 
170  for(int i=0;i<actions.size();i++) { //8x
171  for (int j=0; j<actions[i].size(); j++) { //4x
172  if (usingManualParams) {
173  actions[i][j] = manualParams[i*actions[i].size() + j]*(ranges[j])+mins[j];
174  } else {
175  actions[i][j] = actions[i][j]*(ranges[j])+mins[j];
176  }
177  }
178  }
179  return actions;
180 }
181 
185 void Escape_T6Controller::applyActions(Escape_T6Model& subject, vector< vector <double> > actions)
186 {
187  assert(actions.size() == clusters.size());
188 
189  // Apply actions by cluster
190  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
191  amplitude[cluster] = actions[cluster][0];
192  angularFrequency[cluster] = actions[cluster][1];
193  phaseChange[cluster] = actions[cluster][2];
194  dcOffset[cluster] = actions[cluster][3];
195  }
196  //printSineParams();
197 }
198 
199 void Escape_T6Controller::setupAdapter() {
200  string suffix = "_Escape";
201  string configAnnealEvolution = "Config.ini";
202  AnnealEvolution* evo = new AnnealEvolution(suffix, configAnnealEvolution);
203  bool isLearning = true;
204  configuration configEvolutionAdapter;
205  configEvolutionAdapter.readFile(configAnnealEvolution);
206 
207  evolutionAdapter.initialize(evo, isLearning, configEvolutionAdapter);
208 }
209 
210 //TODO: Doesn't seem to correctly calculate energy spent by tensegrity
211 double Escape_T6Controller::totalEnergySpent(Escape_T6Model& subject) {
212  double totalEnergySpent=0;
213 
214  vector<tgBasicActuator* > tmpStrings = subject.getAllMuscles();
215  for(int i=0; i<tmpStrings.size(); i++)
216  {
217  tgBaseString::BaseStringHistory stringHist = tmpStrings[i]->getHistory();
218 
219  for(int j=1; j<stringHist.tensionHistory.size(); j++)
220  {
221  const double previousTension = stringHist.tensionHistory[j-1];
222  const double previousLength = stringHist.restLengths[j-1];
223  const double currentLength = stringHist.restLengths[j];
224  //TODO: examine this assumption - free spinning motor may require more power
225  double motorSpeed = (currentLength-previousLength);
226  if(motorSpeed > 0) // Vestigial code
227  motorSpeed = 0;
228  const double workDone = previousTension * motorSpeed;
229  totalEnergySpent += workDone;
230  }
231  }
232  return totalEnergySpent;
233 }
234 
235 // Pre-condition: every element in muscles must be defined
236 // Post-condition: every muscle will have a new target length
237 void Escape_T6Controller::setPreferredMuscleLengths(Escape_T6Model& subject, double dt) {
238  double phase = 0; // Phase of cluster1
239 
240  for(int cluster=0; cluster<nClusters; cluster++) {
241  for(int node=0; node<musclesPerCluster; node++) {
242  tgBasicActuator *const pMuscle = clusters[cluster][node];
243  assert(pMuscle != NULL);
244  double newLength = amplitude[cluster] * sin(angularFrequency[cluster] * m_totalTime + phase) + dcOffset[cluster];
245  double minLength = m_initialLengths * (1-maxStringLengthFactor);
246  double maxLength = m_initialLengths * (1+maxStringLengthFactor);
247  if (newLength <= minLength) {
248  newLength = minLength;
249  } else if (newLength >= maxLength) {
250  newLength = maxLength;
251  }
252  pMuscle->setControlInput(newLength, dt);
253  }
254  phase += phaseChange[cluster];
255  }
256 }
257 
258 void Escape_T6Controller::populateClusters(Escape_T6Model& subject) {
259  for(int cluster=0; cluster < nClusters; cluster++) {
260  ostringstream ss;
261  ss << (cluster + 1);
262  string suffix = ss.str();
263  std::vector <tgBasicActuator*> musclesInThisCluster = subject.find<tgBasicActuator>("muscle cluster" + suffix);
264  clusters[cluster] = std::vector<tgBasicActuator*>(musclesInThisCluster);
265  }
266 }
267 
268 void Escape_T6Controller::initializeSineWaves() {
269  amplitude = new double[nClusters];
270  angularFrequency = new double[nClusters];
271  phaseChange = new double[nClusters]; // Does not use last value stored in array
272  dcOffset = new double[nClusters];
273 }
274 
275 double Escape_T6Controller::displacement(Escape_T6Model& subject) {
276  vector<double> finalPosition = subject.getBallCOM();
277 
278  // 'X' and 'Z' are irrelevant. Both variables measure lateral direction
279  //assert(finalPosition[0] > 0); //Negative y-value indicates a flaw in the simulator that run (tensegrity went 'underground')
280 
281  const double newX = finalPosition[0];
282  const double newZ = finalPosition[2];
283  const double oldX = initPosition[0];
284  const double oldZ = initPosition[2];
285 
286  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
287  (newZ-oldZ) * (newZ-oldZ));
288  return distanceMoved;
289 }
290 
291 std::vector<double> Escape_T6Controller::readManualParams(int lineNumber, string filename) {
292  assert(lineNumber > 0);
293  vector<double> result(32, 1.0);
294  string line;
295  ifstream infile(filename.c_str(), ifstream::in);
296 
297  // Grab line from input file
298  if (infile.is_open()) {
299  for (int i=0; i<lineNumber; i++) {
300  getline(infile, line);
301  }
302  infile.close();
303  }
304 
305  //cout << "Using: " << line << " as input for starting parameter values\n";
306 
307  // Split line into parameters
308  stringstream lineStream(line);
309  string cell;
310  int iCell = 0;
311  while(getline(lineStream,cell,',')) {
312  result[iCell] = atof(cell.c_str());
313  iCell++;
314  }
315 
316  // Tweak each read-in parameter by as much as 0.5% (params range: [0,1])
317  for (int i=0; i < result.size(); i++) {
318  //std::cout<<"Cell " << i << ": " << result[i] << "\n";
319  double seed = ((double) (rand() % 100)) / 100;
320  result[i] += (0.01 * seed) - 0.005; // Value +/- 0.005 of original
321  //std::cout<<"Cell " << i << ": " << result[i] << "\n";
322  }
323 
324  return result;
325 }
326 
327 void Escape_T6Controller::printSineParams() {
328  for (size_t cluster = 0; cluster < clusters.size(); cluster++) {
329  std::cout << "amplitude[" << cluster << "]: " << amplitude[cluster] << std::endl;
330  std::cout << "angularFrequency[" << cluster << "]: " << angularFrequency[cluster] << std::endl;
331  std::cout << "phaseChange[" << cluster << "]: " << phaseChange[cluster] << std::endl;
332  std::cout << "dcOffset[" << cluster << "]: " << dcOffset[cluster] << std::endl;
333  }
334 }
335 
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:128
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)