NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
JSONGoalTension.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 "JSONGoalTension.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 
66  std::string args,
67  std::string resourcePath) :
68 JSONGoalControl(config, args, resourcePath)
69 {
70  // Path and filename handled by base class
71 
72 }
73 
74 JSONGoalTension::~JSONGoalTension()
75 {
76 }
77 
79 {
80  m_pCPGSys = new CPGEquationsFB(200);
81 
82  Json::Value root; // will contains the root value after parsing.
83  Json::Reader reader;
84 
85  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
86  if ( !parsingSuccessful )
87  {
88  // report to the user the failure and their locations in the document.
89  std::cout << "Failed to parse configuration\n"
90  << reader.getFormattedErrorMessages();
91  throw std::invalid_argument("Bad filename for JSON");
92  }
93  // Get the value of the member of root named 'encoding', return 'UTF-8' if there is no
94  // such member.
95  Json::Value nodeVals = root.get("nodeVals", "UTF-8");
96  Json::Value edgeVals = root.get("edgeVals", "UTF-8");
97 
98  nodeVals = nodeVals.get("params", "UTF-8");
99  edgeVals = edgeVals.get("params", "UTF-8");
100 
101  array_4D edgeParams = scaleEdgeActions(edgeVals);
102  array_2D nodeParams = scaleNodeActions(nodeVals);
103 
104  setupCPGs(subject, nodeParams, edgeParams);
105 
106  Json::Value feedbackParams = root.get("feedbackVals", "UTF-8");
107  feedbackParams = feedbackParams.get("params", "UTF-8");
108 
109  // Setup neural network
110  m_config.numStates = feedbackParams.get("numStates", "UTF-8").asInt();
111  m_config.numActions = feedbackParams.get("numActions", "UTF-8").asInt();
112  m_config.numHidden = feedbackParams.get("numHidden", "UTF-8").asInt();
113 
114  feedbackWeights.resize(boost::extents[m_config.numActions][m_config.numStates]);
115  setupFeedbackWeights(nodeParams);
116 
117  Json::Value goalParams = root.get("goalVals", "UTF-8");
118  goalParams = goalParams.get("params", "UTF-8");
119 
120  // Setup neural network
121  m_config.goalStates = goalParams.get("numStates", "UTF-8").asInt();
122  m_config.goalActions = goalParams.get("numActions", "UTF-8").asInt();
123  m_config.goalHidden = goalParams.get("numHidden", "UTF-8").asInt();
124 
125  std::string nnFile_goal = controlFilePath + goalParams.get("neuralFilename", "UTF-8").asString();
126 
127  nn_goal = new neuralNetwork(m_config.goalStates, m_config.goalHidden, m_config.goalActions);
128 
129  nn_goal->loadWeights(nnFile_goal.c_str());
130 
131  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
132 #ifdef LOGGING // Conditional compile for data logging
133  m_dataObserver.onSetup(subject);
134 #endif
135 
136 #if (0) // Conditional Compile for debug info
137  std::cout << *m_pCPGSys << std::endl;
138 #endif
139  m_updateTime = 0.0;
140  bogus = false;
141 }
142 
144 {
145  m_updateTime += dt;
146  if (m_updateTime >= m_config.controlTime)
147  {
148 #if (1) // Goal and cable
149 
150  std::vector<double> desComs = getFeedback(subject);
151 
152  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
153 
154  getGoalFeedback(goalSubject);
155 
156 #else // Nothing
157  std::size_t numControllers = subject.getNumberofMuslces() * 3;
158 
159  double descendingCommand = 0.0;
160  std::vector<double> desComs (numControllers, descendingCommand);
161 #endif
162  try
163  {
164  m_pCPGSys->update(desComs, m_updateTime);
165  }
166  catch (std::runtime_error& e)
167  {
168  // Stops the trial immediately, lets teardown know it broke
169  bogus = true;
170  throw (e);
171  }
172 
173 #ifdef LOGGING // Conditional compile for data logging
174  m_dataObserver.onStep(subject, m_updateTime);
175 #endif
176  notifyStep(m_updateTime);
177  m_updateTime = 0;
178  }
179 
180  double currentHeight = subject.getSegmentCOM(m_config.segmentNumber)[1];
181 
183  if (currentHeight > 25 || currentHeight < 1.0)
184  {
186  bogus = true;
187  throw std::runtime_error("Height out of range");
188  }
189 }
190 
192 {
193  scores.clear();
194  // @todo - check to make sure we ran for the right amount of time
195 
196  std::vector<double> finalConditions = subject.getSegmentCOM(m_config.segmentNumber);
197 
198  const double newX = finalConditions[0];
199  const double newZ = finalConditions[2];
200  const double oldX = initConditions[0];
201  const double oldZ = initConditions[2];
202 
203  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
204 
205  const double distanceMoved = calculateDistanceMoved(goalSubject);
206 
207  if (bogus)
208  {
209  scores.push_back(-1.0);
210  }
211  else
212  {
213  scores.push_back(distanceMoved);
214  }
215 
218  double totalEnergySpent=0;
219 
220  std::vector<tgSpringCableActuator* > tmpStrings = subject.getAllMuscles();
221 
222  for(std::size_t i=0; i<tmpStrings.size(); i++)
223  {
224  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
225 
226  for(std::size_t j=1; j<stringHist.tensionHistory.size(); j++)
227  {
228  const double previousTension = stringHist.tensionHistory[j-1];
229  const double previousLength = stringHist.restLengths[j-1];
230  const double currentLength = stringHist.restLengths[j];
231  //TODO: examine this assumption - free spinning motor may require more power
232  double motorSpeed = (currentLength-previousLength);
233  if(motorSpeed > 0) // Vestigial code
234  motorSpeed = 0;
235  const double workDone = previousTension * motorSpeed;
236  totalEnergySpent += workDone;
237  }
238  }
239 
240  scores.push_back(totalEnergySpent);
241 
242  std::cout << "Dist travelled " << scores[0] << std::endl;
243 
244  Json::Value root; // will contains the root value after parsing.
245  Json::Reader reader;
246 
247  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
248  if ( !parsingSuccessful )
249  {
250  // report to the user the failure and their locations in the document.
251  std::cout << "Failed to parse configuration\n"
252  << reader.getFormattedErrorMessages();
253  throw std::invalid_argument("Bad filename for JSON");
254  }
255 
256  Json::Value prevScores = root.get("scores", Json::nullValue);
257 
258  Json::Value subScores;
259  subScores["distance"] = scores[0];
260  subScores["energy"] = totalEnergySpent;
261 
262  prevScores.append(subScores);
263  root["scores"] = prevScores;
264 
265  ofstream payloadLog;
266  payloadLog.open(controlFilename.c_str(),ofstream::out);
267 
268  payloadLog << root << std::endl;
269 
270  delete m_pCPGSys;
271  m_pCPGSys = NULL;
272 
273  for(size_t i = 0; i < m_allControllers.size(); i++)
274  {
275  delete m_allControllers[i];
276  }
277  m_allControllers.clear();
278 
279  delete nn_goal;
280 }
281 
282 array_2D JSONGoalTension::scaleNodeActions (Json::Value actions)
283 {
284  std::size_t numControllers = actions.size();
285  std::size_t numActions = actions[0].size();
286 
287  array_2D nodeActions(boost::extents[numControllers][numActions]);
288 
289  array_2D limits(boost::extents[2][numActions]);
290 
291  // Check if we need to update limits
292  assert(numActions >= 5);
293 
294  limits[0][0] = m_config.lowFreq;
295  limits[1][0] = m_config.highFreq;
296  limits[0][1] = m_config.lowAmp;
297  limits[1][1] = m_config.highAmp;
298  limits[0][2] = m_config.freqFeedbackMin;
299  limits[1][2] = m_config.freqFeedbackMax;
300  limits[0][3] = m_config.ampFeedbackMin;
301  limits[1][3] = m_config.ampFeedbackMax;
302  limits[0][4] = m_config.phaseFeedbackMin;
303  limits[1][4] = m_config.phaseFeedbackMax;
304 
305  for (std::size_t i = 5; i < numActions; i++)
306  {
307  limits[0][i] = -1.0;
308  limits[1][i] = 1.0;
309  }
310 
311  Json::Value::iterator nodeIt = actions.begin();
312 
313  // This one is square
314  for( std::size_t i = 0; i < numControllers; i++)
315  {
316  Json::Value nodeParam = *nodeIt;
317  for( std::size_t j = 0; j < numActions; j++)
318  {
319  nodeActions[i][j] = ( (nodeParam.get(j, 0.0)).asDouble() *
320  (limits[1][j] - limits[0][j])) + limits[0][j];
321  }
322  nodeIt++;
323  }
324 
325  return nodeActions;
326 }
327 
328 std::vector<double> JSONGoalTension::getGoalFeedback(const BaseSpineModelGoal* subject)
329 {
330  const int nSeg = subject->getSegments() - 1;
331 
332  // Only one segment has no actuators, and means we can't get heading.
333  assert(nSeg > 0);
334 
335  // Get heading and generate feedback vector
336  const btVector3 currentPosVector = subject->getSegmentCOMVector(m_config.segmentNumber);
337 
338  const btVector3 goalPosition = subject->goalBoxPosition();
339 
340  btVector3 desiredHeading = (goalPosition - currentPosVector).normalize();
341 
342  // Get current orientation
344  const btVector3 firstSegment = subject->getSegmentCOMVector(0);
345  const btVector3 secondSegment = subject->getSegmentCOMVector(1);
346 
347  btVector3 currentHeading = (firstSegment - secondSegment).normalize();
348 
349  std::vector<double> state;
350  state.push_back(desiredHeading.getX());
351  state.push_back(desiredHeading.getZ());
352  state.push_back(currentHeading.getX());
353  state.push_back(currentHeading.getZ());
354 
355  double *inputs = new double[m_config.goalStates];
356  for (std::size_t i = 0; i < state.size(); i++)
357  {
358  assert(state[i] >= -1.0 && state[i] <= 1.0);
359  // Don't scale! Sigmoid can handle this range
360  inputs[i]=state[i];
361  }
362 
363  double *output = nn_goal->feedForwardPattern(inputs);
364 
365  vector<double> actions;
366 
367 #if (0)
368  for(int j=0;j<m_config.goalActions;j++)
369  {
370  std::cout << output[j] << " ";
371  }
372  std::cout << std::endl;
373 #endif
374 
375  for(int j=0;j<m_config.goalActions;j++)
376  {
377  actions.push_back(output[j]);
378  }
379 
380  transformFeedbackActions(actions);
381 
382  assert(m_config.goalActions * nSeg == m_allControllers.size());
383 
384  // Duplicate the actions across segments
385  for (int i = 0; i != nSeg; i++)
386  {
387  for(int j=0;j<m_config.goalActions;j++)
388  {
389  m_allControllers[i * m_config.goalActions + j]->updateTensionSetpoint(actions[j] * m_config.tensFeedback + m_config.tensFeedback);
390  }
391  }
392 
393 
394 
395  return actions;
396 }
397 
398 std::vector<double> JSONGoalTension::getFeedback(BaseSpineModelLearning& subject)
399 {
400  // Placeholder
401  std::vector<double> feedback;
402 
403  const std::vector<tgSpringCableActuator*>& allCables = subject.getAllMuscles();
404 
405 
406  std::size_t n = allCables.size();
407  for(std::size_t i = 0; i != n; i++)
408  {
409  const tgSpringCableActuator& cable = *(allCables[i]);
410  std::vector<double > state = getCableState(cable);
411 
412  vector<double> actions;
413  for(int j=0;j<m_config.numActions;j++)
414  {
415  // Provide tension to all inputs
416  actions.push_back(state[0] * feedbackWeights[j][0] + state[1] * feedbackWeights[j][1]);
417  }
418 
419  transformFeedbackActions(actions);
420 
421  feedback.insert(feedback.end(), actions.begin(), actions.end());
422  }
423 
424 
425  return feedback;
426 }
428 {
429 
431  int k = 5;
432 
433  for(int i = 0; i < m_config.numActions; i++)
434  {
435  for(int j = 0; j < m_config.numStates; j++)
436  {
437  feedbackWeights[i][j] = nodeVals[0][k];
438  k++;
439  }
440  }
441 
442 }
Contains the definition of class ImpedanceControl. $Id$.
void update(std::vector< double > &descCom, double dt)
A controller for the template class BaseSpineModelLearning.
virtual array_4D scaleEdgeActions(Json::Value edgeParam)
Definition of the tgCPGStringControl observer class.
Implementing the tetrahedral complex spine inspired by Tom Flemons.
A template base class for a tensegrity spine.
Contains the definition of abstract base class tgSpringCableActuator. Assumes that the string is line...
A series of functions to assist with file input/output.
virtual void onTeardown(BaseSpineModelLearning &subject)
Contains the definition of class tgBasicActuator.
virtual void setupFeedbackWeights(array_2D nodeVals)
virtual std::vector< double > getGoalFeedback(const BaseSpineModelGoal *subject)
virtual void onStep(tgModel &model, double dt)
JSONGoalTension(JSONGoalControl::Config config, std::string args, std::string resourcePath="")
Definition of class CPGEquationsFB.
virtual void onSetup(tgModel &model)
virtual void onSetup(BaseSpineModelLearning &subject)
virtual void onStep(BaseSpineModelLearning &subject, double dt)
void notifyStep(double dt)