NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
JSONQuadCPGControl.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 "JSONQuadCPGControl.h"
28 
29 #include <string>
30 
31 
32 // Should include tgString, but compiler complains since its been
33 // included from BaseSpineModelLearning. Perhaps we should move things
34 // to a cpp over there
38 #include "examples/learningSpines/tgCPGCableControl.h"
40 
41 #include "helpers/FileHelpers.h"
42 
43 #include "util/CPGEquations.h"
44 #include "util/CPGNode.h"
45 
46 // JSON
47 #include <json/json.h>
48 
49 #include <exception>
50 
51 //#define LOGGING
52 
53 using namespace std;
54 
56  int tm,
57  int om,
58  int param,
59  int segnum,
60  double ct,
61  double la,
62  double ha,
63  double lp,
64  double hp,
65  double kt,
66  double kp,
67  double kv,
68  bool def,
69  double cl,
70  double lf,
71  double hf) :
72  segmentSpan(ss),
73  theirMuscles(tm),
74  ourMuscles(om),
75  params(param),
76  segmentNumber(segnum),
77  controlTime(ct),
78  lowAmp(la),
79  highAmp(ha),
80  lowFreq(lf),
81  highFreq(hf),
82  lowPhase(lp),
83  highPhase(hp),
84  tension(kt),
85  kPosition(kp),
86  kVelocity(kv),
87  useDefault(def),
88  controlLength(cl)
89 {
90  if (ss <= 0)
91  {
92  throw std::invalid_argument("segmentSpan parameter is negative.");
93  }
94  else if (tm <= 0)
95  {
96  throw std::invalid_argument("theirMuscles parameter is negative.");
97  }
98  else if (om <= 0)
99  {
100  throw std::invalid_argument("Our Muscles parameter is negative.");
101  }
102  else if (param <= 0)
103  {
104  throw std::invalid_argument("Edge parameters is negative.");
105  }
106  else if (segnum < 0)
107  {
108  throw std::invalid_argument("Segment number is negative.");
109  }
110  else if (ct < 0.0)
111  {
112  throw std::invalid_argument("control time is negative.");
113  }
114  else if (kt < 0.0)
115  {
116  throw std::invalid_argument("impedance control tension is negative.");
117  }
118  else if (kp < 0.0)
119  {
120  throw std::invalid_argument("impedance control position is negative.");
121  }
122  else if (kv < 0.0)
123  {
124  throw std::invalid_argument("impedance control velocity is negative.");
125  }
126  else if (cl < 0.0)
127  {
128  throw std::invalid_argument("Control Length is negative.");
129  }
130 }
131 
138  std::string args,
139  std::string resourcePath) :
140 m_pCPGSys(NULL),
141 m_config(config),
142 m_dataObserver("logs/TCData"),
143 m_updateTime(0.0),
144 bogus(false)
145 {
146  if (resourcePath != "")
147  {
148  controlFilePath = FileHelpers::getResourcePath(resourcePath);
149  }
150  else
151  {
152  controlFilePath = "";
153  }
154 
155  controlFilename = controlFilePath + args;
156 }
157 
158 JSONQuadCPGControl::~JSONQuadCPGControl()
159 {
160  scores.clear();
161 }
162 
164 {
165  // Maximum number of sub-steps allowed by CPG
166  m_pCPGSys = new CPGEquations(200);
167  //Initialize the Learning Adapters
168 
169  Json::Value root; // will contains the root value after parsing.
170  Json::Reader reader;
171 
172  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
173  if ( !parsingSuccessful )
174  {
175  // report to the user the failure and their locations in the document.
176  std::cout << "Failed to parse configuration\n"
177  << reader.getFormattedErrorMessages();
178  throw std::invalid_argument("Bad filename for JSON");
179  }
180  // Get the value of the member of root named 'encoding', return 'UTF-8' if there is no
181  // such member.
182  Json::Value nodeVals = root.get("nodeVals", "UTF-8");
183  Json::Value edgeVals = root.get("edgeVals", "UTF-8");
184 
185  nodeVals = nodeVals.get("params", "UTF-8");
186  edgeVals = edgeVals.get("params", "UTF-8");
187 
188  array_4D edgeParams = scaleEdgeActions(edgeVals);
189  array_2D nodeParams = scaleNodeActions(nodeVals);
190 
191  setupCPGs(subject, nodeParams, edgeParams);
192 
193  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
194 #ifdef LOGGING // Conditional compile for data logging
195  m_dataObserver.onSetup(subject);
196 #endif
197 
198 #if (0) // Conditional Compile for debug info
199  std::cout << *m_pCPGSys << std::endl;
200 #endif
201  m_updateTime = 0.0;
202  bogus = false;
203 }
204 
205 void JSONQuadCPGControl::setupCPGs(BaseQuadModelLearning& subject, array_2D nodeActions, array_4D edgeActions)
206 {
207 
208  std::vector <tgSpringCableActuator*> allMuscles = subject.getAllMuscles();
209 
210  for (std::size_t i = 0; i < allMuscles.size(); i++)
211  {
212 #if (1)
213  tgPIDController::Config config(20000.0, 0.0, 5.0, true); // Non backdrivable
214  tgCPGCableControl* pStringControl = new tgCPGCableControl(config);
215 #else
216  tgCPGActuatorControl* pStringControl = new tgCPGActuatorControl();
217 #endif // Update for kinematic cables
218  allMuscles[i]->attach(pStringControl);
219 
220  m_allControllers.push_back(pStringControl);
221  }
222 
224  // First assign node numbers to the info Classes
225  for (std::size_t i = 0; i < m_allControllers.size(); i++)
226  {
227  m_allControllers[i]->assignNodeNumber(*m_pCPGSys, nodeActions);
228  }
229 
230  // Then determine connectivity and setup string
231  for (std::size_t i = 0; i < m_allControllers.size(); i++)
232  {
233  tgCPGActuatorControl * const pStringInfo = m_allControllers[i];
234  assert(pStringInfo != NULL);
235  pStringInfo->setConnectivity(m_allControllers, edgeActions);
236 
237  //String will own this pointer
238  tgImpedanceController* p_ipc = new tgImpedanceController( m_config.tension,
239  m_config.kPosition,
240  m_config.kVelocity);
241  if (m_config.useDefault)
242  {
243  pStringInfo->setupControl(*p_ipc);
244  }
245  else
246  {
247  pStringInfo->setupControl(*p_ipc, m_config.controlLength);
248  }
249  }
250 
251 }
252 
254 {
255  m_updateTime += dt;
256  if (m_updateTime >= m_config.controlTime)
257  {
258  std::size_t numControllers = subject.getNumberofMuslces();
259 
260  double descendingCommand = 2.0;
261  std::vector<double> desComs (numControllers, descendingCommand);
262 
263  m_pCPGSys->update(desComs, m_updateTime);
264 #ifdef LOGGING // Conditional compile for data logging
265  m_dataObserver.onStep(subject, m_updateTime);
266 #endif
267  notifyStep(m_updateTime);
268  m_updateTime = 0;
269  }
270 
271  double currentHeight = subject.getSegmentCOM(m_config.segmentNumber)[1];
272 
274  if (currentHeight > 25 || currentHeight < 1.0)
275  {
277  bogus = true;
278  }
279 }
280 
282 {
283  scores.clear();
284  // @todo - check to make sure we ran for the right amount of time
285 
286  std::vector<double> finalConditions = subject.getSegmentCOM(m_config.segmentNumber);
287 
288  const double newX = finalConditions[0];
289  const double newZ = finalConditions[2];
290  const double oldX = initConditions[0];
291  const double oldZ = initConditions[2];
292 
293  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
294  (newZ-oldZ) * (newZ-oldZ));
295 
296  if (bogus)
297  {
298  scores.push_back(-1.0);
299  }
300  else
301  {
302  scores.push_back(distanceMoved);
303  }
304 
307  double totalEnergySpent=0;
308 
309  std::vector<tgSpringCableActuator* > tmpStrings = subject.getAllMuscles();
310 
311  for(std::size_t i=0; i<tmpStrings.size(); i++)
312  {
313  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
314 
315  for(std::size_t j=1; j<stringHist.tensionHistory.size(); j++)
316  {
317  const double previousTension = stringHist.tensionHistory[j-1];
318  const double previousLength = stringHist.restLengths[j-1];
319  const double currentLength = stringHist.restLengths[j];
320  //TODO: examine this assumption - free spinning motor may require more power
321  double motorSpeed = (currentLength-previousLength);
322  if(motorSpeed > 0) // Vestigial code
323  motorSpeed = 0;
324  const double workDone = previousTension * motorSpeed;
325  totalEnergySpent += workDone;
326  }
327  }
328 
329  scores.push_back(totalEnergySpent);
330 
331  std::cout << "Dist travelled " << scores[0] << std::endl;
332 
333  Json::Value root; // will contains the root value after parsing.
334  Json::Reader reader;
335 
336  bool parsingSuccessful = reader.parse( FileHelpers::getFileString(controlFilename.c_str()), root );
337  if ( !parsingSuccessful )
338  {
339  // report to the user the failure and their locations in the document.
340  std::cout << "Failed to parse configuration\n"
341  << reader.getFormattedErrorMessages();
342  throw std::invalid_argument("Bad filename for JSON");
343  }
344 
345  Json::Value prevScores = root.get("scores", Json::nullValue);
346 
347  Json::Value subScores;
348  subScores["distance"] = scores[0];
349  subScores["energy"] = totalEnergySpent;
350 
351  prevScores.append(subScores);
352  root["scores"] = prevScores;
353 
354  ofstream payloadLog;
355  payloadLog.open(controlFilename.c_str(),ofstream::out);
356 
357  payloadLog << root << std::endl;
358 
359  delete m_pCPGSys;
360  m_pCPGSys = NULL;
361 
362  for(size_t i = 0; i < m_allControllers.size(); i++)
363  {
364  delete m_allControllers[i];
365  }
366  m_allControllers.clear();
367 }
368 
369 const double JSONQuadCPGControl::getCPGValue(std::size_t i) const
370 {
371  // Error handling on input done in CPG_Equations
372  return (*m_pCPGSys)[i];
373 }
374 
375 double JSONQuadCPGControl::getScore() const
376 {
377  if (scores.size() == 2)
378  {
379  return scores[0];
380  }
381  else
382  {
383  throw std::runtime_error("Called before scores were obtained!");
384  }
385 }
386 
387 
389  (Json::Value edgeParam)
390 {
391  assert(edgeParam[0].size() == 2);
392 
393  double lowerLimit = m_config.lowPhase;
394  double upperLimit = m_config.highPhase;
395  double range = upperLimit - lowerLimit;
396 
397  array_4D actionList(boost::extents[m_config.segmentSpan]
398  [m_config.theirMuscles]
399  [m_config.ourMuscles]
400  [m_config.params]);
401 
402  /* Horrid while loop to populate upper diagonal of matrix, since
403  * its symmetric and we want to minimze parameters used in learing
404  * note that i==1, j==k will refer to the same muscle
405  * @todo use boost to set up array so storage is only allocated for
406  * elements that are used
407  */
408  int i = 0;
409  int j = 0;
410  int k = 0;
411 
412  // Quirk of the old learning code. Future examples can move forward
413  Json::Value::iterator edgeIt = edgeParam.end();
414 
415  int count = 0;
416 
417  while (i < m_config.segmentSpan)
418  {
419  while(j < m_config.theirMuscles)
420  {
421  while(k < m_config.ourMuscles)
422  {
423  if (edgeIt == edgeParam.begin())
424  {
425  std::cout << "ran out before table populated!"
426  << std::endl;
428  break;
429  }
430  else
431  {
432  if (i == 1 && j == k)
433  {
434  // std::cout << "Skipped identical muscle" << std::endl;
435  //Skip since its the same muscle
436  }
437  else
438  {
439  edgeIt--;
440  Json::Value edgeParam = *edgeIt;
441  assert(edgeParam.size() == 2);
442  // Weight from 0 to 1
443  actionList[i][j][k][0] = edgeParam[0].asDouble();
444  //std::cout << actionList[i][j][k][0] << " ";
445  // Phase offset from -pi to pi
446  actionList[i][j][k][1] = edgeParam[1].asDouble() *
447  (range) + lowerLimit;
448  //std::cout << actionList[i][j][k][1] << std::endl;
449  count++;
450  }
451  }
452  k++;
453  }
454  j++;
455  k = j;
456 
457  }
458  j = 0;
459  k = 0;
460  i++;
461  }
462 
463  std::cout<< "Params used: " << count << std::endl;
464 
465  assert(edgeParam.begin() == edgeIt);
466 
467  return actionList;
468 }
469 array_2D JSONQuadCPGControl::scaleNodeActions
470  (Json::Value actions)
471 {
472  std::size_t numControllers = actions.size();
473  std::size_t numActions = actions[0].size();
474 
475  array_2D nodeActions(boost::extents[numControllers][numActions]);
476 
477  array_2D limits(boost::extents[2][numActions]);
478 
479  // Check if we need to update limits
480  assert(numActions == 2);
481 
482  limits[0][0] = m_config.lowFreq;
483  limits[1][0] = m_config.highFreq;
484  limits[0][1] = m_config.lowAmp;
485  limits[1][1] = m_config.highAmp;
486 
487  Json::Value::iterator nodeIt = actions.begin();
488 
489  // This one is square
490  for( std::size_t i = 0; i < numControllers; i++)
491  {
492  Json::Value nodeParam = *nodeIt;
493  for( std::size_t j = 0; j < numActions; j++)
494  {
495  nodeActions[i][j] = ( (nodeParam.get(j, 0.0)).asDouble() *
496  (limits[1][j] - limits[0][j])) + limits[0][j];
497  }
498  nodeIt++;
499  }
500 
501  return nodeActions;
502 }
Contains the definition of class ImpedanceControl. $Id$.
virtual void onStep(BaseQuadModelLearning &subject, double dt)
virtual void onSetup(BaseQuadModelLearning &subject)
void update(std::vector< double > &descCom, double dt)
void setConnectivity(const std::vector< tgCPGActuatorControl * > &allStrings, array_4D edgeParams)
JSONQuadCPGControl(JSONQuadCPGControl::Config config, std::string args, std::string resourcePath="")
Definition of the tgCPGStringControl observer class.
Contains the definition of abstract base class tgSpringCableActuator. Assumes that the string is line...
A series of functions to assist with file input/output.
static std::string getResourcePath(std::string relPath)
Definition: FileHelpers.cpp:40
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)
virtual void onTeardown(BaseQuadModelLearning &subject)
A controller for the template class BaseSpineModelLearning.
Definition of class CPGEquations.
virtual void onStep(tgModel &model, double dt)
virtual void setupCPGs(BaseQuadModelLearning &subject, array_2D nodeActions, array_4D edgeActions)
Definition of class CPGNode.
virtual array_4D scaleEdgeActions(Json::Value edgeParam)
virtual void onSetup(tgModel &model)