Categories
3D AI/ML dev simulation

Understanding MotorDemo.cpp (Part 1)

Going through the Bullet3 physics engine example code for ‘MotorDemo’. It’s a hexapod looking robot flexing its joints. The folder is bullet3/examples/DynamicControlDemo/

https://github.com/bulletphysics/bullet3/tree/master/examples/DynamicControlDemo

Little gif of MotorDemo in action

OK so there’s the .h file containing

#ifndef MOTORDEMO_H

#define MOTORDEMO_H

 

class CommonExampleInterface* MotorControlCreateFunc(struct CommonExampleOptions& options);

 

#endif

C++ declares the function in .h files so that when compiling, all the .cpp files know about each other, without necessarily caring about the implementation details.

So this function is called ‘MotorControlCreateFunc’, which returns a pointer to a CommonExampleInterface, and takes a reference to CommonExampleOptions. Nice OOPy programming.

Let’s get into the CPP file.

Ok variables are:

time, cycle period, muscle strength, and an array of TestRig objects called rig

Constructor takes a pointer to a GUIHelperInterface. Destructor does no cleaning up.

They define an ‘initPhysics‘ and ‘exitPhysics‘. exitPhysics seems to have all the destructor code freeing up memory.

And three functions, spawnTestRig, setMotorTargets, and resetCamera.

define NUM_LEGS 6
define BODYPART_COUNT 2 * NUM_LEGS + 1
define JOINT_COUNT BODYPART_COUNT - 1

Looks like we can change some settings maybe. Will play with that later. So a leg had 2 body parts, plus there’s the central ‘body’ of the critter.

Now they define the TestRig class

btDynamicsWorld* m_ownerWorld; 
btCollisionShape* m_shapes[BODYPART_COUNT]; btRigidBody* m_bodies[BODYPART_COUNT]; btTypedConstraint* m_joints[JOINT_COUNT];

TestRig consists of a world, collision shapes, bodies and joints.

Let’s look at the rest of the code, because most of the code takes place in TestRig.

So as mentioned before, initPhysics and exitPhysics set up and shut down the physics engine gui.

So this code will be heavily copy-pasted. In this case, it sets up the 3d world, makes a ground, and spawns two test rigs. (Spider things), with a small ‘start offset’ vector from each other. They are pushed onto the rigs stack variable.

setMotorTargets takes a time as a variable. The comment is

// set per-frame sinusoidal position targets using angular motor (hacky?)

So it iterates over the number of rigs, and then twice per NUM_LEGS, sets up target hinge variables, for the animation.

Per hinge, it gets the current angle, sets the target percentage as time divided by 1000, mod the cycle period, divided by the cycle period. and then sets the target angle as 0.5 * (1 + sin (2*PI*target percentage)). Ok…

Target Limit Angle is the hinge’s lower limit, plus the ( target angle multiplied by the ( upper limit – lower limit)). Ok…

Angle Error is the target limit angle minus the current angle.

DesiredAngularVelocity is angle error / time, which is passed to each hinge’s enableAngularMotor function.

I’d be lying if I said I understood entirely. But this is pretty much the basis of the program.

void motorPreTickCallback(btDynamicsWorld* world, btScalar timeStep)
 {
     MotorDemo* motorDemo = (MotorDemo*)world->getWorldUserInfo();
 motorDemo->setMotorTargets(timeStep);
 }

It basically sets all the motor targets, based on the time that has passed, using some sort of sin-wave and the flexing-limits of the legs

Ok. More later.