NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
VerticalSpineModel.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 
26 // This module
27 #include "VerticalSpineModel.h"
28 // This library
29 #include "core/tgCast.h"
30 //#include "core/tgBasicActuator.h"
32 #include "core/tgString.h"
33 #include "tgcreator/tgBuildSpec.h"
36 #include "tgcreator/tgRodInfo.h"
37 #include "tgcreator/tgStructure.h"
39 #include "tgcreator/tgUtil.h"
40 // The Bullet Physics library
41 #include "btBulletDynamicsCommon.h"
42 // The C++ Standard Library
43 #include <iostream>
44 #include <stdexcept>
45 #include <math.h>
46 
47 // The single constructor.
49  m_segments(segments),
50  tgModel()
51 {
52 }
53 
68 namespace
69 {
70  ConfigVertebra conf_base_vertebra = {
71  "base", // the name of this type of vertebra
72  0.0, // mass. Note that setting a rigid body's mass to 0 makes it fixed in space.
73  0.5, // radius
74  9.303, // leg_length.
75  13.141, // height.
76  0.99, // friction
77  0.01, // rollFriction
78  0.0, // restitution
79  };
80 
81  // On 2016-03-08, the two-segment model of ULTRA Spine (one active, one passive)
82  // weighed 231g. Estimate: 2/5 passive (92.4), 3/5 active (138.6).
83  ConfigVertebra conf_passive_vertebra = {
84  "passive", // the name of this type of vertebra
85  0.0924, // mass
86  0.5, // radius
87  9.303, // leg_length
88  13.141, // height
89  0.99, // friction
90  0.01, // rollFriction
91  0.0, // restitution
92  };
93 
94  ConfigVertebra conf_active_vertebra = {
95  "active", // the name of this type of vertebra
96  0.1386, // mass
97  0.5, // radius
98  9.303, // leg_length
99  13.141, // height
100  0.99, // friction
101  0.01, // rollFriction
102  0.0, // restitution
103  };
104 
105  struct ConfigSpine {
106  double vertebra_separation; // Length in the vertical direction of the initial separation between adjacent vertebrae
107  double base_vertical_offset; // The bottom vertebra clips below the ground a bit. Move it up by this amount.
108  } conf_spine = {
109  7.5, // vertebra_separation
110  2.0 // base_vertical_offset
111  };
112 
113  const struct Config
114  {
115  double stiffness;
116  double damping;
117  double friction;
118  double rollFriction;
119  double restitution;
120  double pretension;
121  bool hist;
122  double maxTens;
123  double targetVelocity;
124  } c =
125  {
126  // On 2016-03-08, the two-segment model of ULTRA Spine (one active, one passive)
127  // weighed 231g.
128  // On 2016-04-04, spring constant is 2.01 lb/in
129  // 1 lbf = 4.448222 N, 1 in = 2.54 cm
130  // thus, y N/m = x lb/in * 175.1269
131  352.005, // stiffness (kg / sec^2)
132  10.0, // damping (kg / sec)
133  0.99, // friction (unitless)
134  0.01, // rollFriction (unitless)
135  0.0, // restitution (?)
136  150.0, // pretension. used to be 2452.0
137  0, // History logging (boolean)
138  100000, // maxTens
139  10000, // targetVelocity
140 
141  };
142 
143 } // end namespace
144 
145 // Helper functions, with explicit scopes, moved from implicit namespace.
146 void VerticalSpineModel::trace(const tgStructureInfo& structureInfo, tgModel& model)
147 {
148  std::cout << std::endl << "StructureInfo Trace:" << std::endl
149  << structureInfo << std::endl
150  << "Model: " << std::endl
151  << model << std::endl;
152 }
153 
158 void VerticalSpineModel::trace(const tgStructure& structure,
159  const tgStructureInfo& structureInfo, tgModel& model)
160 {
161  std::cout << std::endl << "Structure Trace inside VerticalSpineModel:" << std::endl
162  << structure << std::endl
163  << std::endl << "StructureInfo Trace inside VerticalSpineModel:" << std::endl
164  << structureInfo << std::endl
165  << std::endl << "tgModel Trace inside VerticalSpineModel: " << std::endl
166  << model << std::endl;
167 }
168 
169 
170 void VerticalSpineModel::addNodes(tgStructure& vertebra, ConfigVertebra& conf_vertebra)
171 {
172  // right
173  vertebra.addNode( getEdgeLength(conf_vertebra) / 2.0, 0, 0); // node 0
174  // left
175  vertebra.addNode( -getEdgeLength(conf_vertebra) / 2.0, 0, 0); // node 1
176  // top
177  vertebra.addNode(0, conf_vertebra.height, -getEdgeLength(conf_vertebra) / 2.0); // node 2
178  // front
179  vertebra.addNode(0, conf_vertebra.height, getEdgeLength(conf_vertebra) / 2.0); // node 3
180  // middle
181  vertebra.addNode(0, conf_vertebra.height/2, 0); // node 4
182 
183 }
184 
185 double VerticalSpineModel::getDensity(ConfigVertebra& conf_vertebra)
186 {
187  // do we want an assert() here?
188  // Remember that the mass here is distributed over the whole volume of
189  // the vertebra. Assume that one vertebra is 4 rods.
190  int num_rods = 4;
191  double volume = num_rods * M_PI * pow(conf_vertebra.radius, 2) * conf_vertebra.leg_length;
192  double density = conf_vertebra.mass / volume;
193  return density;
194 }
195 
196 double VerticalSpineModel::getEdgeLength(ConfigVertebra& conf_vertebra)
197 {
198  // Do we want an assert here?
199  // Leg length is hypotenuse with triangle with sides h/2 and e/2.
200  double edge = 2 * sqrt( pow(conf_vertebra.leg_length, 2) - pow( conf_vertebra.height / 2, 2) );
201  return edge;
202 }
203 
204 void VerticalSpineModel::addRodPairs(tgStructure& vertebra, ConfigVertebra& conf_vertebra)
205 {
206  vertebra.addPair(0, 4, "rod_" + conf_vertebra.vertebra_name);
207  vertebra.addPair(1, 4, "rod_" + conf_vertebra.vertebra_name);
208  vertebra.addPair(2, 4, "rod_" + conf_vertebra.vertebra_name);
209  vertebra.addPair(3, 4, "rod_" + conf_vertebra.vertebra_name);
210 }
211 
212 // DEPRECATED AS OF 2016-03-11
213 void VerticalSpineModel::addSegments(tgStructure& spine, const tgStructure& vertebra,
214  double edge, size_t segment_count)
215 {
216  //const btVector3 offset(0, 0, -edge * 1.15);
217  //const btVector3 offset(0, 7.5, 0);
218  const btVector3 offset(0, conf_spine.vertebra_separation, 0);
219  // For segment_count many more vertebrae...
220  for (size_t i = 1; i < segment_count; i++)
221  {
222  // Make a copy of the given vertebra object
223  tgStructure* const new_vertebra = new tgStructure(vertebra);
224  // Name the new vertebra
225  new_vertebra -> addTags(tgString("segment", i + 1));
226  // Move it into its new location.
227  new_vertebra -> move((i + 1) * offset);
228  // Add this new vertebra to the spine
229  spine.addChild(new_vertebra);
230  }
231 
232 }
233 
234 void VerticalSpineModel::addVertebra(tgStructure& spine,
235  ConfigVertebra& conf_vertebra,
236  size_t vertebra_number)
237 {
238  // we have to pull out the base vertebra here to copy it.
239  // TO-DO: Why can't we just make a new object?
240  // What does this call to new tgStructure(something) really do?
241  std::vector<tgStructure*> spine_vertebrae = spine.getChildren();
242  // Find the first vertebra
243  // HACK FOR NOW: just pick the first element, we know that's the base.
244  tgStructure* const new_vertebra = new tgStructure( *(spine_vertebrae[0]) );
245 
246  // TO-DO: REMOVE THE OLD TAG FROM THE FIRST VERTEBRA!
247  // Need to remove "segment1".
248  // DO WE ALSO NEED TO REMOVE OTHER OLD STUFF?
249 
250  // Add the nodes and pairs to this new vertebra
251  addNodes( *new_vertebra, conf_vertebra);
252  addRodPairs( *new_vertebra, conf_vertebra);
253 
254  // Move it into place.
255  // Remember that the vertical direction is dimension 2 in Bullet Physics.
256  new_vertebra -> move( btVector3(0.0,
257  (vertebra_number - 1) * conf_spine.vertebra_separation, 0.0) );
258 
259  // Tag it with the segment number
260  new_vertebra -> addTags( tgString("segment", vertebra_number) );
261 
262  // Add the created vertebra to the rest of the spine.
263  spine.addChild(new_vertebra);
264 }
265 
266 // Add muscles that connect the segments
267 void VerticalSpineModel::addMuscles(tgStructure& spine)
268 {
269  const std::vector<tgStructure*> children = spine.getChildren();
270  for (size_t i = 1; i < children.size(); i++)
271  {
272  tgNodes n0 = children[i-1]->getNodes();
273  tgNodes n1 = children[i ]->getNodes();
274 
275  // vertical muscles
276  spine.addPair(n0[0], n1[0], "vertical muscle a");
277  spine.addPair(n0[1], n1[1], "vertical muscle b");
278  spine.addPair(n0[2], n1[2], "vertical muscle c");
279  spine.addPair(n0[3], n1[3], "vertical muscle d");
280 
281  // saddle muscles
282  spine.addPair(n0[2], n1[1], tgString("saddle muscle seg", i-1));
283  spine.addPair(n0[3], n1[1], tgString("saddle muscle seg", i-1));
284  spine.addPair(n0[2], n1[0], tgString("saddle muscle seg", i-1));
285  spine.addPair(n0[3], n1[0], tgString("saddle muscle seg", i-1));
286  }
287 }
288 
289 void VerticalSpineModel::mapMuscles(VerticalSpineModel::MuscleMap& muscleMap,
290  tgModel& model, size_t segmentCount)
291 {
292  // create names for muscles (for getMuscles function)
293 
294  // vertical muscles
295  muscleMap["vertical a"] = model.find<tgSpringCableActuator>("vertical muscle a");
296  muscleMap["vertical b"] = model.find<tgSpringCableActuator>("vertical muscle b");
297  muscleMap["vertical c"] = model.find<tgSpringCableActuator>("vertical muscle c");
298  muscleMap["vertical d"] = model.find<tgSpringCableActuator>("vertical muscle d");
299 
300  // saddle muscles
301  for (size_t i = 1; i < segmentCount ; i++)
302  {
303  muscleMap[tgString("saddle", i-1)] = model.find<tgSpringCableActuator>(tgString("saddle muscle seg", i-1));
304 
305  }
306 }
307 
308 /***************************************
309  * The primary functions., called from other classes.
310  **************************************/
312 {
313 
314  // Flag for debugging output: set to 1 to get more information output
315  // to the command line when the app is run
316  bool debug_flag = 0;
317 
318  // A bit of debugging about this spine's geometry and shape.
319  if( debug_flag ){
320  std::cout << "passive vertebra height: " << conf_passive_vertebra.height
321  << std::endl;
322  std::cout << "passive vertebra edge length: " <<
323  getEdgeLength(conf_passive_vertebra) << std::endl;
324  }
325 
326  // This is the container for the whole spine object,
327  // including all rigid bodies and all cables.
328  tgStructure spine;
329 
330  // Create the first spine vertebra. This one is fixed to the ground,
331  // and is non-moving since it has mass = 0.
332  tgStructure base_vertebra;
333  addNodes(base_vertebra, conf_base_vertebra);
334  addRodPairs(base_vertebra, conf_base_vertebra);
335 
336  // Remember that the second dimension here is vertical.
337  // (Y is the vertical, not Z.)
338  base_vertebra.move(btVector3(0.0, conf_spine.base_vertical_offset, 0));
339 
340  // Tag this vertebra as the first segment in the spine
341  base_vertebra.addTags(tgString("segment", 1));
342  // Apparently we need a const pointer here. Hrrmmmm... Drew 2016-03-10
343  tgStructure* const base_vertebra_copy = new tgStructure(base_vertebra);
344  spine.addChild(base_vertebra_copy);
345 
346  // Add the rest of the vertebrae! As of 2016-03-11, have four more.
347  // The second and 4th vertebrae are actuated.
348  addVertebra(spine, conf_active_vertebra, 2);
349  addVertebra(spine, conf_passive_vertebra, 3);
350  addVertebra(spine, conf_active_vertebra, 4);
351  addVertebra(spine, conf_passive_vertebra, 5);
352 
353 
354  addMuscles(spine);
355 
356  // Create the build spec that uses tags to turn the structure into a real model.
357  // This is where the tagged pairs are matched up with an actual geometric object.
358  tgBuildSpec spec;
359 
360  // For the base
361  const tgRod::Config rodConfigBase(conf_base_vertebra.radius,
362  getDensity(conf_base_vertebra),
363  conf_base_vertebra.friction,
364  conf_base_vertebra.rollFriction,
365  conf_base_vertebra.restitution);
366 
367  // For the passive vertebra
368  const tgRod::Config rodConfigPassive(conf_passive_vertebra.radius,
369  getDensity(conf_passive_vertebra),
370  conf_passive_vertebra.friction,
371  conf_passive_vertebra.rollFriction,
372  conf_passive_vertebra.restitution);
373 
374  // For the active vertebra
375  const tgRod::Config rodConfigActive(conf_active_vertebra.radius,
376  getDensity(conf_active_vertebra),
377  conf_active_vertebra.friction,
378  conf_active_vertebra.rollFriction,
379  conf_active_vertebra.restitution);
380 
381  // Add to the build specification: these tgRod::Config objects matched up
382  // with the vertebra name. This correlates to what's used in addRodPairs.
383  spec.addBuilder("rod_" + conf_base_vertebra.vertebra_name,
384  new tgRodInfo(rodConfigBase));
385  spec.addBuilder("rod_" + conf_passive_vertebra.vertebra_name,
386  new tgRodInfo(rodConfigPassive));
387  spec.addBuilder("rod_" + conf_active_vertebra.vertebra_name,
388  new tgRodInfo(rodConfigActive));
389 
390 
391  // set muscle (string) parameters
392  // @todo replace acceleration constraint with tgKinematicActuator if needed...
393  tgSpringCableActuator::Config muscleConfig(c.stiffness, c.damping, c.pretension,
394  c.hist, c.maxTens, c.targetVelocity);
395  spec.addBuilder("muscle", new tgBasicActuatorInfo(muscleConfig));
396 
397  // Create the structureInfo
398  tgStructureInfo structureInfo(spine, spec);
399 
400  // Debugging: print out the tgStructure of the spine and its children
401  //std::cout << spine << std::endl;
402  //trace(structureInfo, *this);
403  trace(spine, structureInfo, *this);
404 
405  // Use the structureInfo to build this model
406  structureInfo.buildInto(*this, world);
407 
408  // We could now use tgCast::filter or similar to pull out the models (e.g. muscles)
409  // that we want to control.
410  allMuscles = tgCast::filter<tgModel, tgSpringCableActuator> (getDescendants());
411  mapMuscles(muscleMap, *this, m_segments);
412 
413  // Let's see what type of objects are inside the spine.
414  std::vector<tgModel*> all_children = getDescendants();
415  // Pick out the tgBaseRigid objects
416  std::vector<tgBaseRigid*> all_tgBaseRigid = tgCast::filter<tgModel, tgBaseRigid>(all_children);
417 
418  // Print out the tgBaseRigids
419  // std::cout << "Spine tgBaseRigids: " << std::endl;
420  // for (size_t i = 0; i < all_tgBaseRigid.size(); i++)
421  // {
422  // std::cout << "object number " << i << ": " << std::endl;
423  // std::cout << "mass: " << all_tgBaseRigid[i]->mass() << std::endl;
424  // std::cout << all_tgBaseRigid[i]->toString() << std::endl;
425  // }
426 
427  //trace(structureInfo, *this);
428 
429  // Actually setup the children
430  notifySetup();
431  tgModel::setup(world);
432 }
433 
435 {
436  if (dt < 0.0)
437  {
438  throw std::invalid_argument("dt is not positive");
439  }
440  else
441  {
442  // Notify observers (controllers) of the step so that they can take action
443  notifyStep(dt);
444  // Step any children
445  tgModel::step(dt);
446  }
447 }
448 
449 const std::vector<tgSpringCableActuator*>&
450 VerticalSpineModel::getMuscles (const std::string& key) const
451 {
452  const MuscleMap::const_iterator it = muscleMap.find(key);
453  if (it == muscleMap.end())
454  {
455  throw std::invalid_argument("Key '" + key + "' not found in muscle map");
456  }
457  else
458  {
459  return it->second;
460  }
461 }
462 
463 const std::vector<tgSpringCableActuator*>& VerticalSpineModel::getAllMuscles() const
464 {
465  return allMuscles;
466 }
467 
const std::vector< tgStructure * > & getChildren() const
Definition: tgStructure.h:184
virtual void step(const double dt)
virtual void setup(tgWorld &world)
Definition: tgModel.cpp:57
void addChild(tgStructure *child)
Definition of class tgRodInfo.
const std::vector< tgSpringCableActuator * > & getMuscles(const std::string &key) const
virtual void setup(tgWorld &world)
Convenience function for combining strings with ints, mostly for naming structures.
virtual void step(double dt)
Definition: tgModel.cpp:84
Utility class for class casting and filtering collections by type.
Contains the definition of class VerticalSpineModel.
VerticalSpineModel(size_t segments)
Definition of class tgBasicActuatorInfo.
void addPair(int fromNodeIdx, int toNodeIdx, std::string tags="")
Definition: tgStructure.cpp:80
Contains the definition of abstract base class tgSpringCableActuator. Assumes that the string is line...
std::string tgString(std::string s, int i)
Definition: tgString.h:33
Definition of class tgStructure.
std::vector< T * > find(const tgTagSearch &tagSearch)
Definition: tgModel.h:128
Definition of class tgStructureInfo.
std::map< std::string, std::vector< tgSpringCableActuator * > > MuscleMap
Rand seeding simular to the evolution and terrain classes.
Definition of class tgBuildSpec.
Definition of class tgRigidAutoCompound.
std::vector< tgModel * > getDescendants() const
Definition: tgModel.cpp:170
const std::vector< tgSpringCableActuator * > & getAllMuscles() const
void buildInto(tgModel &model, tgWorld &world)
void addNode(double x, double y, double z, std::string tags="")
Definition: tgStructure.cpp:70