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