Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: More descriptive title

...

Code Block
languagecpp
#include <OpenSim/OpenSim.h>

int main(int argc, char *argv[])
{
    // First model: exhibiting the counter-rotation mechanism of flipping
    // ========================================================================
    // ********** PART A   **********	

    // Define anterior and posterior halves of the cat
    // ------------------------------------------------------------------------
    // ********** PART A.1 **********

    // Define joints between bodies (ground and two halves)
    // ------------------------------------------------------------------------
    // ********** PART A.2 **********

    // Display geometry
    // ------------------------------------------------------------------------
    // ********** PART A.3 **********

    // Actuation
    // ------------------------------------------------------------------------
    // ********** PART A.4 **********    
    
    
    // Second model: adding legs for the variable-inertia mechanism of flipping
    // ========================================================================
    // ********** PART B   **********    

    return EXIT_SUCCESS;
};

Table of Contents

A: First model: exhibiting the counter-rotation mechanism of flipping

This first model has 2 degrees of freedom between its halves, hunch and wag, as show in the image on the main Flippin' Felines page. These degrees of freedom are sufficient for allowing the cat to execute a flip using counter-rotation. Mass properties come from Kane and Scher [2] and extensive inspection of cats.

Code Block
languagecpp
// Properties
// ------------------------------------------------------------------------
double segmentalLength = 0.175;                           // m
double segmentalDiam = 0.15;                              // m
double segmentalMass = 1;                                 // kg
double segmentalTransverseMomentOfInertia = 1;            // kg-m^2

// Ratio of transverse to axial moment of inertia (Kane and Scher, 1969):
double JIratio = 0.25;

// For actuators:
double maxTorque = 40.0;                                  // N-m

// Basics
// ------------------------------------------------------------------------
// Create the cat model.
OpenSim::Model cat;

// Name the cat after the founder of Stanford University.
// This model will be able to hunch and wag (see the joints below).
cat.setName("Leland_hunch_wag");

// 'Turn off' gravity so it's easier to watch an animation of the flip.
using SimTK::Vec3;
cat.setGravity(Vec3(0, 0, 0));

A.1: Define anterior and posterior halves of the cat

Both halves of the cat have the same inertial properties (mass, mass center, moments of inertia). We model the cat's halves as axially symmetric. By the variable names below, we note that the X-axis is the long axis of the bodies.

...

Code Block
languagecpp
// By choosing the following as the mass center, we choose the origin of
// the anteriorBody frame to be at the body's positive-X extent. That is,
// the anterior body sits to the -X direction from its origin.
anteriorBody->setMassCenter(Vec3(-0.5 * segmentalLength, 0, 0));
// Posterior body sits to the +X direction from its origin.
posteriorBody->setMassCenter(Vec3(0.5 * segmentalLength, 0, 0));

A.2: Define joints between bodies (ground and two halves)

We now connect the bodies, and thus define how they can move with respect to one another. The cat is free to move about in space, and we represent this using a 6-degree-of-freedom joint between the ground (the inertial frame) and the cat's anterior body. We could use a FreeJoint, which is simpler to define, but we instead employ a CustomJoint. In our case, we do so because we want to specify the type of Euler angles that describe the orientation of the cat's anterior segment. Although the FreeJoint also uses Euler angles, it does not allow the modeler to choose which type of Euler angles are used.

...

Code Block
languagecpp
cat.addBody(anteriorBody);
cat.addBody(posteriorBody);

A.3: Display geometry

As is, if we load the model into the GUI we won't see anything in the View (display window). That's because we haven't told OpenSim how to display our model. We do so by adding DisplayGeometry to the bodies. The display geometry has nothing to do with the actual geometry, and thus mass properties, of the bodies. DisplayGeometry is loaded from geometry files, such as cylinder.vtp, located in the Geometry folder of an OpenSim installation. The geometry defined in a geometry file, which is sometimes simply a list of coordinates, implicitly contains a reference frame. For the cylinder.vtp file, the origin of this frame is at the cylinder's centroid. Equivalently, the cylinder's centroid is at the frame's origin. Also, the symmetry axis of the cylinder is its Y-axis. The diameter and height of the cylinder are both 1 meter.

...

Code Block
languagecpp
SimTK::Rotation rot;
// Rotate the cylinder's symmetry (Y) axis to align with the body's X axis:
rot.setRotationFromAngleAboutZ(0.5 * Pi);
// Tranform combines rotation and translation:
anteriorDisplay->setTransform(
        SimTK::Transform(rot, Vec3(-0.5 * segmentalLength, 0, 0)));
anteriorDisplay->setScaleFactors(
        Vec3(segmentalDiam, segmentalLength, segmentalDiam));
anteriorBody->updDisplayer()->updGeometrySet().adoptAndAppend(anteriorDisplay);
anteriorBody->updDisplayer()->setShowAxes(true);

// Posterior body
// ``````````````
DisplayGeometry * posteriorDisplay = new DisplayGeometry("cylinder.vtp");
posteriorDisplay->setOpacity(0.5);
posteriorDisplay->setColor(Vec3(0.7, 0.7, 0.7));

posteriorDisplay->setTransform(
        SimTK::Transform(rot, Vec3(0.5 * segmentalLength, 0, 0)));
posteriorDisplay->setScaleFactors(
        Vec3(segmentalDiam, segmentalLength, segmentalDiam));
posteriorBody->updDisplayer()->updGeometrySet().adoptAndAppend(posteriorDisplay);
posteriorBody->updDisplayer()->setShowAxes(true);

A.4: Actuation

Since the coordinates between the segments are angles, the actuators are effectively torque actuators. The reason to use a CoordinateActuator instead of a TorqueActuator is that for a CoordinateActuator we needn't specify the axis of the actuation, or the bodies on which it acts.

...

Code Block
languagecpp
cat.print("flippinfelines_hunch_wag.osim");

B: Second model: adding legs for the variable-inertia mechanism of flipping

This model will be able to twist, and also has legs. Most of the set-up for this model has already been done. Many of the objects and variables defined above are simply updated or renamed.

...

Code Block
languagecpp
cat.setName("Leland_hunch_wag_twist_legs");

// Allow twist.
anteriorPosteriorCS[2].setDefaultLocked(false);
CoordinateActuator * twistAct = new CoordinateActuator("twist");
twistAct->setName("twist_actuator");
twistAct->setMinControl(-maxTorque);
twistAct->setMaxControl(maxTorque);
cat.addForce(twistAct);

// Leg properties
// ------------------------------------------------------------------------
double legsLength = 0.125;                             // m
double legsDiam = 0.1 * legsLength;                    // m
// Sum of both legs (60% distance across the belly):
double legsWidth = 0.6 * segmentalDiam;                // m
double legsMass = 0.2;                                 // kg

B.1: Define leg bodies

We model the legs as having the same shape as the anterior and posterior halves; just scaled down. We model the cat's 4 legs using just 2 bodies: one for both front legs and another for the back legs.

Code Block
languagecpp
// Scale the segmental inertia.
Inertia legsInertia = (legsMass/segmentalMass) * segmentalInertia;

// Anterior and posterior legs.
Body * anteriorLegs = new Body();
anteriorLegs->setName("anteriorLegs");
anteriorLegs->setMass(legsMass);
anteriorLegs->setMassCenter(Vec3(0.5 * legsLength, 0, 0));
anteriorLegs->setInertia(legsInertia);

Body * posteriorLegs = new Body();
posteriorLegs->setName("posteriorLegs");
posteriorLegs->setMass(legsMass);
posteriorLegs->setMassCenter(Vec3(0.5 * legsLength, 0, 0));
posteriorLegs->setInertia(legsInertia); 

B.2: Define leg joints (i.e., between legs and two halves of cat)

Note the definition of orientALegsInLegs. We rotate the leg about what will become the pin-joint axis (the leg's Z-axis) so that it points straight out from the belly when leg angle is 0. In other words, we position the leg's long (X) axis normal to the half of the cat.

...

Code Block
languagecpp
cat.addBody(anteriorLegs);
cat.addBody(posteriorLegs);

B.3: Display geometry

We give both legs the same display geometry. We needn't create two DisplayGeometry objects.

Code Block
languagecpp
// 'box.vtp' is in the Geometry folder of an OpenSim installation.
DisplayGeometry legsDisplay = DisplayGeometry("box.vtp");
legsDisplay.setOpacity(0.5);
legsDisplay.setColor(Vec3(0.7, 0.7, 0.7));
legsDisplay.setTransform(Transform(Vec3(0.3 * legsLength, 0, 0)));
legsDisplay.setScaleFactors(Vec3(legsLength, legsDiam, legsWidth));

anteriorLegs->updDisplayer()->updGeometrySet().cloneAndAppend(legsDisplay);
anteriorLegs->updDisplayer()->setShowAxes(true);

posteriorLegs->updDisplayer()->updGeometrySet().cloneAndAppend(legsDisplay);
posteriorLegs->updDisplayer()->setShowAxes(true); 

B.4: Actuation

Code Block
languagecpp
// front legs.
CoordinateActuator * frontLegsAct = new CoordinateActuator("frontLegs");
frontLegsAct->setName("frontLegs_actuator");
frontLegsAct->setMinControl(-maxTorque);
frontLegsAct->setMaxControl(maxTorque);
cat.addForce(frontLegsAct);

// back legs.
CoordinateActuator * backLegsAct = new CoordinateActuator("backLegs");
backLegsAct->setName("backLegs_actuator");
backLegsAct->setMinControl(-maxTorque);
backLegsAct->setMaxControl(maxTorque);
cat.addForce(backLegsAct);

B.5: Enforce joint limits on the legs

In playing around with the models, it became evident that enforcing reasonable joint limits on the legs would be helpful. These objects apply a restorative force about the corresponding coordinate when the coordinate goes outside its range. The range for both of these coordinates is [-90 , 90] degrees.

...