Computational Embodied Neuroscience Simulator  1.1
3D simulation library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends

CENSLIB - Computational Embodied Neuroscience Simulation Library

Table of Contents

Authors
Francesco Mannella

Download page: http://sourceforge.net/projects/censlib/

Demo showing motor control and touch sensor

Introduction

This software allows to build 3D simulated environments and robots using Bullet physics library for physics simulation and OpenGL for rendering. The library contains the CENSEngine class that is able to automatize the rendering of objects created through the physics engine. Moreover the CENSSerializedEngine::loadBulletFile method contained in CENSSerializedEngine derived from CENSEngine completely automatize the creation of btRigidBody and btHingeConstraint objects and their usage. Objects and constraints can be designed in a visual mode using Blender and then imported into CENSLIB through the method CENSSerializedEngine::loadBulletFile. By this mean writing bullet code directly can also be avoided.

The project start code is the DemoApplication class present in the Demos/OpenGL folder in the Bullet physics library package. The idea of this library was born from the need to have a cleaner code to allow users to bypass all graphics rendering issues and focus on the bullet code.

Installation

The CENSLIB library is meant to be used on a linux platform. It depends on bullet library and OpenGL. Other needed libraries are the Eigen2 linear algebra library, used for data manipulation within the graphics module, the Magick++ library used to manipulate screenshots of simulations as pixmaps and boost libraries used for regular expressions and file management.

On a debian distribution install these packages through this command:

sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev \
       libmagick++-dev libeigen2-dev libboost-regex-dev \
       libboost-system-dev libboost-filesystem-dev libxmu-dev libxi-dev

Then install bullet 2.82 release. On a terminal:

svn checkout http://bullet.googlecode.com/svn/tags/bullet-2.82/ bullet
cd bullet; mkdir cmake_build; cd cmake_build
cmake  .. -DBUILD_SHARED_LIBS=ON -DBUILD_EXTRAS=ON -DINSTALL_LIBS=ON -DINSTALL_EXTRA_LIBS=ON
make -j4
sudo make install

Now download the latest CENSLIB version from here, untar, build and install it:

tar xzvf censlib-<release-number>.tar.gz
cd CENSLIB

and you can build it using cmake :

mkdir build; cd build
cmake ..
make -j4
sudo make install

or the Autotools:

mkdir build; cd build
../configure
make -j4 
sudo make install

Usage

The following command Compiles and links an example.cpp source file:

c++ -O2 -o example example.cpp \
   -I/usr/local/include/CENS \
   -I/usr/local/include/bullet -I/usr/include/eigen2 \
   -I/usr/include/ImageMagick \
   -lCENS-1.1 \
   -lBulletWorldImporter -lBulletFileLoader \
   -lBulletSoftBody -lBulletDynamics \
   -lBulletCollision -lLinearMath \
   -lboost_regex -lboost_system -lboost_filesystem\
   -lGL -lGLU -lglut \
   -lMagick++ 

Getting started

Building a simple program

In a typical simulation using CENSLIB a new class is derived from CENSEngine:

#include "cens_engine.h"
using namespace cens;
class MyEngine : public CENSEngine
{
public:
// overloadings
void initObjects();
void step( int step_interval);
};

Two methods must be overloaded. One of them is CENSEngine::initObjects where you write bullet code about the initialization of your objects and constraints :

void MyEngine::initObjects()
{
// YOUR BULLET CODE
// for instance we build a simple cube:
// a box shape
btCollisionShape* shape =
new btBoxShape( btVector3(1,1,1) );
// a transform centered on the origin
btTransform transform;
transform.setIdentity();
// the mass of the object
double mass = 1.0;
// let's build the object!
btRigidBody *body =
localCreateRigidBody(
mass, transform, shape);
}

Note how the building of a btRigidBody is simplified by the use of the method CENSEngine::localCreateRigidBody, which automatically build and attach a btRigidBody to the world starting from a shape, a transform and a mass value.

The other method to overload is CENSEngine::step. You can write here your bullet code for applying forces at each timestep:

void MyEngine::step(int step_interval)
{
// YOUR BULLET CODE
CENSEngine::step(timestep);
}

the call to CENSEngine::step must always be at the end of your overloaded step method, otherwise the simulation does not start.

Once you built your class derived from CENSEngine you only need to define an object of that type and call its CENSEngine::init and CENSEngine::run methods in the main function:

int main(int argc, char** argv)
{
MyEngine engine;
engine.init(argc,argv);
engine.run();
return 0;
}

When the program runs the first time two files are searched for in the ./parameters directory, one called "physics_parameters", the other called "graphics_parameters". They contain the values of graphics and physics parameters respectiivelly. Both can bechanged offline before starting a simulation. if those files do not exist they are created with default values:

graphics_parameters

   800             ------- SCREEN_WIDTH      <- pixel width of the window
   600             ------- SCREEN_HEIGHT     <- pixel height of the window
   10              ------- SCREEN_XGAP       <- horizontal position of the window
   10              ------- SCREEN_YGAP       <- vertical position of the window
   10              ------- TIMESTEP          <- time interval of a single timestep (milliseconds)
   Demo            ------- SCREEN_TITLE      <- title of the window
   70              ------- FIELD_OF_VIEW     <- the angle (degrees) defining the width of the FoV
   1.2             ------- RATIO             <- ratio between horizontal/vertical FoV angle
   1               ------- NEAR              <- near plane of view
   10000           ------- FAR               <- far plane of view
   0.15            ------- ANGLE             <- from where the camera points to the target
   15              ------- DISTANCE          <- distance of the camera from the origin
   15              ------- HEIGHT            <- height of the camera with respect to the origin
   0;0;1           ------- TARGET            <- the center of the scene 
   0;0;1           ------- UP                <- direction of the UP vector
   0.1             ------- MOV               <- right-left increment when moving the scene
   0.1             ------- GAP               <- bottom-up increment when moving the scene
   0.1;0.1;0       ------- ENV_COLOR         <- background color
   0.6;0.2;0.2     ------- GROUND_COLOR      <- floor color
   0.2;0.6;0.2     ------- OBJECT_COLOR      <- generic object color 
   0.2;0.2;0.0     ------- BOX_COLOR         <- box color
   0.4;0.4;0.6     ------- CAPSULE_COLOR     <- capsule color
   0.7;0.1;0.7     ------- SPHERE_COLOR      <- sphere color
   0.2;0.6;0.2     ------- COMPOUND_COLOR    <- color of a compound object
   0;0.6;0.6       ------- SOFT_COLOR        <- color of a soft body
   0.5;.5;0.2;1    ------- LIGHT_AMBIENT     <- lights - ambient light color
   1;1;1;1         ------- LIGHT_DIFFUSE     <- lights - diffuse light color
   1;1;1;1         ------- LIGHT_SPECULAR    <- lights -specular light color
   10;10;10;1      ------- LIGHT_POSITION0   <- light0 position
   10;10;10;0      ------- LIGHT_POSITION1   <- light1 position

physics_parameters

   0;0;-9.8        ------- GRAVITY           <- a vector defining gravity amplitude and direction
   0.05            ------- STEP              <- integration step for physics
   20              ------- SUBSTEP           <- number of real teps if timestep is too long

Examples

These are some examples of how to implement the main features:

Joints

derive an Engine from CENSEngine:

1 #ifndef ENGINE_H
2 #define ENGINE_H
3 
4 #include "cens_engine.h"
5 
6 
7 using namespace cens;
8 
9 class Engine : public CENSEngine {
10 
11  public:
12 
13  Engine():time_counter(0) {}
14 
15  void initObjects();
16  void step( int timestep);
17 
18  private:
19  btRigidBody *body;
20  int time_counter;
21 
22 };
23 
24 #endif // ENGINE_H

Implement the overloading of CENSEngine::initObjects and CENSEngine::step

1 #include "engine.h"
2 
3 void Engine::initObjects() {
4 
6  // Initialize a sphere and add an hinge attached //
7  // to the world . //
8  // On runtime a force is applied to the sphere. //
9  // As a result the sphere rotates around the hinge. //
11 
12 
13  // initializing a sphere
14  btCollisionShape* spShape =
15  new btSphereShape(
16  btScalar(2.) );
17  btTransform spTransform;
18  spTransform.setIdentity();
19  spTransform.setOrigin(btVector3(4,4,2));
20  spTransform.setRotation(btQuaternion(0,0,0));
21  body =
22  localCreateRigidBody(
23  btScalar(1),
24  spTransform,
25  spShape,
26  btVector3(.6,.6,.8) );
27 
28 
29  //initializing a joint.
30  // set the ransform information for a joint:
31  btTransform jointTransform;
32  jointTransform.setIdentity();
33 
34  // translation: the point in the solid body that is
35  // constrained to the hinge. Coordinates are local,
36  // relative to the body.
37  jointTransform.setOrigin( btVector3(-4, -4, 0));
38 
39  // rotation: which axis the plane of the hinge constraint
40  // will rotate on and how much it will rotate.
41  jointTransform.setRotation(
42  btQuaternion(
43  btVector3(0,0,1), // axis
44  0)); // angle
45 
46  // build the joint. Giving a single body
47  // and a single transform is enough to tell bullet
48  // to use the world as the other body.
49  btHingeConstraint *constraint = new btHingeConstraint(
50  *body,
51  jointTransform);
52 
53  // add the joint to the world
54  // (argument true means they can compenetrate!)
55  m_phDynamicsWorld->addConstraint(constraint,true);
56 
57  toggleTexture();
58  toggleObjectAxes();
59  toggleJointAxes();
60 }
61 
62 
63 
64 void Engine::step( int timestep )
65 {
66 
67  // A force is applied to the sphere.
68  float force = 50;
69  body->applyCentralForce(
70  body->getWorldTransform() * // body center of mass
71  (btVector3(.1,0,0) * force) ); // direction of force
72  // initialize a counter to know in
73  // which part of the simulation we are
74  time_counter++;
75 
76  // This call should always be here.
77  // Without it bullet and graphics steps
78  // are not called, and the loop of
79  // simulation doesn't go on.
80  CENSEngine::step(timestep);
81 
82 }
83 

Soft bodies

derive an Engine from CENSEngine:

1 #ifndef ENGINE_H
2 #define ENGINE_H
3 
4 #include "cens_engine.h"
5 
6 using namespace cens;
7 
8 
9 class Engine : public CENSEngine {
10  public:
11  Engine():time_counter(0) {}
12  void initObjects();
13  void step( int timestep);
14  private:
15  btRigidBody* body; // a rigid body
16  int time_counter; // a counter of the simulation steps
17 };
18 
19 #endif //ENGINE_H
20 

Implement the overloading of CENSEngine::initObjects and CENSEngine::step

1 #include "engine.h"
2 
3 using namespace cens;
4 
5 
6 
7 // Define the initObjects function
8 // in which we initialize the rigid body
9 // as a box
10 void Engine::initObjects() {
11 
12  // A SPHERE
13  // Create a Sphere shape
14  // with radius 2
15  btCollisionShape* spShape =
16  new btSphereShape(
17  btScalar(2.) );
18 
19  // Create a transform
20  btTransform spTransform;
21  // Initialize the transform to (0,3,8),
22  // with no rotation
23  spTransform.setIdentity();
24  spTransform.setOrigin(btVector3(0,0,8));
25  spTransform.setRotation(btQuaternion(0,0,0));
26 
27  // Create the soft body giving mass and shape.
28  btSoftBody *softSphere =localCreateSoftBody(
29  btScalar(10),
30  spShape);
31  // give the sphere the righttransform
32  softSphere->transform( spTransform );
33 
34  // Linear stiffness [0,1]
35  softSphere->m_materials[0]->m_kLST = 0.1f;
36  // Area/angular stiffness [0,1]
37  softSphere->m_materials[0]->m_kAST = 0.1f;
38  // volume stiffness [0,1]
39  softSphere->m_materials[0]->m_kVST = 0.1f;
40 
41  softSphere->setPose( true, false );
42  softSphere->generateBendingConstraints(3);
43 
44 
45  // Create a 10x10x10 box shape
46  // ( giving the half-extents of dimensions )
47  btCollisionShape* shape =
48  new btBoxShape( btVector3(15,15,2) );
49 
50  // Create a transform
51  btTransform transform;
52 
53  // Initialize the transform to the origin,
54  // with no rotation
55  transform.setIdentity();
56  transform.setOrigin(btVector3(0,0,0));
57 
58  // Create the body giving mass, shape and transform.
59  body =
60  localCreateRigidBody(
61  0.0, // mass
62  transform, // transform
63  shape); // shape
64 }
65 
66 // Finally, define the step function
67 // in which we apply a force to the box
68 // for the first 10 steps of the simulation
69 void Engine::step( int timestep )
70 {
71 
72  // applying a force on the body
73 
74  // thetransform of the body
75  btTransform &aBodyTransform = body->getWorldTransform();
76  // the direction of the force
77  btVector3 direction = btVector3(.5,.5,1);
78  // the magnitude of the force
79  float force = 20;
80 
81  // applying the force
82  if (time_counter<10)
83  body->applyCentralForce(
84  // multiplying the direction times the transform
85  // the local reference to the object is mantained
86  aBodyTransform * direction * force );
87 
88  // if more tha 200 steps are been run
89  // end the simulation
90  if( time_counter > 200 )
91  quit();
92 
93  // update a counter to know in
94  // which part of the simulation we are
95  time_counter++;
96 
97  // This call should always be here.
98  // Without it bullet and graphics steps
99  // are not called, and the loop of
100  // simulation doesn't go on.
101  CENSEngine::step(timestep);
102 
103 }
104 

Importing a bullet file

derive an Engine from CENSSerializedEngine:

1 #ifndef ENGINE_H
2 #define ENGINE_H
3 
5 
6 using namespace cens;
7 
8 
9 // First, declare a CENSEngine derived class
10 class Engine : public CENSSerializedEngine {
11  public:
12  void initObjects();
13  void step( int timestep);
14 
15 };
16 
17 #endif //ENGINE_H
18 

Now the overloading of CENSEngine::initObjects and CENSEngine::step is very simple. Just call loadBulletFile:

1 #include "engine.h"
2 
3 using namespace cens;
4 
5 
6 // Then define the initObjects function
7 // in which we initialize the rigid body
8 // as a box
9 void Engine::initObjects() {
10 
11  // Simply import all objects form a *.bullet file
12  m_eRobots["robot_test"]=loadBulletFile("robot_test","test.bullet");
13 }
14 
15 // Finally, define the step function
16 // in which we apply a force to the box
17 // for the first 10 steps of the simulation
18 void Engine::step( int timestep )
19 {
20  // This call should always be here.
21  // Without it bullet and graphics steps
22  // are not called, and the loop of
23  // simulation doesn't go on.
24  CENSEngine::step(timestep);
25 
26 }
27 

Controlling motors on an imported joint

derive an Engine from CENSSerializedEngine:

1 #ifndef ENGINE_H
2 #define ENGINE_H
3 
5 
6 using namespace cens;
7 
8 
9 // First, declare a CENSEngine derived class
10 class Engine : public CENSSerializedEngine {
11  public:
12  Engine():timecounter(0) {}
13  void initObjects();
14  void step( int timestep);
15 
16  int timecounter;
17 };
18 
19 #endif //ENGINE_H
20 

Within CENSEngine::initObjects call loadBulletFile, while within CENSEngine::step call the method CENSSerializedRobot::move on the acquired CENSSerializedRobot object, indicating one of the loaded hinges:

1 #include "engine.h"
2 
3 using namespace cens;
4 
5 
6 // Then define the initObjects function
7 // in which we initialize the rigid body
8 // as a box
9 void Engine::initObjects() {
10 
11  // Simply import all objects form a .bullet file
12  m_eRobots["robot_test"] = loadBulletFile("robot_test","test.bullet");
13 
14  // Object3 is a touch sensor
15  addTouchSensor(m_eBodies["Object3"],"Object3");
16 
17  // Object3 is red
18  m_eShapes[m_eBodies["Object3"]]->setColor(Vector3f(1,0,0));
19 
20  toggleTexture();
21  toggleJointAxes();
22 }
23 
24 // Finally, define the step function
25 // in which we apply a force to the box
26 // for the first 10 steps of the simulation
27 void Engine::step( int timestep )
28 {
29 
30 
31  static double angle = 0;
32  static double impulse = 1;
33  angle -= M_PI/100.;
34 
35 
36  saveCameraPixelMap(0, timecounter,"png");
37 
38  // if Object3 touches something
39  if(m_eSensors["Object3"]->isTouched())
40  {
41  // go backward
42  angle += M_PI/10.;
43 
44  // std::cout <<
45  // impulse << " " <<
46  // m_eSensors["Object3"]->getPressure() << std::endl;
47 
48  // higher impulse
49  impulse += .01;
50 
51  }
52 
53  m_eRobots["robot_test"]->move("Object2_to_Object1", angle, impulse);
54  m_eRobots["robot_test"]->move("Object3_to_Object2", angle, impulse);
55 
56  // if(timecounter>200)
57  // exit(0);
58 
59  timecounter++;
60 
61  // This call should always be here.
62  // Without it bullet and graphics steps
63  // are not called, and the loop of
64  // simulation doesn't go on.
66 
67 }
68 
69 

Cameras

derive an Engine from CENSSerializedEngine

1 #ifndef ENGINE_H
2 #define ENGINE_H
3 
5 
6 
7 using namespace cens;
8 
9 
10 class Engine : public CENSSerializedEngine {
11 
12  public:
13 
14  void initObjects();
15  void step( int timestep);
16 
17 
18 };
19 
20 #endif // ENGINE_H

Implement the overloading of CENSSerializedEngine::initObjects and CENSSerializedEngine::step

1 #include "engine.h"
2 
3 void Engine::initObjects() {
4 
5  // Simply import all objects form a .bullet file
6  m_eRobots["robot_test"] =
7  loadBulletFile("robot_test","test.bullet");
8 
9  btTransform local;
10  local.setOrigin(btVector3(0,0,-1));
11  local.setRotation(btQuaternion(M_PI/2.,0,0));
12 
13  addCamera(
14  "SphereCamera",
15  m_eBodies["Sphere"], // pointer to the attached body
16  local, // local transform relative to the attached body
17  "Sphere_EYE", // name of the camera
18  480,400, // dimensions of the camera window (pixels)
19  800,50, // position of the camera window on the screen (pixels)
20  btVector3(0,0,1), // UP vector of the camera
21  10, // target distance
22  100,
23  .00001);
24 
25  toggleTexture();
26  toggleCameraAxis();
27 }
28 
29 void Engine::step( int timestep )
30 {
31 
32  // This call should always be here.
33  // Without it bullet and graphics steps
34  // are not called, and the loop of
35  // simulation doesn't go on.
36  CENSEngine::step(timestep);
37 }
38