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