/** * Run a simulation of a sliding block being pulled by two muscle */ int main() { std::clock_t startTime = std::clock(); try { /////////////////////////////////////////////// // DEFINE THE SIMULATION START AND END TIMES // /////////////////////////////////////////////// // Define the initial and final simulation times double initialTime = 0.0; double finalTime = 10.0; /////////////////////////////////////////// // DEFINE BODIES AND JOINTS OF THE MODEL // /////////////////////////////////////////// // Create an OpenSim model and set its name Model osimModel; osimModel.setName("tugOfWar"); // GROUND FRAME // Get a reference to the model's ground body Ground& ground = osimModel.updGround(); // Add display geometry to the ground to visualize in the GUI ground.attachGeometry(new Mesh("ground.vtp")); ground.attachGeometry(new Mesh("anchor1.vtp")); ground.attachGeometry(new Mesh("anchor2.vtp")); // BLOCK BODY // Specify properties of a 20 kg, 10cm length block body double blockMass = 20.0, blockSideLength = 0.1; Vec3 blockMassCenter(0); Inertia blockInertia = blockMass*Inertia::brick(blockSideLength, blockSideLength, blockSideLength); // Create a new block body with the specified properties OpenSim::Body *block = new OpenSim::Body("block", blockMass, blockMassCenter, blockInertia); // Add display geometry to the block to visualize in the GUI block->attachGeometry(new Mesh("block.vtp")); // FREE JOINT // Create a new free joint with 6 degrees-of-freedom (coordinates) // between the block and ground bodies double halfLength = blockSideLength/2.0; Vec3 locationInParent(0, halfLength, 0), orientationInParent(0); Vec3 locationInBody(0, halfLength, 0), orientationInBody(0); FreeJoint *blockToGround = new FreeJoint("blockToGround", ground, locationInParent, orientationInParent, *block, locationInBody, orientationInBody); // Set the angle and position ranges for the free (6-degree-of-freedom) // joint between the block and ground frames. double angleRange[2] = {-SimTK::Pi/2, SimTK::Pi/2}; double positionRange[2] = {-1, 1}; blockToGround->updCoordinate(FreeJoint::Coord::Rotation1X).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::Rotation2Y).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::Rotation3Z).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationX).setRange(positionRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationY).setRange(positionRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationZ).setRange(positionRange); // Add the block body to the model osimModel.addBody(block); osimModel.addJoint(blockToGround); /////////////////////////////////////// // DEFINE FORCES ACTING ON THE MODEL // /////////////////////////////////////// // MUSCLE FORCES // Create two new muscles double maxIsometricForce = 1000.0, optimalFiberLength = 0.2, tendonSlackLength = 0.1, pennationAngle = 0.0, fatigueFactor = 0.30, recoveryFactor = 0.20; // fatigable muscle (Millard2012EquilibriumMuscle with fatigue) FatigableMuscle* fatigable = new FatigableMuscle("fatigable", maxIsometricForce, optimalFiberLength, tendonSlackLength, pennationAngle, fatigueFactor, recoveryFactor); // original muscle model (muscle without fatigue) Millard2012EquilibriumMuscle* original = new Millard2012EquilibriumMuscle("original", maxIsometricForce, optimalFiberLength, tendonSlackLength, pennationAngle); // Define the path of the muscles fatigable->addNewPathPoint("fatigable-point1", ground, Vec3(0.0, halfLength, -0.35)); fatigable->addNewPathPoint("fatigable-point2", *block, Vec3(0.0, halfLength, -halfLength)); original->addNewPathPoint("original-point1", ground, Vec3(0.0, halfLength, 0.35)); original->addNewPathPoint("original-point2", *block, Vec3(0.0, halfLength, halfLength)); // Define the default states for the two muscles // Activation fatigable->setDefaultActivation(0.01); original->setDefaultActivation(0.01); // Fiber length fatigable->setDefaultFiberLength(optimalFiberLength); original->setDefaultFiberLength(optimalFiberLength); // Add the two muscles (as forces) to the model osimModel.addForce(fatigable); osimModel.addForce(original); /////////////////////////////////// // DEFINE CONTROLS FOR THE MODEL // /////////////////////////////////// // Create a prescribed controller that simply supplies controls as // a function of time. // For muscles, controls are normalized stoor-neuron excitations PrescribedController *muscleController = new PrescribedController(); muscleController->setActuators(osimModel.updActuators()); // Set the prescribed muscle controller to use the same muscle control function for each muscle muscleController->prescribeControlForActuator("fatigable", new Constant(1.0)); muscleController->prescribeControlForActuator("original", new Constant(1.0)); // Add the muscle controller to the model osimModel.addController(muscleController); // Add a Muscle analysis MuscleAnalysis* muscAnalysis = new MuscleAnalysis(&osimModel); Array<std::string> coords(blockToGround->getCoordinate(FreeJoint::Coord::TranslationZ).getName(),1); muscAnalysis->setCoordinates(coords); muscAnalysis->setComputeMoments(false); osimModel.addAnalysis(muscAnalysis); // Turn on the visualizer to view the simulation run live. osimModel.setUseVisualizer(false); ////////////////////////// // PERFORM A SIMULATION // ////////////////////////// // Initialize the system and get the state SimTK::State& si = osimModel.initSystem(); // Init coords to 0 and lock the rotational degrees of freedom so the block doesn't twist CoordinateSet& coordinates = osimModel.updCoordinateSet(); coordinates[0].setValue(si, 0); coordinates[1].setValue(si, 0); coordinates[2].setValue(si, 0); coordinates[3].setValue(si, 0); coordinates[4].setValue(si, 0); coordinates[5].setValue(si, 0); coordinates[0].setLocked(si, true); coordinates[1].setLocked(si, true); coordinates[2].setLocked(si, true); // Last coordinate (index 5) is the Z translation of the block coordinates[4].setLocked(si, true); // Compute initial conditions for muscles osimModel.equilibrateMuscles(si); // Create the integrator, force reporter, and manager for the simulation. // Create the integrator SimTK::RungeKuttaMersonIntegrator integrator(osimModel.getMultibodySystem()); integrator.setAccuracy(1.0e-6); // Create the force reporter ForceReporter* reporter = new ForceReporter(&osimModel); osimModel.updAnalysisSet().adoptAndAppend(reporter); // Create the manager Manager manager(osimModel, integrator); // Print out details of the model osimModel.printDetailedInfo(si, std::cout); // Integrate from initial time to final time manager.setInitialTime(initialTime); manager.setFinalTime(finalTime); std::cout<<"\nIntegrating from "<<initialTime<<" to "<<finalTime<<std::endl; manager.integrate(si); ////////////////////////////// // SAVE THE RESULTS TO FILE // ////////////////////////////// // Save the simulation results // Save the states auto statesTable = manager.getStatesTable(); STOFileAdapter_<double>::write(statesTable, "tugOfWar_fatigue_states.sto"); auto forcesTable = reporter->getForcesTable(); STOFileAdapter_<double>::write(forcesTable, "tugOfWar_fatigue_forces.sto"); // Save the muscle analysis results IO::makeDir("MuscleAnalysisResults"); muscAnalysis->printResults("fatigue", "MuscleAnalysisResults"); // Save the OpenSim model to a file osimModel.print("tugOfWar_fatigue_model.osim"); } catch (const std::exception& ex) { std::cout << ex.what() << std::endl; return 1; } catch (...) { std::cout << "UNRECOGNIZED EXCEPTION" << std::endl; return 1; } std::cout << "main() routine time = " << 1.e3*(std::clock()-startTime)/CLOCKS_PER_SEC << "ms\n"; std::cout << "OpenSim example completed successfully.\n"; return 0; }
/** * Create a model that does nothing. */ int main() { try { /////////////////////////////////////////// // DEFINE BODIES AND JOINTS OF THE MODEL // /////////////////////////////////////////// // Create an OpenSim model and set its name Model osimModel; osimModel.setName("tugOfWar"); // GROUND FRAME // Get a reference to the model's ground frame Ground& ground = osimModel.updGround(); // Attach geometry to the ground to visualize in the GUI ground.attachGeometry(new Mesh("ground.vtp")); ground.attachGeometry(new Mesh("anchor1.vtp")); ground.attachGeometry(new Mesh("anchor2.vtp")); // BLOCK BODY // Specify properties of a 20 kg, 0.1 m^3 block body double blockMass = 20.0, blockSideLength = 0.1; Vec3 blockMassCenter(0); Inertia blockInertia = blockMass*Inertia::brick(blockSideLength, blockSideLength, blockSideLength); // Create a new block body with the specified properties OpenSim::Body *block = new OpenSim::Body("block", blockMass, blockMassCenter, blockInertia); // Add display geometry to the block to visualize in the GUI block->attachGeometry(new Brick(SimTK::Vec3(0.05, 0.05, 0.05))); // FREE JOINT // Create a new free joint with 6 degrees-of-freedom (coordinates) between the block and ground frames Vec3 locationInParent(0, blockSideLength/2, 0), orientationInParent(0), locationInBody(0), orientationInBody(0); FreeJoint *blockToGround = new FreeJoint("blockToGround", ground, locationInParent, orientationInParent, *block, locationInBody, orientationInBody); // Set the angle and position ranges for the free (6-degree-of-freedom) // joint between the block and ground frames. double angleRange[2] = {-SimTK::Pi/2, SimTK::Pi/2}; double positionRange[2] = {-1, 1}; blockToGround->updCoordinate(FreeJoint::Coord::Rotation1X).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::Rotation2Y).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::Rotation3Z).setRange(angleRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationX).setRange(positionRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationY).setRange(positionRange); blockToGround->updCoordinate(FreeJoint::Coord::TranslationZ).setRange(positionRange); // Add the block body to the model osimModel.addBody(block); osimModel.addJoint(blockToGround); /////////////////////////////////////////////// // DEFINE THE SIMULATION START AND END TIMES // /////////////////////////////////////////////// // Define the initial and final simulation times double initialTime = 0.0; double finalTime = 3.00; ///////////////////////////////////////////// // DEFINE CONSTRAINTS IMPOSED ON THE MODEL // ///////////////////////////////////////////// // Specify properties of a constant distance constraint to limit the block's motion double distance = 0.2; Vec3 pointOnGround(0, blockSideLength/2 ,0); Vec3 pointOnBlock(0, 0, 0); // Create a new constant distance constraint ConstantDistanceConstraint *constDist = new ConstantDistanceConstraint(ground, pointOnGround, *block, pointOnBlock, distance); // Add the new point on a line constraint to the model osimModel.addConstraint(constDist); /////////////////////////////////////// // DEFINE FORCES ACTING ON THE MODEL // /////////////////////////////////////// // GRAVITY // Obtain the default acceleration due to gravity Vec3 gravity = osimModel.getGravity(); // MUSCLE FORCES // Create two new muscles with identical properties double maxIsometricForce = 1000.0, optimalFiberLength = 0.25, tendonSlackLength = 0.1, pennationAngle = 0.0; Thelen2003Muscle *muscle1 = new Thelen2003Muscle("muscle1",maxIsometricForce,optimalFiberLength,tendonSlackLength,pennationAngle); Thelen2003Muscle *muscle2 = new Thelen2003Muscle("muscle2",maxIsometricForce,optimalFiberLength,tendonSlackLength,pennationAngle); // Specify the paths for the two muscles // Path for muscle 1 muscle1->addNewPathPoint("muscle1-point1", ground, Vec3(0.0,0.05,-0.35)); muscle1->addNewPathPoint("muscle1-point2", *block, Vec3(0.0,0.0,-0.05)); // Path for muscle 2 muscle2->addNewPathPoint("muscle2-point1", ground, Vec3(0.0,0.05,0.35)); muscle2->addNewPathPoint("muscle2-point2", *block, Vec3(0.0,0.0,0.05)); // Add the two muscles (as forces) to the model osimModel.addForce(muscle1); osimModel.addForce(muscle2); // PRESCRIBED FORCE // Create a new prescribed force to be applied to the block PrescribedForce *prescribedForce = new PrescribedForce("prescribedForce", *block); // Specify properties of the force function to be applied to the block double time[2] = {0, finalTime}; // time nodes for linear function double fXofT[2] = {0, -blockMass*gravity[1]*3.0}; // force values at t1 and t2 // Create linear function for the force components PiecewiseLinearFunction *forceX = new PiecewiseLinearFunction(2, time, fXofT); // Set the force and point functions for the new prescribed force prescribedForce->setForceFunctions(forceX, new Constant(0.0), new Constant(0.0)); prescribedForce->setPointFunctions(new Constant(0.0), new Constant(0.0), new Constant(0.0)); // Add the new prescribed force to the model osimModel.addForce(prescribedForce); /////////////////////////////////// // DEFINE CONTROLS FOR THE MODEL // /////////////////////////////////// // Create a prescribed controller that simply applies controls as function of time // For muscles, controls are normalized motor-neuron excitations PrescribedController *muscleController = new PrescribedController(); muscleController->setActuators(osimModel.updActuators()); // Define linear functions for the control values for the two muscles Array<double> slopeAndIntercept1(0.0, 2); // array of 2 doubles Array<double> slopeAndIntercept2(0.0, 2); // muscle1 control has slope of -1 starting 1 at t = 0 slopeAndIntercept1[0] = -1.0/(finalTime-initialTime); slopeAndIntercept1[1] = 1.0; // muscle2 control has slope of 0.95 starting 0.05 at t = 0 slopeAndIntercept2[0] = 0.95/(finalTime-initialTime); slopeAndIntercept2[1] = 0.05; // Set the individual muscle control functions for the prescribed muscle controller muscleController->prescribeControlForActuator("muscle1", new LinearFunction(slopeAndIntercept1)); muscleController->prescribeControlForActuator("muscle2", new LinearFunction(slopeAndIntercept2)); // Add the muscle controller to the model osimModel.addController(muscleController); /////////////////////////////////// // SPECIFY MODEL DEFAULT STATES // /////////////////////////////////// // Define the default states for the two muscles // Activation muscle1->setDefaultActivation(slopeAndIntercept1[1]); muscle2->setDefaultActivation(slopeAndIntercept2[1]); // Fiber length muscle2->setDefaultFiberLength(optimalFiberLength); muscle1->setDefaultFiberLength(optimalFiberLength); // Save the model to a file osimModel.print("tugOfWar_model.osim"); ////////////////////////// // PERFORM A SIMULATION // ///////////////////////// //osimModel.setUseVisualizer(true); // Initialize the system and get the default state SimTK::State& si = osimModel.initSystem(); // Define non-zero (defaults are 0) states for the free joint CoordinateSet& modelCoordinateSet = osimModel.updCoordinateSet(); modelCoordinateSet[3].setValue(si, distance); // set x-translation value modelCoordinateSet[5].setValue(si, 0.0); // set z-translation value modelCoordinateSet[3].setSpeedValue(si, 0.0); // set x-speed value double h_start = 0.5; modelCoordinateSet[4].setValue(si, h_start); // set y-translation which is height std::cout << "Start height = "<< h_start << std::endl; osimModel.getMultibodySystem().realize(si, Stage::Velocity); // Compute initial conditions for muscles osimModel.equilibrateMuscles(si); // double mfv1 = muscle1->getFiberVelocity(si); // double mfv2 = muscle2->getFiberVelocity(si); // Create the force reporter for obtaining the forces applied to the model // during a forward simulation ForceReporter* reporter = new ForceReporter(&osimModel); osimModel.addAnalysis(reporter); // Create the manager managing the forward integration and its outputs Manager manager(osimModel); manager.setIntegratorAccuracy(1.0e-6); // Integrate from initial time to final time si.setTime(initialTime); manager.initialize(si); std::cout<<"\nIntegrating from "<<initialTime<<" to "<<finalTime<<std::endl; manager.integrate(finalTime); } catch (const std::exception& ex) { std::cerr << ex.what() << std::endl; return 1; } catch (...) { std::cerr << "UNRECOGNIZED EXCEPTION" << std::endl; return 1; } std::cout << "OpenSim environment test completed successfully. You should see a block attached to two muscles visualized in a separate window." << std::endl; return 0; }