NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
JSONMGCPGGeneralControl.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 
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
37 #include "tgCPGMGActuatorControl.h"
38 #include "tgCPGMGCableControl.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 JSONMGCPGGeneralControl::~JSONMGCPGGeneralControl()
159 {
160  scores.clear();
161 }
162 
164 {
165  // Maximum number of sub-steps allowed by CPG
166  m_pCPGSys = new CPGEquations(2000);
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_3D 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 JSONMGCPGGeneralControl::setupCPGs(BaseQuadModelLearning& subject, array_2D nodeActions, array_3D 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  tgCPGMGCableControl* pStringControl = new tgCPGMGCableControl(config);
215 #else
216  tgCPGMGActuatorControl* pStringControl = new tgCPGMGActuatorControl();
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  tgCPGMGActuatorControl * 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 JSONMGCPGGeneralControl::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 JSONMGCPGGeneralControl::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_3D actionList(boost::extents[m_config.theirMuscles]
398  [m_config.ourMuscles]
399  [m_config.params]);
400 
401  /* Horrid while loop to populate upper diagonal of matrix, since
402  * its symmetric and we want to minimze parameters used in learing
403  * note that j==k will refer to the same muscle
404  * @todo use boost to set up array so storage is only allocated for
405  * elements that are used
406  */
407  int j = 0;
408  int k = 0;
409 
410  // Quirk of the old learning code. Future examples can move forward
411  Json::Value::iterator edgeIt = edgeParam.end();
412 
413  int count = 0;
414 
415 
416  while(j < m_config.theirMuscles)
417  {
418  while(k < m_config.ourMuscles)
419  {
420  if (edgeIt == edgeParam.begin())
421  {
422  std::cout << "ran out before table populated!"
423  << std::endl;
425  break;
426  }
427  else
428  {
429  if (j == k)
430  {
431  // std::cout << "Skipped identical muscle" << std::endl;
432  //Skip since its the same muscle
433  }
434  else
435  {
436  edgeIt--;
437  Json::Value edgeParam = *edgeIt;
438  assert(edgeParam.size() == 2);
439  // Weight from 0 to 1
440  actionList[j][k][0] = edgeParam[0].asDouble();
441  //std::cout << actionList[j][k][0] << " ";
442  // Phase offset from -pi to pi
443  actionList[j][k][1] = edgeParam[1].asDouble() *
444  (range) + lowerLimit;
445  //std::cout << actionList[j][k][1] << std::endl;
446  count++;
447  }
448  }
449  k++;
450  }
451  j++;
452  k = j;
453 
454  }
455 
456 
457  std::cout<< "Params used: " << count << std::endl;
458 
459  assert(edgeParam.begin() == edgeIt);
460 
461  return actionList;
462 }
463 array_2D JSONMGCPGGeneralControl::scaleNodeActions
464  (Json::Value actions)
465 {
466  std::size_t numControllers = actions.size();
467  std::size_t numActions = actions[0].size();
468 
469  array_2D nodeActions(boost::extents[numControllers][numActions]);
470 
471  array_2D limits(boost::extents[2][numActions]);
472 
473  // Check if we need to update limits
474  assert(numActions == 2);
475 
476  limits[0][0] = m_config.lowFreq;
477  limits[1][0] = m_config.highFreq;
478  limits[0][1] = m_config.lowAmp;
479  limits[1][1] = m_config.highAmp;
480 
481  Json::Value::iterator nodeIt = actions.begin();
482 
483  // This one is square
484  for( std::size_t i = 0; i < numControllers; i++)
485  {
486  Json::Value nodeParam = *nodeIt;
487  for( std::size_t j = 0; j < numActions; j++)
488  {
489  nodeActions[i][j] = ( (nodeParam.get(j, 0.0)).asDouble() *
490  (limits[1][j] - limits[0][j])) + limits[0][j];
491  }
492  nodeIt++;
493  }
494 
495  return nodeActions;
496 }
Contains the definition of class ImpedanceControl. $Id$.
Definition of the tgCPGStringControl observer class.
void update(std::vector< double > &descCom, double dt)
virtual void onTeardown(BaseQuadModelLearning &subject)
void setConnectivity(const std::vector< tgCPGMGActuatorControl * > &allStrings, array_3D edgeParams)
An adaptation of JSONCPGControl that utilizes JSON for parameters.
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
virtual array_3D scaleEdgeActions(Json::Value edgeParam)
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 onSetup(BaseQuadModelLearning &subject)
A controller for the template class BaseSpineModelLearning.
Definition of class CPGEquations.
virtual void onStep(tgModel &model, double dt)
Definition of class CPGNode.
JSONMGCPGGeneralControl(JSONMGCPGGeneralControl::Config config, std::string args, std::string resourcePath="")
virtual void setupCPGs(BaseQuadModelLearning &subject, array_2D nodeActions, array_3D edgeActions)
virtual void onSetup(tgModel &model)
virtual void onStep(BaseQuadModelLearning &subject, double dt)