NTRT Simulator
 All Classes Files Functions Variables Typedefs Friends Pages
BaseSpineCPGControl.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 "BaseSpineCPGControl.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
35 #include "core/tgLinearString.h"
36 #include "core/ImpedanceControl.h"
37 #include "tgCPGStringControl.h"
38 
41 
42 #include "util/CPGEdge.h"
43 #include "util/CPGEquations.h"
44 #include "util/CPGNode.h"
45 
47  int tm,
48  int om,
49  int param,
50  int segnum,
51  double ct,
52  double la,
53  double ha,
54  double lp,
55  double hp,
56  double kt,
57  double kp,
58  double kv,
59  bool def,
60  double cl) :
61  segmentSpan(ss),
62  theirMuscles(tm),
63  ourMuscles(om),
64  params(param),
65  segmentNumber(segnum),
66  controlTime(ct),
67  lowAmp(la),
68  highAmp(ha),
69  lowPhase(lp),
70  highPhase(hp),
71  tension(kt),
72  kPosition(kp),
73  kVelocity(kv),
74  useDefault(def),
75  controlLength(cl)
76 {
77  if (ss <= 0)
78  {
79  throw std::invalid_argument("segmentSpan parameter is negative.");
80  }
81  else if (tm <= 0)
82  {
83  throw std::invalid_argument("theirMuscles parameter is negative.");
84  }
85  else if (om <= 0)
86  {
87  throw std::invalid_argument("Our Muscles parameter is negative.");
88  }
89  else if (param <= 0)
90  {
91  throw std::invalid_argument("Edge parameters is negative.");
92  }
93  else if (segnum < 0)
94  {
95  throw std::invalid_argument("Segment number is negative.");
96  }
97  else if (ct < 0.0)
98  {
99  throw std::invalid_argument("control time is negative.");
100  }
101  else if (kt < 0.0)
102  {
103  throw std::invalid_argument("impedance control tension is negative.");
104  }
105  else if (kp < 0.0)
106  {
107  throw std::invalid_argument("impedance control position is negative.");
108  }
109  else if (kv < 0.0)
110  {
111  throw std::invalid_argument("impedance control velocity is negative.");
112  }
113  else if (cl < 0.0)
114  {
115  throw std::invalid_argument("Control Length is negative.");
116  }
117 }
118 
125  std::string args,
126  std::string ec,
127  std::string nc) :
128 m_config(config),
129 edgeConfigFilename(ec),
130 nodeConfigFilename(nc),
131 edgeEvolution(args + "_edge", edgeConfigFilename),
132 // Can't have identical args or they'll overwrite each other
133 nodeEvolution(args + "_node", nodeConfigFilename),
134 // Will be overwritten by configuration data
135 nodeLearning(false),
136 edgeLearning(false),
137 m_dataObserver("logs/TCData"),
138 m_pCPGSys(NULL),
139 m_updateTime(0.0)
140 {
141  nodeConfigData.readFile(nodeConfigFilename);
142  edgeConfigData.readFile(edgeConfigFilename);
143  nodeLearning = nodeConfigData.getintvalue("learning");
144  edgeLearning = edgeConfigData.getintvalue("learning");
145 
146 }
147 
149 {
150  m_pCPGSys = new CPGEquations();
151  //Initialize the Learning Adapters
152  nodeAdapter.initialize(&nodeEvolution,
153  nodeLearning,
154  nodeConfigData);
155  edgeAdapter.initialize(&edgeEvolution,
156  edgeLearning,
157  edgeConfigData);
158  /* Empty vector signifying no state information
159  * All parameters are stateless parameters, so we can get away with
160  * only doing this once
161  */
162  std::vector<double> state;
163  double dt = 0;
164 
165  array_4D edgeParams = scaleEdgeActions(edgeAdapter.step(dt, state));
166  array_2D nodeParams = scaleNodeActions(nodeAdapter.step(dt, state));
167 
168  setupCPGs(subject, nodeParams, edgeParams);
169 
170  initConditions = subject.getSegmentCOM(m_config.segmentNumber);
171 #if (0) // Conditional compile for data logging
172  m_dataObserver.onSetup(subject);
173 #endif
174 
175 #if (0) // Conditional Compile for debug info
176  std::cout << *m_pCPGSys << std::endl;
177 #endif
178  m_updateTime = 0.0;
179 }
180 
181 void BaseSpineCPGControl::setupCPGs(BaseSpineModelLearning& subject, array_2D nodeActions, array_4D edgeActions)
182 {
183 
184  std::vector <tgLinearString*> allMuscles = subject.getAllMuscles();
185 
186  for (std::size_t i = 0; i < allMuscles.size(); i++)
187  {
188  tgCPGStringControl* pStringControl = new tgCPGStringControl();
189  allMuscles[i]->attach(pStringControl);
190  m_allControllers.push_back(pStringControl);
191  }
192 
194  // First assign node numbers to the info Classes
195  for (std::size_t i = 0; i < m_allControllers.size(); i++)
196  {
197  m_allControllers[i]->assignNodeNumber(*m_pCPGSys, nodeActions);
198  }
199 
200  // Then determine connectivity and setup string
201  for (std::size_t i = 0; i < m_allControllers.size(); i++)
202  {
203  tgCPGStringControl * const pStringInfo = m_allControllers[i];
204  assert(pStringInfo != NULL);
205  pStringInfo->setConnectivity(m_allControllers, edgeActions);
206 
207  //String will own this pointer
208  ImpedanceControl* p_ipc = new ImpedanceControl( m_config.tension,
209  m_config.kPosition,
210  m_config.kVelocity);
211  if (m_config.useDefault)
212  {
213  pStringInfo->setupControl(*p_ipc);
214  }
215  else
216  {
217  pStringInfo->setupControl(*p_ipc, m_config.controlLength);
218  }
219  }
220 
221 }
222 
224 {
225  m_updateTime += dt;
226  if (m_updateTime >= m_config.controlTime)
227  {
228  std::size_t numControllers = subject.getNumberofMuslces();
229 
230  double descendingCommand = 2.0;
231  std::vector<double> desComs (numControllers, descendingCommand);
232 
233  m_pCPGSys->update(desComs, m_updateTime);
234 #if (0) // Conditional compile for data logging
235  m_dataObserver.onStep(subject, m_updateTime);
236 #endif
237  m_updateTime = 0;
238  }
239 }
240 
242 {
243  std::vector<double> scores;
244  // @todo - check to make sure we ran for the right amount of time
245 
246  std::vector<double> finalConditions = subject.getSegmentCOM(m_config.segmentNumber);
247 
248  const double newX = finalConditions[0];
249  const double newZ = finalConditions[2];
250  const double oldX = initConditions[0];
251  const double oldZ = initConditions[2];
252 
253  const double distanceMoved = sqrt((newX-oldX) * (newX-oldX) +
254  (newZ-oldZ) * (newZ-oldZ));
255 
256  scores.push_back(distanceMoved);
257 
260  double totalEnergySpent=0;
261 
262  vector<tgLinearString* > tmpStrings = subject.getAllMuscles();
263  for(int i=0; i<tmpStrings.size(); i++)
264  {
265  tgBaseString::BaseStringHistory stringHist = tmpStrings[i]->getHistory();
266 
267  for(int j=1; j<stringHist.tensionHistory.size(); j++)
268  {
269  const double previousTension = stringHist.tensionHistory[j-1];
270  const double previousLength = stringHist.restLengths[j-1];
271  const double currentLength = stringHist.restLengths[j];
272  //TODO: examine this assumption - free spinning motor may require more power
273  double motorSpeed = (currentLength-previousLength);
274  if(motorSpeed > 0) // Vestigial code
275  motorSpeed = 0;
276  const double workDone = previousTension * motorSpeed;
277  totalEnergySpent += workDone;
278  }
279  }
280 
281  scores.push_back(totalEnergySpent);
282 
283  edgeAdapter.endEpisode(scores);
284  nodeAdapter.endEpisode(scores);
285 
286  delete m_pCPGSys;
287  m_pCPGSys = NULL;
288 
289  for(size_t i = 0; i < m_allControllers.size(); i++)
290  {
291  delete m_allControllers[i];
292  }
293  m_allControllers.clear();
294 }
295 
297  (vector< vector <double> > actions)
298 {
299  std::size_t numControllers = edgeConfigData.getintvalue("numberOfControllers");
300 
301  // Ensure reading from the same file
302  assert(numControllers == actions.size());
303  assert(actions[0].size() == 2);
304 
305  double lowerLimit = m_config.lowPhase;
306  double upperLimit = m_config.highPhase;
307  double range = upperLimit - lowerLimit;
308 
309  array_4D actionList(boost::extents[m_config.segmentSpan]
310  [m_config.theirMuscles]
311  [m_config.ourMuscles]
312  [m_config.params]);
313 
314  /* Horrid while loop to populate upper diagonal of matrix, since
315  * its symmetric and we want to minimze parameters used in learing
316  * note that i==1, j==k will refer to the same muscle
317  * @todo use boost to set up array so storage is only allocated for
318  * elements that are used
319  */
320  int i = 0;
321  int j = 0;
322  int k = 0;
323 
324  while (i < m_config.segmentSpan)
325  {
326  while(j < m_config.theirMuscles)
327  {
328  while(k < m_config.ourMuscles)
329  {
330  if (actions.empty())
331  {
332  std::cout << "ran out before table populated!"
333  << std::endl;
334  break;
335  }
336  else
337  {
338  if (i == 1 && j == k)
339  {
340  // std::cout << "Skipped identical muscle" << std::endl;
341  //Skip since its the same muscle
342  }
343  else
344  {
345  std::vector<double> edgeParam = actions.back();
346  // Weight from 0 to 1
347  actionList[i][j][k][0] = edgeParam[0];
348  // Phase offset from -pi to pi
349  actionList[i][j][k][1] = edgeParam[1] *
350  (range) + lowerLimit;
351  actions.pop_back();
352  }
353  }
354  k++;
355  }
356  j++;
357  k = j;
358 
359  }
360  j = 0;
361  k = 0;
362  i++;
363  }
364 
365  assert(actions.empty());
366 
367  return actionList;
368 }
369 array_2D BaseSpineCPGControl::scaleNodeActions
370  (vector< vector <double> > actions)
371 {
372  std::size_t numControllers = nodeConfigData.getintvalue("numberOfControllers");
373  std::size_t numActions = nodeConfigData.getintvalue("numberOfActions");
374 
375  assert( actions.size() == numControllers);
376  assert( actions[0].size() == numActions);
377 
378  array_2D nodeActions(boost::extents[numControllers][numActions]);
379 
380  array_2D limits(boost::extents[2][numActions]);
381 
382  // Check if we need to update limits
383  assert(numActions == 2);
384 
385 
386  for (int i = 0; i !=2; i++)
387  {
388 
389  limits[0][i] = m_config.lowAmp;
390  limits[1][i] = m_config.highAmp;
391  }
392 
393  // This one is square
394  for( std::size_t i = 0; i < numControllers; i++)
395  {
396  for( std::size_t j = 0; j < numActions; j++)
397  {
398  nodeActions[i][j] = ( actions[i][j] *
399  (limits[1][j] - limits[0][j])) + limits[0][j];
400  }
401  }
402 
403  return nodeActions;
404 }
virtual void onSetup(BaseSpineModelLearning &subject)
virtual void onStep(BaseSpineModelLearning &subject, double dt)
virtual void onTeardown(BaseSpineModelLearning &subject)
virtual void setupCPGs(BaseSpineModelLearning &subject, array_2D nodeActions, array_4D edgeActions)
std::deque< double > tensionHistory
Definition: tgBaseString.h:165
A class to read a learning configuration from a .ini file.
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)
void update(std::vector< double > descCom, double dt)
Contains the definition of class AnnealEvolution. Adapting NeuroEvolution to do Simulated Annealing...
void setConnectivity(const std::vector< tgCPGStringControl * > &allStrings, array_4D edgeParams)
Contains the definition of class ImpedanceControl. $Id$.
Contains the definition of class tgLinearString.
Definition of class CPGEdge.
virtual array_4D scaleEdgeActions(std::vector< std::vector< double > > actions)
A controller for the template class BaseSpineModelLearning.
Definition of class CPGEquations.
BaseSpineCPGControl(BaseSpineCPGControl::Config config, std::string args, std::string ec="edgeConfig.ini", std::string nc="nodeConfig.ini")
virtual void onStep(tgModel &model, double dt)
Definition of class CPGNode.
void initialize(AnnealEvolution *evo, bool isLearning, configuration config)
virtual void onSetup(tgModel &model)
std::deque< double > restLengths
Definition: tgBaseString.h:156