You are viewing the documentation for OpenSim 3.x. Are you looking for the latest OpenSim 4.0 Documentation?

Performing a Simulation Part One

The steps in part one are:

Create an OpenSim Model

To perform a simulation, we first create an OpenSim model and set its name in our main program.

#include <OpenSim/OpenSim.h>
using namespace OpenSim; 
 
int main()
{
  try {
  // Create an OpenSim model and set its name
  Model osimModel;
  osimModel.setName("tugOfWar");
  }
  catch (OpenSim::Exception ex) {
  std::cout << ex.getMessage() << std::endl;
  return 1;
} 
 
std::cout << "OpenSim example completed successfully.\n";
return 0;
} 


Exercise 1: This version of the example is available as TugOfWar1_CreateModel.cpp. While the main program above compiles and runs, the OpenSim model it creates is "empty" and no information is saved to a file.

Note that you only need to include the header file <OpenSim/OpenSim.h> at the top of the file. Also, note the line: 

using namespace OpenSim;  

This line is required to avoid having to prefix every symbol with OpenSim:: since all OpenSim classes live in the namespace OpenSim. Another namespace that will appear later in this guide is SimTK which is utilized by OpenSim for many fundamental classes. Please see Chapter 5 for details.

You should now compile and run the program:

  1. Run CMake to generate a Visual Studio solution file. If you installed OpenSim in a location different from the default then you need to change the value of the CMake variable OPENSIM_INSTALL_DIR to point to the actual installation directory. This can be done either in the CMake interface (after you "Configure" and before you "Generate") or alternatively it could be done directly in the CMakeLists.txt file.
  2. Launch Visual Studio and load in the solution file that CMake just created.
  3. Within Visual Studio, set the Configuration to "RelWithDebInfo" (that is "release with debug information" – unfortunately it won't work in Debug).
  4. Compile and run the program. If it is working, it should output "OpenSim example completed successfully." and do nothing else. If it doesn't work, be sure to resolve the problem before attempting to move any further through the exercise.

 

Get the Model's Ground Body

A new OpenSim model comes with a ground body. This ground body, however, has no geometry attached to it. After we have created an OpenSim model, we get a reference to the model's ground body. We can then add display geometry to it so we can visualize it in the OpenSim GUI:

 

// Get a reference to the model's ground body
OpenSim::Body& ground = osimModel.getGroundBody(); 
 
// Add display geometry to the ground to visualize in the GUI
ground.addDisplayGeometry("ground.vtp");
ground.addDisplayGeometry("anchor1.vtp");
ground.addDisplayGeometry("anchor2.vtp");

OpenSim allows for files of type .vtp, .stl and .obj as display geometry. At this point we still haven't saved any information to a file, so the model cannot be opened or visualized within the OpenSim GUI. We'll fix that next.

Save the Model to a File

After we have created a ground body and added its display geometry, we save the model to a file with the ".osim" extension in order to visualize the model we have created.

// Save the model to a file
osimModel.print("tugOfWar_model.osim");

 

Exercise 2: After we compile and run the TugOfWar2_AddGround.cpp main program, we can open the model file tugOfWar_model.osim in the OpenSim GUI and visualize the ground body (highlighted with green for this guide).

Figure: Model with only visible ground geometry.

Except for the colors, you should see an image in the GUI like the one above.

Create a New Block Body

To add an additional body to the OpenSim model, we create a new block body with inertial properties and add display geometry to it.

using namespace SimTK;
⋮
// 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(Vec3(blockSideLength/2.));

// Create a new block body with specified properties
OpenSim::Body *block = new OpenSim::Body("block", blockMass, blockMassCenter, blockInertia);

// Add display geometry to the block to visualize in the GUI
block->addDisplayGeometry("block.vtp");

The classes Vec3 and Inertia live in the namespace SimTK and are explained in Chapter 5. You can write them as SimTK::Vec3 and SimTK::Inertia or include a "using namespace" statement as we did above. 

Also, note that the units for mass and length are kilograms and meters, respectively. OpenSim uses the SI convention (length in meters; mass in kilograms; time in seconds; forces in Newtons; and moments/torques are in Newton-meters). Angles can be in degrees or radians; internally, OpenSim uses radians.

Programming Note: OpenSim model objects in this example are allocated on the heap using "new". Whenever they are added to the model, the model takes ownership of these objects and you shouldn't call "delete" on these objects, otherwise the model will be left holding onto stale pointers. Objects owned by the model will be deleted by the model destructor. 

At this point, the block body is not connected to the OpenSim model and cannot be used or visualized in the GUI. To achieve that, the block body has to be connected to the ground body (or any other body already in the model) with a joint. We'll do that next.

Create a Joint between the Ground and the Block

Before we add the block body to the OpenSim model, we create a new free joint (i.e., 6 degrees-of-freedom) between the block and ground.

 

// Create a new free joint with 6 degrees-of-freedom (coordinates) between the block and ground bodies
Vec3 locationInParent(0, blockSideLength/2, 0), orientationInParent(0), locationInBody(0), orientationInBody(0);
FreeJoint *blockToGround = new FreeJoint("blockToGround", ground, locationInParent, orientationInParent, *block, locationInBody, orientationInBody); 
 
// Get a reference to the coordinate set (6 degrees-of-freedom) between the block and ground bodies
CoordinateSet& jointCoordinateSet = blockToGround->upd_CoordinateSet(); 
 
// Set the angle and position ranges for the coordinate set (SimTK:: prefix not actually needed here)
double angleRange[2] = {-SimTK::Pi/2, SimTK::Pi/2};
double positionRange[2] = {-1, 1};
 
jointCoordinateSet[0].setRange(angleRange);
jointCoordinateSet[1].setRange(angleRange);
jointCoordinateSet[2].setRange(angleRange);
jointCoordinateSet[3].setRange(positionRange);
jointCoordinateSet[4].setRange(positionRange);
jointCoordinateSet[5].setRange(positionRange);

At this point, the block body and corresponding free joint are ready to be added to the OpenSim model.

Although we defined a FreeJoint in this example, different kinds of joints are available, with corresponding constructors:

  • WeldJoint
  • PinJoint
  • SliderJoint
  • BallJoint
  • EllipsoidJoint
  • CustomJoint: This is a more general joint that enables joint motion about/along six spatial axes to be specified as user-supplied functions of joint coordinates.
  • Joint: an abstract definition that is sub-classed to create new types of joints (e.g., EllipsoidJoint)

Add the Block Body to the Model

To finish this step, we simply add the block body to the OpenSim model.

// Add the block body to the model
osimModel.addBody(block);

 

Exercise 3: After we compile and run the current main program, we can open the model in the OpenSim GUI (same file name tugOfWar_model.osim as in the earlier step) and visualize the ground body (highlighted with green for this guide) and the block body (highlighted with blue for this guide). You'll also be able to open the "coordinate viewer" within the GUI and interactively change the coordinates. For the FreeJoint, the built-in names of the coordinates are "X-rotation", "Y-rotation", "Z-rotation" followed by the three translations "X-translation", "Y-translation", and "Z-translation". These names, however, can be changed by calling the coordinate's setName() method directly.


Figure: Model of a moving free block between two fixed anchors.

Except for the colors, the model in the GUI should look like the image above. Be sure to go to the "coordinates" pane, move the sliders corresponding to the six coordinates, and note the effect that has on the block's position and orientation.

Define Gravity

In order for the block to actually fall during the simulation, we define the acceleration of gravity to pull the block towards the ground. The actual direction of the vector is arbitrary; however, OpenSim uses the convention that gravity is in the negative Y-direction in the models included with the OpenSim distribution.

// Define the acceleration of gravity
osimModel.setGravity(Vec3(0,-9.80665,0)); 



OpenSim is supported by the Mobilize Center , an NIH Biomedical Technology Resource Center (grant P41 EB027060); the Restore Center , an NIH-funded Medical Rehabilitation Research Resource Network Center (grant P2C HD101913); and the Wu Tsai Human Performance Alliance through the Joe and Clara Tsai Foundation. See the People page for a list of the many people who have contributed to the OpenSim project over the years. ©2010-2024 OpenSim. All rights reserved.