1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
/* -------------------------------------------------------------------------- *
* Simbody(tm) Example: ClosedTopologyMechanism *
* -------------------------------------------------------------------------- *
* This is part of the SimTK biosimulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
* *
* Portions copyright (c) 2012 Stanford University and the Authors. *
* Authors: Michael Sherman *
* Contributors: *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain a *
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* -------------------------------------------------------------------------- */
#include "Simbody.h"
using namespace SimTK;
/* This example shows two different ways to create a closed-topology mechanism:
by cutting a joint and by cutting a body. Generally it is easier to cut a body
because then all joints are treated the same way, and the only constraint
needed is a Weld constraint to glue the broken body back together.
The mechanism here is a spatial crank/rocker with three bodies. We're using
a ground frame orientation in which Y is up, X is right, and Z is pointing
out of the screen at the viewer. The crank is a disk we'll call "rotor", with
its axis connected by a Pin mobilizer (1 dof revolute internal coordinate
joint) to ground along the X axis. The rocker is long thin rectangle pinned
at the top to ground along the Z axis, and initially hanging down in the -Y
direction. Finally there is a "linker" body connected to a point on the
surface of the rotor by a Ball mobilizer (3 dof spherical internal coordinate
joint). The linker will connect to the bottom of the rocker by another ball
joint, which gives the mechanism a closed topology. One option is to implement
this final joint directly using a Ball constraint (3 constraint equations).
Another is to split the linker into two overlapping halves, linkerA and linkerB
with half the mass properties each, connect linkerB to the rocker with a Ball
mobilizer, and then glue the two halves back together with a Weld constraint
(6 constraint equations). We'll do it both ways here and then perform a dynamic
simulation (applying a torque to the rotor) and output the final state to
show that the mechanisms are equivalent.
Here the choice of which approach is better is easy -- the direct approach
gives us a 5 dof tree system and 3 constraints; the second gives an
8 dof tree system and 6 constraints which is much bigger. And the Ball
constraint is a Simbody built-in and no harder to work with than a Ball
mobilizer. But in general the split-body approach can be very appealing --
for example, if the loop were closed with a pin joint (a very simple
mobilizer) the corresponding awkward constraint would have 5 equations, isn't
a built-in, would have no convenient axis along which to apply forces or
measure rotations, and special treatment would be required to handle multiple
rotations if they were to be tracked.
(In case you're counting, yes this mechanism has two net degrees of freedom
because the two ball joints permit the linker to rotate about its long axis.)
*/
int main() {
try { // catch errors if any
// --------------------------------------------------------------
// Create the system, with subsystems for bodies and some forces.
// --------------------------------------------------------------
MultibodySystem system;
SimbodyMatterSubsystem matter(system);
GeneralForceSubsystem forces(system);
Force::Gravity(forces, matter, -YAxis, 9.8);
// The rotor will be centered at (0,0,0).
const Vec3 RockerLocation = Vec3(1.75,1.5,0); // Where to pin rocker.
const Real TorqueOnRotor = 4; // How hard to crank.
const Vec3 Mech2Offset = Vec3(5,0,0); // Shift for 2nd mechanism.
// Some useful rotation matrices we'll need below.
const Rotation YtoX(-Pi/2, ZAxis); // Reorient to put Y where X was.
const Rotation ZtoX( Pi/2, YAxis); // Reorient to put Z where X was.
// --------------------------------------------------------------
// Define body information. These are just convenient collections
// of related information useful for constructing the Mobilized
// Bodies that actually comprise the multibody system.
// --------------------------------------------------------------
// Define body "rotor".
Real rotorMass = 10, rotorRadius = 1, rotorHalfThickness = .1;
Body::Rigid rotorInfo(MassProperties(rotorMass, Vec3(0),
UnitInertia::cylinderAlongX(rotorRadius, rotorHalfThickness)));
rotorInfo.addDecoration(YtoX,
DecorativeCylinder(rotorRadius, rotorHalfThickness)); // along Y
// Define body "rocker".
Real rockerMass = 3; Vec3 rockerHalfDims(.1, 1, .1);
Body::Rigid rockerInfo(MassProperties(rockerMass, Vec3(0),
UnitInertia::brick(rockerHalfDims)));
rockerInfo.addDecoration(Vec3(0),
DecorativeBrick(rockerHalfDims).setColor(Red));
// Define a full "linker" for mechanism 1.
Real linkerMass = 0.5; Vec3 linkerHalfDims(.6, .05, .05);
Body::Rigid linkerInfo(MassProperties(linkerMass, Vec3(0),
UnitInertia::brick(linkerHalfDims)));
linkerInfo.addDecoration(Vec3(0),
DecorativeBrick(linkerHalfDims).setColor(Blue));
// Define a half "linker" for mechanism 2 (we'll use it twice). The only
// change is to use half the mass (since UnitInertia gets scaled by mass).
Body::Rigid halfLinkerInfo(MassProperties(linkerMass/2, Vec3(0),
UnitInertia::brick(linkerHalfDims)));
halfLinkerInfo.addDecoration(Vec3(0),
DecorativeBrick(linkerHalfDims).setColor(Blue).setOpacity(0.5));
// --------------------------------------------------------------
// MECHANISM 1 (Ball constraint)
// --------------------------------------------------------------
// Note that the Pin mobilizer is defined to rotate about local Z so we
// need to create local frames with Z pointing along the bodies' X.
MobilizedBody::Pin rotor1(matter.Ground(), ZtoX,
rotorInfo, ZtoX);
MobilizedBody::Pin rocker1(matter.Ground(), RockerLocation,
rockerInfo, Vec3(0,rockerHalfDims[1],0));
MobilizedBody::Ball linker1
(rotor1, Vec3(rotorHalfThickness,0,.8*rotorRadius),
linkerInfo, Vec3(-linkerHalfDims[0],0,0));
// Add a ball constraint instead of a ball mobilizer to connect
// linker to rocker.
Constraint::Ball(linker1, Vec3(linkerHalfDims[0],0,0),
rocker1, Vec3(0,-rockerHalfDims[1],0));
// Apply a constant torque about the rotor's Pin axis.
Force::MobilityConstantForce(forces, rotor1, 0, TorqueOnRotor);
// --------------------------------------------------------------
// MECHANISM 2 (Split linker + Weld constraint)
// --------------------------------------------------------------
MobilizedBody::Pin rotor2(matter.Ground(), Transform(ZtoX,Mech2Offset),
rotorInfo, ZtoX);
MobilizedBody::Pin rocker2(matter.Ground(), RockerLocation+Mech2Offset,
rockerInfo, Vec3(0,rockerHalfDims[1],0));
// First half-linker connects to the rotor just as above.
MobilizedBody::Ball linker2a
(rotor2, Vec3(rotorHalfThickness,0,.8*rotorRadius),
halfLinkerInfo, Vec3(-linkerHalfDims[0],0,0));
// Second half-linker connects to the rocker at the other end.
MobilizedBody::Ball linker2b
(rocker2, Vec3(0,-rockerHalfDims[1],0),
halfLinkerInfo, Vec3(linkerHalfDims[0],0,0));
// Now add a weld constraint to glue the half-linkers together. We're
// taking the default which places the Weld at the body origins.
Constraint::Weld(linker2a, linker2b);
// Apply the same constant torque about the rotor's Pin axis.
Force::MobilityConstantForce(forces, rotor2, 0, TorqueOnRotor);
// --------------------------------------------------------------
// SET UP VISUALIZATION, RUN SIMULATION
// --------------------------------------------------------------
// Ask for visualization every 1/30 second.
system.setUseUniformBackground(true); // turn off floor
Visualizer viz(system);
system.addEventReporter(new Visualizer::Reporter(viz, 1./30));
// Initialize the system and obtain the default state.
State state = system.realizeTopology();
viz.report(state); // draw frame
printf("Not yet assembled ... (hit ENTER)\n");
getchar();
// Assemble both systems into identical configurations to a tight
// tolerance. (Note that the linker is free to rotate about its long
// axis so might not come out exactly the same but it doesn't matter.)
Assembler assembler(system);
assembler.lockMobilizer(rotor1);
assembler.lockMobilizer(rotor2);
assembler.setAccuracy(1e-10);
assembler.assemble(state);
viz.report(state);
printf("Assembled ... (hit ENTER)\n");
getchar();
// Simulate for 10 seconds.
RungeKuttaMersonIntegrator integ(system);
integ.setAccuracy(1e-5); // default is 1e-3
TimeStepper ts(system, integ);
ts.initialize(state);
ts.stepTo(10);
state = ts.getState(); // retrieve final state
// --------------------------------------------------------------
// OUTPUT FINAL ANGLES AND RATES - should match to accuracy.
// --------------------------------------------------------------
printf("Final angles: rotor1=%g, rocker1=%g\n",
rotor1.getAngle(state), rocker1.getAngle(state));
printf(" roter2=%g, rocker2=%g\n",
rotor2.getAngle(state), rocker2.getAngle(state));
printf("Final rates: rotor1=%g, rocker1=%g\n",
rotor1.getRate(state), rocker1.getRate(state));
printf(" roter2=%g, rocker2=%g\n",
rotor2.getRate(state), rocker2.getRate(state));
} catch (const std::exception& e) {
std::cout << "ERROR: " << e.what() << std::endl;
return 1;
}
return 0;
}
|