NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
SpineGoalControl.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 "SpineGoalControl.h"
28 
29 #include "BaseSpineModelGoal.h"
30 
31 // Should include tgString, but compiler complains since its been
32 // included from BaseSpineModelLearning. Perhaps we should move things
33 // to a cpp over there
35 #include "core/tgBasicActuator.h"
36 #include "tgcreator/tgUtil.h"
39 #include "examples/learningSpines/tgCPGCableControl.h"
40 
41 #include "helpers/FileHelpers.h"
42 
45 
46 #include "util/CPGEquationsFB.h"
47 #include "examples/learningSpines/tgCPGCableControl.h"
48 
49 #include <iterator> // std::iterator
50 #include <string>
51 
52 //#define LOGGING
53 #define USE_KINEMATIC
54 
55 using namespace std;
56 
58  int tm,
59  int om,
60  int param,
61  int segnum,
62  double ct,
63  double la,
64  double ha,
65  double lp,
66  double hp,
67  double kt,
68  double kp,
69  double kv,
70  bool def,
71  double cl,
72  double lf,
73  double hf,
74  double ffMin,
75  double ffMax,
76  double afMin,
77  double afMax,
78  double pfMin,
79  double pfMax,
80  double tf) :
81 BaseSpineCPGControl::Config::Config(ss, tm, om, param, segnum, ct, la, ha,
82  lp, hp, kt, kp, kv, def, cl, lf, hf),
83 freqFeedbackMin(ffMin),
84 freqFeedbackMax(ffMax),
85 ampFeedbackMin(afMin),
86 ampFeedbackMax(afMax),
87 phaseFeedbackMin(pfMin),
88 phaseFeedbackMax(pfMax),
89 tensFeedback(tf)
90 {
91 
92 }
99  std::string args,
100  std::string resourcePath,
101  std::string ec,
102  std::string nc,
103  std::string fc,
104  std::string gc) :
105 BaseSpineCPGControl(config, args, resourcePath, ec, nc),
106 m_config(config),
107 feedbackConfigFilename(fc),
108 // Evolution assumes no pre-processing was done on these names
109 feedbackEvolution(args + "_fb", fc, resourcePath),
110 // Will be overwritten by configuration data
111 feedbackLearning(false),
112 goalConfigFilename(gc),
113 // Evolution assumes no pre-processing was done on these names
114 goalEvolution(args + "_goal", gc, resourcePath),
115 goalLearning(false)
116 {
117  std::string path;
118  if (resourcePath != "")
119  {
120  path = FileHelpers::getResourcePath(resourcePath);
121  }
122  else
123  {
124  path = "";
125  }
126 
127  feedbackConfigData.readFile(path + feedbackConfigFilename);
128  feedbackLearning = feedbackConfigData.getintvalue("learning");
129 
130  goalConfigData.readFile(path + goalConfigFilename);
131  goalLearning = goalConfigData.getintvalue("learning");
132 
133 }
134 
136 {
137  m_pCPGSys = new CPGEquationsFB(200);
138  //Initialize the Learning Adapters
139  nodeAdapter.initialize(&nodeEvolution,
140  nodeLearning,
143  edgeLearning,
144  edgeConfigData);
145  feedbackAdapter.initialize(&feedbackEvolution,
146  feedbackLearning,
147  feedbackConfigData);
148  goalAdapter.initialize(&goalEvolution,
149  goalLearning,
150  goalConfigData);
151  /* Empty vector signifying no state information
152  * All parameters are stateless parameters, so we can get away with
153  * only doing this once
154  */
155  std::vector<double> state;
156  double dt = 0;
157 
158  array_4D edgeParams = scaleEdgeActions(edgeAdapter.step(dt, state));
159  array_2D nodeParams = scaleNodeActions(nodeAdapter.step(dt, state));
160 
161  setupCPGs(subject, nodeParams, edgeParams);
162 
163  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
164 #ifdef LOGGING // Conditional compile for data logging
165  m_dataObserver.onSetup(subject);
166 #endif
167 
168 #if (0) // Conditional Compile for debug info
169  std::cout << *m_pCPGSys << std::endl;
170 #endif
171  m_updateTime = 0.0;
172  bogus = false;
173 
174  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
175  std::cout << goalSubject->goalBoxPosition() << std::endl;
176 }
177 
179 {
180  m_updateTime += dt;
181  if (m_updateTime >= m_config.controlTime)
182  {
183 
184 #if (1)
185  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
186  std::vector<double> desComs = getGoalFeedback(goalSubject);
187 #else // Goal feedback vs others
188  #if (1)
189  std::vector<double> desComs = getFeedback(subject);
190 
191  #else // cable feedback vs no feedback
192  std::size_t numControllers = subject.getNumberofMuslces() * 3;
193 
194  double descendingCommand = 0.0;
195  std::vector<double> desComs (numControllers, descendingCommand);
196  #endif
197 #endif
198  try
199  {
200  m_pCPGSys->update(desComs, m_updateTime);
201  }
202  catch (std::runtime_error& e)
203  {
204  // Stops the trial immediately, lets teardown know it broke
205  bogus = true;
206  throw (e);
207  }
208 
209 #ifdef LOGGING // Conditional compile for data logging
210  m_dataObserver.onStep(subject, m_updateTime);
211 #endif
212  notifyStep(m_updateTime);
213  m_updateTime = 0;
214  }
215 
216  double currentHeight = subject.getSegmentCOM(m_config.segmentNumber)[1];
217 
219  if (currentHeight > 25 || currentHeight < 1.0)
220  {
222  bogus = true;
223  throw std::runtime_error("Height out of range");
224  }
225 }
226 
228 {
229  scores.clear();
230  // @todo - check to make sure we ran for the right amount of time
231 
232  const BaseSpineModelGoal* goalSubject = tgCast::cast<BaseSpineModelLearning, BaseSpineModelGoal>(subject);
233 
234  const double distanceMoved = calculateDistanceMoved(goalSubject);
235 
236  if (bogus)
237  {
238  scores.push_back(-1.0);
239  }
240  else
241  {
242  scores.push_back(distanceMoved);
243  }
244 
247  double totalEnergySpent=0;
248 
249  std::vector<tgSpringCableActuator* > tmpStrings = subject.getAllMuscles();
250 
251  for(int i=0; i<tmpStrings.size(); i++)
252  {
253  tgSpringCableActuator::SpringCableActuatorHistory stringHist = tmpStrings[i]->getHistory();
254 
255  for(int j=1; j<stringHist.tensionHistory.size(); j++)
256  {
257  const double previousTension = stringHist.tensionHistory[j-1];
258  const double previousLength = stringHist.restLengths[j-1];
259  const double currentLength = stringHist.restLengths[j];
260  //TODO: examine this assumption - free spinning motor may require more power
261  double motorSpeed = (currentLength-previousLength);
262  if(motorSpeed > 0) // Vestigial code
263  motorSpeed = 0;
264  const double workDone = previousTension * motorSpeed;
265  totalEnergySpent += workDone;
266  }
267  }
268 
269  scores.push_back(totalEnergySpent);
270 
271  edgeAdapter.endEpisode(scores);
272  nodeAdapter.endEpisode(scores);
273  feedbackAdapter.endEpisode(scores);
274  goalAdapter.endEpisode(scores);
275 
276  delete m_pCPGSys;
277  m_pCPGSys = NULL;
278 
279  for(size_t i = 0; i < m_allControllers.size(); i++)
280  {
281  delete m_allControllers[i];
282  }
283  m_allControllers.clear();
284 }
285 
286 void SpineGoalControl::setupCPGs(BaseSpineModelLearning& subject, array_2D nodeActions, array_4D edgeActions)
287 {
288 
289  std::vector <tgSpringCableActuator*> allMuscles = subject.getAllMuscles();
290 
291  CPGEquationsFB& m_CPGFBSys = *(tgCast::cast<CPGEquations, CPGEquationsFB>(m_pCPGSys));
292 
293  for (std::size_t i = 0; i < allMuscles.size(); i++)
294  {
295 
296  tgPIDController::Config config(20000.0, 0.0, 5.0, true); // Non backdrivable
297  tgCPGCableControl* pStringControl = new tgCPGCableControl(config);
298 
299  allMuscles[i]->attach(pStringControl);
300 
301  // First assign node numbers
302  pStringControl->assignNodeNumberFB(m_CPGFBSys, nodeActions);
303 
304  m_allControllers.push_back(pStringControl);
305  }
306 
307  // Then determine connectivity and setup string
308  for (std::size_t i = 0; i < m_allControllers.size(); i++)
309  {
310  tgCPGActuatorControl * const pStringInfo = m_allControllers[i];
311  assert(pStringInfo != NULL);
312  pStringInfo->setConnectivity(m_allControllers, edgeActions);
313 
314  //String will own this pointer
315  tgImpedanceController* p_ipc = new tgImpedanceController( m_config.tension,
316  m_config.kPosition,
317  m_config.kVelocity);
318  if (m_config.useDefault)
319  {
320  pStringInfo->setupControl(*p_ipc);
321  }
322  else
323  {
324  pStringInfo->setupControl(*p_ipc, m_config.controlLength);
325  }
326  }
327 
328 }
329 
330 array_2D SpineGoalControl::scaleNodeActions
331  (vector< vector <double> > actions)
332 {
333  std::size_t numControllers = nodeConfigData.getintvalue("numberOfControllers");
334  std::size_t numActions = nodeConfigData.getintvalue("numberOfActions");
335 
336  assert( actions.size() == numControllers);
337  assert( actions[0].size() == numActions);
338 
339  array_2D nodeActions(boost::extents[numControllers][numActions]);
340 
341  array_2D limits(boost::extents[2][numActions]);
342 
343  // Check if we need to update limits
344  assert(numActions == 5);
345 
346  limits[0][0] = m_config.lowFreq;
347  limits[1][0] = m_config.highFreq;
348  limits[0][1] = m_config.lowAmp;
349  limits[1][1] = m_config.highAmp;
350  limits[0][2] = m_config.freqFeedbackMin;
351  limits[1][2] = m_config.freqFeedbackMax;
352  limits[0][3] = m_config.ampFeedbackMin;
353  limits[1][3] = m_config.ampFeedbackMax;
354  limits[0][4] = m_config.phaseFeedbackMin;
355  limits[1][4] = m_config.phaseFeedbackMax;
356 
357  // This one is square
358  for( std::size_t i = 0; i < numControllers; i++)
359  {
360  for( std::size_t j = 0; j < numActions; j++)
361  {
362  nodeActions[i][j] = ( actions[i][j] *
363  (limits[1][j] - limits[0][j])) + limits[0][j];
364  }
365  }
366 
367  return nodeActions;
368 }
369 
370 std::vector<double> SpineGoalControl::getFeedback(BaseSpineModelLearning& subject)
371 {
372  // Placeholder
373  std:vector<double> feedback;
374  // Adapter doesn't use this anyway, so just do zero here for now (will trigger errors if it starts to use it =) )
375  const double dt = 0;
376 
377  const std::vector<tgSpringCableActuator*>& allCables = subject.getAllMuscles();
378 
379  std::size_t n = allCables.size();
380  for(std::size_t i = 0; i != n; i++)
381  {
382  const tgSpringCableActuator& cable = *(allCables[i]);
383  std::vector<double > state = getCableState(cable);
384  std::vector< std::vector<double> > actions = feedbackAdapter.step(m_updateTime, state);
385  std::vector<double> cableFeedback = transformFeedbackActions(actions, feedbackConfigData);
386 
387  feedback.insert(feedback.end(), cableFeedback.begin(), cableFeedback.end());
388  }
389 
390 
391  return feedback;
392 }
393 
394 std::vector<double> SpineGoalControl::getGoalFeedback(const BaseSpineModelGoal* subject)
395 {
396 
397  // Add cable feedback to close the low level loop
398  const std::vector<tgSpringCableActuator*>& allCables = subject->getAllMuscles();
399 
400  std::size_t n = allCables.size();
401  std::size_t nA = feedbackConfigData.getintvalue("numberOfActions");
402 
403  // Placeholder
404  std:vector<double> feedback;
405 
406  // Adapter doesn't use this anyway, so just do zero here for now (will trigger errors if it starts to use it =) )
407  const double dt = 0;
408 
409  // Get heading and generate feedback vector
410  std::vector<double> currentPosition = subject->getSegmentCOM(m_config.segmentNumber);
411 
412  assert(currentPosition.size() == 3);
413 
414  btVector3 currentPosVector(currentPosition[0], currentPosition[1], currentPosition[2]);
415 
416  btVector3 goalPosition = subject->goalBoxPosition();
417 
418  btVector3 desiredHeading = (goalPosition - currentPosVector).normalize();
419 
420 
421 #if (1) // Direct to CPG or set impedance controller tensions
422 
423  int m = subject->getSegments() - 1;
424 
425  for (int i = 0; i != m; i++)
426  {
427  // 2D for now to cut down on parameters
428  std::vector<double> state;
429  state.push_back(desiredHeading.getX());
430  state.push_back(desiredHeading.getZ());
431 
432  assert(state[0] >= -1.0 && state[0] <= 1.0);
433  assert(state[1] >= -1.0 && state[1] <= 1.0);
434 
435  std::vector< std::vector<double> > actions = goalAdapter.step(m_updateTime, state);
436  // 24 actions as of 5_12_15, amplitude and phase
437  std::vector<double> segmentFeedback = transformFeedbackActions(actions, goalConfigData);
438 
439  feedback.insert(feedback.end(), segmentFeedback.begin(), segmentFeedback.end());
440  }
441 
442  assert (feedback.size() == n * nA);
443 
444 
445 #else
446 
447  setGoalTensions(subject, desiredHeading);
448 
449  std::vector<double> zeroFB(n * nA, 0.0);
450 
451  feedback.insert(feedback.end(), zeroFB.begin(), zeroFB.end());
452 
453 #endif
454  assert (feedback.size() == n * nA);
455 
456 #if (0) //Switch for cable based feedback
457  for(std::size_t i = 0; i != n; i++)
458  {
459  const tgSpringCableActuator& cable = *(allCables[i]);
460  std::vector<double > state = getCableState(cable);
461  std::vector< std::vector<double> > actions = feedbackAdapter.step(m_updateTime, state);
462  std::vector<double> cableFeedback = transformFeedbackActions(actions, feedbackConfigData);
463 
464  for (std::size_t j = 0; j != nA; j++)
465  {
466  feedback[i * nA + j] += cableFeedback[j];
467  }
468  }
469 #endif
470 
471  return feedback;
472 }
473 
474 void SpineGoalControl::setGoalTensions(const BaseSpineModelGoal* subject, btVector3& desiredHeading)
475 {
476  std::vector<double> state;
477  state.push_back(desiredHeading.getX());
478  state.push_back(desiredHeading.getZ());
479 
480  assert(state[0] >= -1.0 && state[0] <= 1.0);
481  assert(state[1] >= -1.0 && state[1] <= 1.0);
482 
483  std::size_t numActions = goalConfigData.getintvalue("numberOfActions");
484 
485  std::vector< std::vector<double> > actions = goalAdapter.step(m_updateTime, state);
486 
487  for (std::size_t i = 0; i < subject->getSegments() - 1; i++)
488  {
489  for (std::size_t j = 0; j < numActions; j++)
490  {
491  tgCPGCableControl* cableControl = tgCast::cast<tgCPGActuatorControl, tgCPGCableControl>(m_allControllers[i * numActions + j]);
492  cableControl->updateTensionSetpoint(actions[0][j] * m_config.tensFeedback);
493  }
494  }
495 }
496 
497 std::vector<double> SpineGoalControl::getCableState(const tgSpringCableActuator& cable)
498 {
499  // For each string, scale value from -1 to 1 based on initial length or max tension of motor
500 
501  std::vector<double> state;
502 
503  // Scale length by starting length
504  const double startLength = cable.getStartLength();
505  state.push_back((cable.getCurrentLength() - startLength) / startLength);
506 
507  const double maxTension = cable.getConfig().maxTens;
508  state.push_back((cable.getTension() - maxTension / 2.0) / maxTension);
509 
510  return state;
511 }
512 
513 std::vector<double> SpineGoalControl::transformFeedbackActions(std::vector< std::vector<double> >& actions, configuration& configData)
514 {
515  // Placeholder
516  std:vector<double> feedback;
517 
518  std::size_t numControllers = configData.getintvalue("numberOfControllers");
519  std::size_t numActions = configData.getintvalue("numberOfActions");
520 
521  assert( actions.size() == numControllers);
522  assert( actions[0].size() == numActions);
523 
524  // Scale values back to -1 to +1
525  for( std::size_t i = 0; i < numControllers; i++)
526  {
527  for( std::size_t j = 0; j < numActions; j++)
528  {
529  feedback.push_back(actions[i][j] * 2.0 - 1.0);
530  }
531  }
532 
533  return feedback;
534 }
535 
536 double SpineGoalControl::calculateDistanceMoved(const BaseSpineModelGoal* subject) const
537 {
538  std::vector<double> finalConditions = subject->getSegmentCOM(m_config.segmentNumber);
539 
540  const btVector3 goalPos = subject->goalBoxPosition();
541 
542  std::cout << goalPos << std::endl;
543 
544  double x= finalConditions[0] - goalPos.getX();
545  double z= finalConditions[2] - goalPos.getZ();
546  double distanceNew=sqrt(x*x + z*z);
547  double xx=initConditions[0]-goalPos.getX();
548  double zz=initConditions[2]-goalPos.getZ();
549  double distanceOld=sqrt(xx*xx + zz*zz);
550  double distanceMoved=distanceOld-distanceNew;
551 
552  //If you want to calculate only the distance moved independent of the target:
553 // distanceMoved=sqrt((x-xx)*(x-xx)+(z-zz)*(z-zz));
554 
555  return distanceMoved;
556 }
Contains the definition of class ImpedanceControl. $Id$.
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)
void initialize(NeuroEvolution *evo, bool isLearning, configuration config)
void update(std::vector< double > &descCom, double dt)
AnnealEvolution edgeEvolution
virtual const double getTension() const
virtual const double getStartLength() const
void setConnectivity(const std::vector< tgCPGActuatorControl * > &allStrings, array_4D edgeParams)
SpineGoalControl(SpineGoalControl::Config config, std::string args, std::string resourcePath="", std::string ec="edgeConfig.ini", std::string nc="nodeConfig.ini", std::string fc="feedbackConfig.ini", std::string gc="goalConfig.ini")
void updateTensionSetpoint(double newTension)
configuration nodeConfigData
Definition of the tgCPGStringControl observer class.
Implementing the tetrahedral complex spine inspired by Tom Flemons.
A class to read a learning configuration from a .ini file.
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
Contains the definition of class AnnealEvolution. Adapting NeuroEvolution to do Simulated Annealing...
Contains the definition of class tgBasicActuator.
A controller for the template class BaseSpineModelLearning.
const Config & getConfig() const
virtual array_4D scaleEdgeActions(std::vector< std::vector< double > > actions)
virtual void onStep(tgModel &model, double dt)
virtual void onSetup(BaseSpineModelLearning &subject)
Definition of class CPGEquationsFB.
void initialize(AnnealEvolution *evo, bool isLearning, configuration config)
Rand seeding simular to the evolution and terrain classes.
virtual void onSetup(tgModel &model)
virtual void onStep(BaseSpineModelLearning &subject, double dt)
virtual const double getCurrentLength() const
void assignNodeNumberFB(CPGEquationsFB &CPGSys, array_2D nodeParams)