...
For the display geometry to help at all, we must transform the geometry so that it aligns with how we defined the inertial properties of the bodies. Namely, we want the centroid of the cylinder to be located at the body's mass center , and for the
...
language | cpp |
---|
(+/-0.5
...
*
...
segmentalLength,
...
0,
...
0),
...
and
...
for the symmetry axis of the cylinder to be parallel to the X axis of the body's frame.
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
using OpenSim::CoordinateActuator; // hunch CoordinateActuator * hunchAct = new CoordinateActuator("hunch"); hunchAct->setName("hunch_actuator"); hunchAct->setMinControl(-maxTorque); hunchAct->setMaxControl(maxTorque); cat.addForce(hunchAct); // wag CoordinateActuator * wagAct = new CoordinateActuator("wag"); wagAct->setName("wag_actuator"); wagAct->setMinControl(-maxTorque); wagAct->setMaxControl(maxTorque); cat.addForce(wagAct); |
We're done making the first model! Print it (serialize) to a file.
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
// First model: exhibiting the counter-rotation mechanism of flipping
// ========================================================================
// ********** PART A **********
// Second model: adding legs for the variable-inertia mechanism of flipping
// ========================================================================
// ********** PART B **********
// Define leg bodies
// ------------------------------------------------------------------------
// ********** PART B.1 **********
// Define leg joints (i.e., between legs and two halves of cat)
// ------------------------------------------------------------------------
// ********** PART B.2 **********
// Display geometry
// ------------------------------------------------------------------------
// ********** PART B.3 **********
// Actuation
// ------------------------------------------------------------------------
// ********** PART B.4 **********
// Enforce joint limits on the legs
// ------------------------------------------------------------------------
// ********** PART B.5 **********
| ||
Code Block | ||
| ||
// NOTE: Most of the set-up for this model has already been done. Many of // ---- the variables defined above are simply updated or renamed below. // This model will additionally be able to |
The twist
...
degree of freedom was defined already. Now, we just need to unlock it and add a corresponding actuator.
Code Block | ||
---|---|---|
| ||
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 |
...
// 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 body for both front legs, and another for the back legs.
Code Block | ||
---|---|---|
| ||
// 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)
...
language | cpp |
---|
...
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 | ||
---|---|---|
| ||
using OpenSim::PinJoint;
// Anterior leg
// ````````````
Vec3 locALegsInAnterior(-0.75 * segmentalLength, 0.5 * segmentalDiam, 0);
Vec3 orientALegsInAnterior(0);
Vec3 locALegsInLegs(0);
Vec3 orientALegsInLegs(0, 0, -0.5 * Pi);
PinJoint * anteriorToLegs = new PinJoint("anterior_legs",
*anteriorBody, locALegsInAnterior, orientALegsInAnterior,
*anteriorLegs, locALegsInLegs, orientALegsInLegs);
CoordinateSet & anteriorToLegsCS = anteriorToLegs->upd_CoordinateSet();
anteriorToLegsCS[0].setName("frontLegs");
double anteriorToLegsCS0range[2] = {-0.5 * Pi, 0.5 * Pi};
anteriorToLegsCS[0].setRange(anteriorToLegsCS0range);
// So that the legs are directed strictly upwards initially:
double pitch = groundAnteriorCS[1].getDefaultValue();
anteriorToLegsCS[0].setDefaultValue(-pitch);
anteriorToLegsCS[0].setDefaultLocked(false); |
The posterior leg joint is defined similarly.
Code Block | ||
---|---|---|
| ||
// Posterior leg // ````````````` Vec3 locPLegsInPosterior(0.75 * segmentalLength, 0.5 * segmentalDiam, 0); Vec3 orientPLegsInPosterior(0, Pi, 0); Vec3 locPLegsInLegs(0); Vec3 orientPLegsInLegs(0, 0, -0.5 * Pi); PinJoint * posteriorToLegs = new PinJoint("posterior_legs", *posteriorBody, locPLegsInPosterior, orientPLegsInPosterior, *posteriorLegs, locPLegsInLegs, orientPLegsInLegs); CoordinateSet & posteriorToLegsCS = posteriorToLegs->upd_CoordinateSet(); posteriorToLegsCS[0].setName("backLegs"); double posteriorToLegsCS0range[2] = {-0.5 * Pi, 0.5 * Pi}; posteriorToLegsCS[0].setRange(posteriorToLegsCS0range); posteriorToLegsCS[0].setDefaultValue(-pitch); posteriorToLegsCS[0].setDefaultLocked(false); |
Now that we've defined the joints, we can add the bodies.
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
// Both legs have the same display geometry.
// '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.5: Enforce joint limits on the legs
In playing around with models, it became evident that actually 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.
Code Block | ||
---|---|---|
| ||
using OpenSim::CoordinateLimitForce; CoordinateLimitForce * frontLegsLimitForce = new CoordinateLimitForce( "frontLegs", 90, 1.0E2, -90, 1.0E2, 1.0E1, 2.0, false); cat.addForce(frontLegsLimitForce); CoordinateLimitForce * backLegsLimitForce = new CoordinateLimitForce( "backLegs", 90, 1.0E2, -90, 1.0E2, 1.0E1, 2.0, false); cat.addForce(backLegsLimitForce); |
Code Block | ||
---|---|---|
| ||
cat.print("flippinfelines_hunch_wag_twist_legs.osim"); |
Let's put these cats to use!