NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
JSONFeedbackControl.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 #include "JSONFeedbackControl.h"
28 
29 
30 // Should include tgString, but compiler complains since its been
31 // included from BaseSpineModelLearning. Perhaps we should move things
32 // to a cpp over there
34 #include "core/tgBasicActuator.h"
37 #include "examples/learningSpines/tgCPGCableControl.h"
38 
40 #include "helpers/FileHelpers.h"
41 
44 
45 #include "util/CPGEquationsFB.h"
46 #include "examples/learningSpines/tgCPGCableControl.h"
47 
48 #include "neuralNet/Neural Network v2/neuralNetwork.h"
49 
50 #include <json/json.h>
51 
52 #include <string>
53 #include <iostream>
54 #include <vector>
55 
56 //#define LOGGING
57 #define USE_KINEMATIC
58 
59 using namespace std;
60 
62  int tm,
63  int om,
64  int param,
65  int segnum,
66  double ct,
67  double la,
68  double ha,
69  double lp,
70  double hp,
71  double kt,
72  double kp,
73  double kv,
74  bool def,
75  double cl,
76  double lf,
77  double hf,
78  double ffMin,
79  double ffMax,
80  double afMin,
81  double afMax,
82  double pfMin,
83  double pfMax) :
84 JSONCPGControl::Config::Config(ss, tm, om, param, segnum, ct, la, ha,
85  lp, hp, kt, kp, kv, def, cl, lf, hf),
86 freqFeedbackMin(ffMin),
87 freqFeedbackMax(ffMax),
88 ampFeedbackMin(afMin),
89 ampFeedbackMax(afMax),
90 phaseFeedbackMin(pfMin),
91 phaseFeedbackMax(pfMax)
92 {
93 
94 }
101  std::string args,
102  std::string resourcePath) :
103 JSONCPGControl(config, args, resourcePath),
104 m_config(config)
105 {
106  // Path and filename handled by base class
107 
108 }
109 
110 JSONFeedbackControl::~JSONFeedbackControl()
111 {
112  delete nn;
113 }
114 
116 {
117  m_pCPGSys = new CPGEquationsFB(100);
118 
119  Json::Value root; // will contains the root value after parsing.
120  Json::Reader reader;
121 
122  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
123  if ( !parsingSuccessful )
124  {
125  // report to the user the failure and their locations in the document.
126  std::cout << "Failed to parse configuration\n"
127  << reader.getFormattedErrorMessages();
128  throw std::invalid_argument("Bad filename for JSON");
129  }
130  // Get the value of the member of root named 'encoding', return 'UTF-8' if there is no
131  // such member.
132  Json::Value nodeVals = root.get("nodeVals", "UTF-8");
133  Json::Value edgeVals = root.get("edgeVals", "UTF-8");
134 
135  std::cout << nodeVals << std::endl;
136 
137  nodeVals = nodeVals.get("params", "UTF-8");
138  edgeVals = edgeVals.get("params", "UTF-8");
139 
140  array_4D edgeParams = scaleEdgeActions(edgeVals);
141  array_2D nodeParams = scaleNodeActions(nodeVals);
142 
143  setupCPGs(subject, nodeParams, edgeParams);
144 
145  Json::Value feedbackParams = root.get("feedbackVals", "UTF-8");
146  feedbackParams = feedbackParams.get("params", "UTF-8");
147 
148  // Setup neural network
149  m_config.numStates = feedbackParams.get("numStates", "UTF-8").asInt();
150  m_config.numActions = feedbackParams.get("numActions", "UTF-8").asInt();
151  //m_config.numHidden = feedbackParams.get("numHidden", "UTF-8").asInt();
152 
153  std::string nnFile = controlFilePath + feedbackParams.get("neuralFilename", "UTF-8").asString();
154 
155  nn = new neuralNetwork(m_config.numStates, m_config.numStates*2, m_config.numActions);
156 
157  nn->loadWeights(nnFile.c_str());
158 
159  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
160  for (int i = 0; i < initConditions.size(); i++)
161  {
162  std::cout << initConditions[i] << " ";
163  }
164  std::cout << std::endl;
165 #ifdef LOGGING // Conditional compile for data logging
166  m_dataObserver.onSetup(subject);
167 #endif
168 
169 #if (0) // Conditional Compile for debug info
170  std::cout << *m_pCPGSys << std::endl;
171 #endif
172  m_updateTime = 0.0;
173  bogus = false;
174 }
175 
177 {
178  m_updateTime += dt;
179  if (m_updateTime >= m_config.controlTime)
180  {
181 #if (1)
182  std::vector<double> desComs = getFeedback(subject);
183 
184 #else
185  std::size_t numControllers = subject.getNumberofMuslces() * 3;
186 
187  double descendingCommand = 0.0;
188  std::vector<double> desComs (numControllers, descendingCommand);
189 #endif
190  try
191  {
192  m_pCPGSys->update(desComs, m_updateTime);
193  }
194  catch (std::runtime_error& e)
195  {
196  // Stops the trial immediately, lets teardown know it broke
197  bogus = true;
198  throw (e);
199  }
200 
201 #ifdef LOGGING // Conditional compile for data logging
202  m_dataObserver.onStep(subject, m_updateTime);
203 #endif
204  notifyStep(m_updateTime);
205  m_updateTime = 0;
206  }
207 
208  double currentHeight = subject.getSegmentCOM(m_config.segmentNumber)[1];
209 
211  if (currentHeight > 25 || currentHeight < 1.0)
212  {
214  bogus = true;
215  throw std::runtime_error("Height out of range");
216  }
217 }
218 
220 {
221  scores.clear();
222  // @todo - check to make sure we ran for the right amount of time
223 
224  std::vector<double> finalConditions = subject.getSegmentCOM(m_config.segmentNumber);
225 
226  const double newX = finalConditions[0];
227  const double newZ = finalConditions[2];
228  const double oldX = initConditions[0];
229  const double oldZ = initConditions[2];
230 
231  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
232  (newZ-oldZ) * (newZ-oldZ));
233 
234  if (bogus)
235  {
236  scores.push_back(-1.0);
237  }
238  else
239  {
240  scores.push_back(distanceMoved);
241  }
242 
245  double totalEnergySpent=0;
246 
247  std::vector<tgSpringCableActuator* > tmpStrings = subject.getAllMuscles();
248 
249  for(std::size_t i=0; i<tmpStrings.size(); i++)
250  {
251  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
252 
253  for(std::size_t j=1; j<stringHist.tensionHistory.size(); j++)
254  {
255  const double previousTension = stringHist.tensionHistory[j-1];
256  const double previousLength = stringHist.restLengths[j-1];
257  const double currentLength = stringHist.restLengths[j];
258  //TODO: examine this assumption - free spinning motor may require more power
259  double motorSpeed = (currentLength-previousLength);
260  if(motorSpeed > 0) // Vestigial code
261  motorSpeed = 0;
262  const double workDone = previousTension * motorSpeed;
263  totalEnergySpent += workDone;
264  }
265  }
266 
267  scores.push_back(totalEnergySpent);
268 
269  std::cout << "Dist travelled " << scores[0] << std::endl;
270 
271  Json::Value root; // will contains the root value after parsing.
272  Json::Reader reader;
273 
274  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
275  if ( !parsingSuccessful )
276  {
277  // report to the user the failure and their locations in the document.
278  std::cout << "Failed to parse configuration\n"
279  << reader.getFormattedErrorMessages();
280  throw std::invalid_argument("Bad filename for JSON");
281  }
282 
283  Json::Value prevScores = root.get("scores", Json::nullValue);
284 
285  Json::Value subScores;
286  subScores["distance"] = scores[0];
287  subScores["energy"] = totalEnergySpent;
288 
289  prevScores.append(subScores);
290  root["scores"] = prevScores;
291 
292  ofstream payloadLog;
293  payloadLog.open(controlFilename.c_str(),ofstream::out);
294 
295  payloadLog << root << std::endl;
296 
297  delete m_pCPGSys;
298  m_pCPGSys = NULL;
299 
300  for(size_t i = 0; i < m_allControllers.size(); i++)
301  {
302  delete m_allControllers[i];
303  }
304  m_allControllers.clear();
305 }
306 
307 void JSONFeedbackControl::setupCPGs(BaseSpineModelLearning& subject, array_2D nodeActions, array_4D edgeActions)
308 {
309 
310  std::vector <tgSpringCableActuator*> allMuscles = subject.getAllMuscles();
311 
312  CPGEquationsFB& m_CPGFBSys = *(tgCast::cast<CPGEquations, CPGEquationsFB>(m_pCPGSys));
313 
314  for (std::size_t i = 0; i < allMuscles.size(); i++)
315  {
316 
317  tgPIDController::Config config(20000.0, 0.0, 5.0, true); // Non backdrivable
318  tgCPGCableControl* pStringControl = new tgCPGCableControl(config);
319 
320  allMuscles[i]->attach(pStringControl);
321 
322  // First assign node numbers
323  pStringControl->assignNodeNumberFB(m_CPGFBSys, nodeActions);
324 
325  m_allControllers.push_back(pStringControl);
326  }
327 
328  // Then determine connectivity and setup string
329  for (std::size_t i = 0; i < m_allControllers.size(); i++)
330  {
331  tgCPGActuatorControl * const pStringInfo = m_allControllers[i];
332  assert(pStringInfo != NULL);
333  pStringInfo->setConnectivity(m_allControllers, edgeActions);
334 
335  //String will own this pointer
336  tgImpedanceController* p_ipc = new tgImpedanceController( m_config.tension,
337  m_config.kPosition,
338  m_config.kVelocity);
339  if (m_config.useDefault)
340  {
341  pStringInfo->setupControl(*p_ipc);
342  }
343  else
344  {
345  pStringInfo->setupControl(*p_ipc, m_config.controlLength);
346  }
347  }
348 
349 }
350 
351 array_2D JSONFeedbackControl::scaleNodeActions (Json::Value actions)
352 {
353  std::size_t numControllers = actions.size();
354  std::size_t numActions = actions[0].size();
355 
356  array_2D nodeActions(boost::extents[numControllers][numActions]);
357 
358  array_2D limits(boost::extents[2][numActions]);
359 
360  // Check if we need to update limits
361  assert(numActions == 5);
362 
363  limits[0][0] = m_config.lowFreq;
364  limits[1][0] = m_config.highFreq;
365  limits[0][1] = m_config.lowAmp;
366  limits[1][1] = m_config.highAmp;
367  limits[0][2] = m_config.freqFeedbackMin;
368  limits[1][2] = m_config.freqFeedbackMax;
369  limits[0][3] = m_config.ampFeedbackMin;
370  limits[1][3] = m_config.ampFeedbackMax;
371  limits[0][4] = m_config.phaseFeedbackMin;
372  limits[1][4] = m_config.phaseFeedbackMax;
373 
374  Json::Value::iterator nodeIt = actions.begin();
375 
376  // This one is square
377  for( std::size_t i = 0; i < numControllers; i++)
378  {
379  Json::Value nodeParam = *nodeIt;
380  for( std::size_t j = 0; j < numActions; j++)
381  {
382  nodeActions[i][j] = ( (nodeParam.get(j, 0.0)).asDouble() *
383  (limits[1][j] - limits[0][j])) + limits[0][j];
384  }
385  nodeIt++;
386  }
387 
388  return nodeActions;
389 }
390 
391 std::vector<double> JSONFeedbackControl::getFeedback(BaseSpineModelLearning& subject)
392 {
393  // Placeholder
394  std::vector<double> feedback;
395 
396  const std::vector<tgSpringCableActuator*>& allCables = subject.getAllMuscles();
397 
398  double *inputs = new double[m_config.numStates];
399 
400  std::size_t n = allCables.size();
401  for(std::size_t i = 0; i != n; i++)
402  {
403  std::vector< std::vector<double> > actions;
404 
405  const tgSpringCableActuator& cable = *(allCables[i]);
406  std::vector<double > state = getCableState(cable);
407 
408  // Rescale to 0 to 1 (consider doing this inside getState
409  for (std::size_t i = 0; i < state.size(); i++)
410  {
411  inputs[i]=state[i] / 2.0 + 0.5;
412  }
413 
414  double *output = nn->feedForwardPattern(inputs);
415  vector<double> tmpAct;
416  for(int j=0;j<m_config.numActions;j++)
417  {
418  tmpAct.push_back(output[j]);
419  }
420  actions.push_back(tmpAct);
421 
422  std::vector<double> cableFeedback = transformFeedbackActions(actions);
423 
424  feedback.insert(feedback.end(), cableFeedback.begin(), cableFeedback.end());
425  }
426 
427 
428  return feedback;
429 }
430 
431 std::vector<double> JSONFeedbackControl::getCableState(const tgSpringCableActuator& cable)
432 {
433  // For each string, scale value from -1 to 1 based on initial length or max tension of motor
434 
435  std::vector<double> state;
436 
437  // Scale length by starting length
438  const double startLength = cable.getStartLength();
439  state.push_back((cable.getCurrentLength() - startLength) / startLength);
440 
441  const double maxTension = cable.getConfig().maxTens;
442  state.push_back((cable.getTension() - maxTension / 2.0) / maxTension);
443 
444  return state;
445 }
446 
447 std::vector<double> JSONFeedbackControl::transformFeedbackActions(std::vector< std::vector<double> >& actions)
448 {
449  // Placeholder
450  std::vector<double> feedback;
451 
452  // Leave in place for generalization later
453  const std::size_t numControllers = 1;
454  const std::size_t numActions = m_config.numActions;
455 
456  assert( actions.size() == numControllers);
457  assert( actions[0].size() == numActions);
458 
459  // Scale values back to -1 to +1
460  for( std::size_t i = 0; i < numControllers; i++)
461  {
462  for( std::size_t j = 0; j < numActions; j++)
463  {
464  feedback.push_back(actions[i][j] * 2.0 - 1.0);
465  }
466  }
467 
468  return feedback;
469 }
Contains the definition of class ImpedanceControl. $Id$.
void update(std::vector< double > &descCom, double dt)
virtual const double getTension() const
virtual const double getStartLength() const
virtual void onSetup(BaseSpineModelLearning &subject)
virtual array_4D scaleEdgeActions(Json::Value edgeParam)
void setConnectivity(const std::vector< tgCPGActuatorControl * > &allStrings, array_4D edgeParams)
A controller for the template class BaseSpineModelLearning.
virtual void onStep(BaseSpineModelLearning &subject, double dt)
Definition of the tgCPGStringControl observer class.
A template base class for a tensegrity spine.
A class to read a learning configuration from a .ini file.
Contains the definition of abstract base class tgSpringCableActuator. Assumes that the string is line...
A series of functions to assist with file input/output.
Contains the definition of class AnnealEvolution. Adapting NeuroEvolution to do Simulated Annealing...
virtual void onTeardown(BaseSpineModelLearning &subject)
Contains the definition of class tgBasicActuator.
const Config & getConfig() const
virtual void onStep(tgModel &model, double dt)
Definition of class CPGEquationsFB.
Config(int ss, int tm, int om, int param, int segnum=6, double ct=0.1, double la=0, double ha=30, double lp=-1 *M_PI, double hp=M_PI, double kt=0.0, double kp=1000.0, double kv=100.0, bool def=true, double cl=10.0, double lf=0.0, double hf=30.0, double ffMin=0.0, double ffMax=0.0, double afMin=0.0, double afMax=0.0, double pfMin=0.0, double pfMax=0.0)
virtual void onSetup(tgModel &model)
JSONFeedbackControl(JSONFeedbackControl::Config config, std::string args, std::string resourcePath="")
virtual const double getCurrentLength() const
void notifyStep(double dt)
void assignNodeNumberFB(CPGEquationsFB &CPGSys, array_2D nodeParams)