NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
JSONGoalControl.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 "JSONGoalControl.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 #include "examples/learningSpines/tgSCASineControl.h"
39 
42 #include "helpers/FileHelpers.h"
43 
44 #include "util/CPGEquationsFB.h"
45 #include "examples/learningSpines/tgCPGCableControl.h"
46 
47 #include "neuralNet/Neural Network v2/neuralNetwork.h"
48 
49 #include <json/json.h>
50 
51 #include <string>
52 #include <iostream>
53 #include <vector>
54 
55 //#define LOGGING
56 #define USE_KINEMATIC
57 
58 using namespace std;
59 
61  int tm,
62  int om,
63  int param,
64  int segnum,
65  double ct,
66  double la,
67  double ha,
68  double lp,
69  double hp,
70  double kt,
71  double kp,
72  double kv,
73  bool def,
74  double cl,
75  double lf,
76  double hf,
77  double ffMin,
78  double ffMax,
79  double afMin,
80  double afMax,
81  double pfMin,
82  double pfMax,
83  double tf) :
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 tensFeedback(tf)
93 {
94 
95 }
102  std::string args,
103  std::string resourcePath) :
104 JSONCPGControl(config, args, resourcePath),
105 m_config(config),
106 nn(NULL),
107 nn_goal(NULL)
108 {
109  // Path and filename handled by base class
110 
111 }
112 
113 JSONGoalControl::~JSONGoalControl()
114 {
115 }
116 
118 {
119  m_pCPGSys = new CPGEquationsFB(200);
120 
121  Json::Value root; // will contains the root value after parsing.
122  Json::Reader reader;
123 
124  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
125  if ( !parsingSuccessful )
126  {
127  // report to the user the failure and their locations in the document.
128  std::cout << "Failed to parse configuration\n"
129  << reader.getFormattedErrorMessages();
130  throw std::invalid_argument("Bad filename for JSON");
131  }
132  // Get the value of the member of root named 'encoding', return 'UTF-8' if there is no
133  // such member.
134  Json::Value nodeVals = root.get("nodeVals", "UTF-8");
135  Json::Value edgeVals = root.get("edgeVals", "UTF-8");
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  // TODO make a function that does this based on a NN pointer and a string
146  Json::Value feedbackParams = root.get("feedbackVals", "UTF-8");
147  feedbackParams = feedbackParams.get("params", "UTF-8");
148 
149  // Setup neural network
150  m_config.numStates = feedbackParams.get("numStates", "UTF-8").asInt();
151  m_config.numActions = feedbackParams.get("numActions", "UTF-8").asInt();
152  m_config.numHidden = feedbackParams.get("numHidden", "UTF-8").asInt();
153 
154  std::string nnFile = controlFilePath + feedbackParams.get("neuralFilename", "UTF-8").asString();
155 
156  nn = new neuralNetwork(m_config.numStates, m_config.numHidden, m_config.numActions);
157 
158  nn->loadWeights(nnFile.c_str());
159 
160  Json::Value goalParams = root.get("goalVals", "UTF-8");
161  goalParams = goalParams.get("params", "UTF-8");
162 
163  // Setup neural network
164  m_config.goalStates = goalParams.get("numStates", "UTF-8").asInt();
165  m_config.goalActions = goalParams.get("numActions", "UTF-8").asInt();
166  m_config.goalHidden = goalParams.get("numHidden", "UTF-8").asInt();
167 
168  std::string nnFile_goal = controlFilePath + goalParams.get("neuralFilename", "UTF-8").asString();
169 
170  nn_goal = new neuralNetwork(m_config.goalStates, m_config.goalHidden, m_config.goalActions);
171 
172  nn_goal->loadWeights(nnFile_goal.c_str());
173 
174  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
175 #ifdef LOGGING // Conditional compile for data logging
176  m_dataObserver.onSetup(subject);
177 #endif
178 
179 #if (0) // Conditional Compile for debug info
180  std::cout << *m_pCPGSys << std::endl;
181 #endif
182  m_updateTime = 0.0;
183  bogus = false;
184 }
185 
187 {
188  m_updateTime += dt;
189  if (m_updateTime >= m_config.controlTime)
190  {
191 #if (1) // Goal and cable
192 
193  std::vector<double> desComs = getFeedback(subject);
194 
195  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
196  std::vector<double> desComsSet2 = getGoalFeedback(goalSubject);
197 
198  std::size_t n = desComs.size();
199  assert(n == desComsSet2.size());
200 
201  for (std::size_t i = 0; i < n; i++)
202  {
203  desComs[i] += desComsSet2[i];
204  }
205 
206 #elif (1) // Just goal
207  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
208  std::vector<double> desComs = getGoalFeedback(goalSubject);
209 #else // Nothing
210  std::size_t numControllers = subject.getNumberofMuslces() * 3;
211 
212  double descendingCommand = 0.0;
213  std::vector<double> desComs (numControllers, descendingCommand);
214 #endif
215  try
216  {
217  m_pCPGSys->update(desComs, m_updateTime);
218  }
219  catch (std::runtime_error& e)
220  {
221  // Stops the trial immediately, lets teardown know it broke
222  bogus = true;
223  throw (e);
224  }
225 
226 #ifdef LOGGING // Conditional compile for data logging
227  m_dataObserver.onStep(subject, m_updateTime);
228 #endif
229  notifyStep(m_updateTime);
230  m_updateTime = 0;
231  }
232 
233  double currentHeight = subject.getSegmentCOM(m_config.segmentNumber)[1];
234 
236  if (currentHeight > 25 || currentHeight < 1.0)
237  {
239  bogus = true;
240  throw std::runtime_error("Height out of range");
241  }
242 }
243 
245 {
246  scores.clear();
247  // @todo - check to make sure we ran for the right amount of time
248 
249  std::vector<double> finalConditions = subject.getSegmentCOM(m_config.segmentNumber);
250 
251  const double newX = finalConditions[0];
252  const double newZ = finalConditions[2];
253  const double oldX = initConditions[0];
254  const double oldZ = initConditions[2];
255 
256  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
257 
258  const double distanceMoved = calculateDistanceMoved(goalSubject);
259 
260  if (bogus)
261  {
262  scores.push_back(-1.0);
263  }
264  else
265  {
266  scores.push_back(distanceMoved);
267  }
268 
271  double totalEnergySpent=0;
272 
273  std::vector<tgSpringCableActuator* > tmpStrings = subject.getAllMuscles();
274 
275  for(std::size_t i=0; i<tmpStrings.size(); i++)
276  {
277  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
278 
279  for(std::size_t j=1; j<stringHist.tensionHistory.size(); j++)
280  {
281  const double previousTension = stringHist.tensionHistory[j-1];
282  const double previousLength = stringHist.restLengths[j-1];
283  const double currentLength = stringHist.restLengths[j];
284  //TODO: examine this assumption - free spinning motor may require more power
285  double motorSpeed = (currentLength-previousLength);
286  if(motorSpeed > 0) // Vestigial code
287  motorSpeed = 0;
288  const double workDone = previousTension * motorSpeed;
289  totalEnergySpent += workDone;
290  }
291  }
292 
293  scores.push_back(totalEnergySpent);
294 
295  std::cout << "Dist travelled " << scores[0] << std::endl;
296 
297  Json::Value root; // will contains the root value after parsing.
298  Json::Reader reader;
299 
300  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
301  if ( !parsingSuccessful )
302  {
303  // report to the user the failure and their locations in the document.
304  std::cout << "Failed to parse configuration\n"
305  << reader.getFormattedErrorMessages();
306  throw std::invalid_argument("Bad filename for JSON");
307  }
308 
309  Json::Value prevScores = root.get("scores", Json::nullValue);
310 
311  Json::Value subScores;
312  subScores["distance"] = scores[0];
313  subScores["energy"] = totalEnergySpent;
314 
315  prevScores.append(subScores);
316  root["scores"] = prevScores;
317 
318  ofstream payloadLog;
319  payloadLog.open(controlFilename.c_str(),ofstream::out);
320 
321  payloadLog << root << std::endl;
322 
323  delete m_pCPGSys;
324  m_pCPGSys = NULL;
325 
326  for(size_t i = 0; i < m_allControllers.size(); i++)
327  {
328  delete m_allControllers[i];
329  }
330  m_allControllers.clear();
331 
332  delete nn;
333  delete nn_goal;
334 }
335 
336 void JSONGoalControl::setupCPGs(BaseSpineModelLearning& subject, array_2D nodeActions, array_4D edgeActions)
337 {
338 
339  std::vector <tgSpringCableActuator*> allMuscles = subject.getAllMuscles();
340 
341  CPGEquationsFB& m_CPGFBSys = *(tgCast::cast<CPGEquations, CPGEquationsFB>(m_pCPGSys));
342 
343  for (std::size_t i = 0; i < allMuscles.size(); i++)
344  {
345 
346  tgPIDController::Config config(20000.0, 0.0, 5.0, true); // Non backdrivable
347  tgCPGCableControl* pStringControl = new tgCPGCableControl(config);
348 
349  allMuscles[i]->attach(pStringControl);
350 
351  // First assign node numbers
352  pStringControl->assignNodeNumberFB(m_CPGFBSys, nodeActions);
353 
354  m_allControllers.push_back(pStringControl);
355  }
356 
357  // Then determine connectivity and setup string
358  for (std::size_t i = 0; i < m_allControllers.size(); i++)
359  {
360  tgCPGActuatorControl * const pStringInfo = m_allControllers[i];
361  assert(pStringInfo != NULL);
362  pStringInfo->setConnectivity(m_allControllers, 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 JSONGoalControl::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> JSONGoalControl::getGoalFeedback(const BaseSpineModelGoal* subject)
421 {
422  const int nSeg = subject->getSegments() - 1;
423 
424  // Only one segment has no actuators, and means we can't get heading.
425  assert(nSeg > 0);
426 
427  // Get heading and generate feedback vector
428  const btVector3 currentPosVector = subject->getSegmentCOMVector(m_config.segmentNumber);
429 
430  const btVector3 goalPosition = subject->goalBoxPosition();
431 
432  btVector3 desiredHeading = (goalPosition - currentPosVector).normalize();
433 
434  // Get current orientation
436  const btVector3 firstSegment = subject->getSegmentCOMVector(0);
437  const btVector3 secondSegment = subject->getSegmentCOMVector(1);
438 
439  btVector3 currentHeading = (firstSegment - secondSegment).normalize();
440 
441  std::vector<double> state;
442  state.push_back(desiredHeading.getX());
443  state.push_back(desiredHeading.getZ());
444  state.push_back(currentHeading.getX());
445  state.push_back(currentHeading.getZ());
446 
447  double *inputs = new double[m_config.goalStates];
448  for (std::size_t i = 0; i < state.size(); i++)
449  {
450  assert(state[i] >= -1.0 && state[i] <= 1.0);
451  // Don't scale! Sigmoid can handle this range
452  inputs[i]=state[i];
453  }
454 
455  double *output = nn_goal->feedForwardPattern(inputs);
456 
457  vector<double> actions;
458 
459 #if (0)
460  for(int j=0;j<m_config.goalActions;j++)
461  {
462  std::cout << output[j] << " ";
463  }
464  std::cout << std::endl;
465 #endif
466 
467  // Duplicate the actions across segments
468  for (int i = 0; i != nSeg; i++)
469  {
470  for(int j=0;j<m_config.goalActions;j++)
471  {
472  actions.push_back(output[j]);
473  }
474  }
475 
476 
477  transformFeedbackActions(actions);
478 
479  return actions;
480 }
481 
482 std::vector<double> JSONGoalControl::getFeedback(BaseSpineModelLearning& subject)
483 {
484  // Placeholder
485  std::vector<double> feedback;
486 
487  const std::vector<tgSpringCableActuator*>& allCables = subject.getAllMuscles();
488 
489  double *inputs = new double[m_config.numStates];
490 
491  std::size_t n = allCables.size();
492  for(std::size_t i = 0; i != n; i++)
493  {
494  const tgSpringCableActuator& cable = *(allCables[i]);
495  std::vector<double > state = getCableState(cable);
496 
497  // Rescale to 0 to 1 (consider doing this inside getState
498  for (std::size_t i = 0; i < state.size(); i++)
499  {
500  inputs[i]=state[i];
501  }
502 
503  double *output = nn->feedForwardPattern(inputs);
504  vector<double> actions;
505  for(int j=0;j<m_config.numActions;j++)
506  {
507  actions.push_back(output[j]);
508  }
509 
510  transformFeedbackActions(actions);
511 
512  feedback.insert(feedback.end(), actions.begin(), actions.end());
513  }
514 
515 
516  return feedback;
517 }
518 
519 std::vector<double> JSONGoalControl::getCableState(const tgSpringCableActuator& cable)
520 {
521  // For each string, scale value from -1 to 1 based on initial length or max tension of motor
522 
523  std::vector<double> state;
524 
525  // Scale length by starting length
526  const double startLength = cable.getStartLength();
527  state.push_back((cable.getCurrentLength() - startLength) / startLength);
528 
529  const double maxTension = cable.getConfig().maxTens;
530  state.push_back((cable.getTension() - maxTension / 2.0) / maxTension);
531 
532  return state;
533 }
534 
535 void JSONGoalControl::transformFeedbackActions(std::vector<double> & actions)
536 {
537 #if (0) // Only true if actions are applied to all segments
538  assert( actions.size() == numActions);
539 #endif
540  // Scale values back to -1 to +1
541  for( std::size_t i = 0; i < actions.size(); i++)
542  {
543  actions[i] = actions[i] * 2.0 - 1.0;
544  }
545 }
546 
547 double JSONGoalControl::calculateDistanceMoved(const BaseSpineModelGoal* subject) const
548 {
549  std::vector<double> finalConditions = subject->getSegmentCOM(m_config.segmentNumber);
550 
551  const btVector3 goalPos = subject->goalBoxPosition();
552 
553  std::cout << goalPos << std::endl;
554 
555  double x= finalConditions[0] - goalPos.getX();
556  double z= finalConditions[2] - goalPos.getZ();
557  double distanceNew=sqrt(x*x + z*z);
558  double xx=initConditions[0]-goalPos.getX();
559  double zz=initConditions[2]-goalPos.getZ();
560  double distanceOld=sqrt(xx*xx + zz*zz);
561  double distanceMoved=distanceOld-distanceNew;
562 
563  //If you want to calculate only the distance moved independent of the target:
564 // distanceMoved=sqrt((x-xx)*(x-xx)+(z-zz)*(z-zz));
565 
566  return distanceMoved;
567 }
Contains the definition of class ImpedanceControl. $Id$.
void update(std::vector< double > &descCom, double dt)
A controller for the template class BaseSpineModelLearning.
virtual const double getTension() const
virtual void onSetup(BaseSpineModelLearning &subject)
virtual const double getStartLength() const
virtual array_4D scaleEdgeActions(Json::Value edgeParam)
void setConnectivity(const std::vector< tgCPGActuatorControl * > &allStrings, array_4D edgeParams)
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 tf=0.0)
JSONGoalControl(JSONGoalControl::Config config, std::string args, std::string resourcePath="")
Definition of the tgCPGStringControl observer class.
Implementing the tetrahedral complex spine inspired by Tom Flemons.
neuralNetwork * nn
A template base class for a tensegrity spine.
Contains the definition of abstract base class tgSpringCableActuator. Assumes that the string is line...
virtual void onTeardown(BaseSpineModelLearning &subject)
std::vector< double > getGoalFeedback(const BaseSpineModelGoal *subject)
A series of functions to assist with file input/output.
Contains the definition of class tgBasicActuator.
virtual void onStep(BaseSpineModelLearning &subject, double dt)
const Config & getConfig() const
virtual void onStep(tgModel &model, double dt)
Definition of class CPGEquationsFB.
virtual void onSetup(tgModel &model)
virtual const double getCurrentLength() const
void notifyStep(double dt)
void assignNodeNumberFB(CPGEquationsFB &CPGSys, array_2D nodeParams)