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

A. Add Bodies to the Model

In this section, you will add the platform, pelvis, thigh, and shank segments to the model.

Setup

There are several functions in the SimTK library that can help you as you begin to write your code. The default units for OpenSim are Newtons, kilograms, meters, and radians. However, certain functions do expect angles in degrees rather than radians. Some functions that you might find useful for this exercise are:

int main()
{
    try {
		// Code to the construct the model will go here
		// Section: Setup
		// Define key model variables 
		double pelvisWidth = 0.20, thighLength = 0.40, shankLength = 0.435;

		// Create an OpenSim Model
		Model osimModel = Model();
		osimModel.setName("DynamicWalkerModel");

		// Get a reference to the ground object
        OpenSim::Body& ground = osimModel.getGroundBody();

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

		// TODO: Construct Bodies and Joints Here
		// ********** BEGIN CODE **********


		// **********  END CODE  **********

		// TODO: Construct ContactGeometry and HuntCrossleyForces Here
		// ********** BEGIN CODE **********


		// **********  END CODE  **********

		// TODO: Construct CoordinateLimitForces Heres
		// ********** BEGIN CODE **********


		// **********  END CODE  **********

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

One of the advantages of building Models in the API vs. through XML is the ability to store model parameters in variables. Here, we create variables for the width of the pelvis and the lengths of the thighs and shanks. These three variables are used throughout the exercise, such as when defining the joints, the display geometry, and the contact elements.

A Model object called "osimModel" is created and the name of the model is set to "DynamicWalkerModel." In the final model file, this has the effect of creating a Model XML element and setting the "name" attribute to DynamicWalkerModel (i.e., <Model name="DynamicWalkerModel"> ... </Model>).

The setGravity function sets the direction and magnitude of the acceleration due to gravity using a SimTK::Vec3, and adds the XML element <gravity> 0 -9.80665 0 </gravity> to the model.

The print command creates the resulting OpenSim model file. The print command must be called after all of the ModelComponents (bodies, constraints, contact geometry, controllers, etc.) are added to the Model.

Create the Platform Body

In this section, we will create our first Body, the platform. After constructing the Body object, we will add a VisibleObject to it so we can visualize it in the GUI. The base objects (e.g., box and sphere) have lengths of 1 m; scale factors are used to change the dimensions of the objects shown in the OpenSim GUI.

To create a body, you must specify the following:

  • Mass

  • Location of the center of mass from the body's origin, expressed in the body frame

  • The moments and products of inertia of the body about its center of mass, expressed in the body frame

The following API functions are used to create the body:

The following functions are used to create the visual representation of the Body in the GUI. Objects are added to the DisplayGeometry element of a Body object. The properties of the displayed object such as the color, scale, and display preference (e.g., solid, wire, shaded) can be accessed by getting a VisibleObject from the Body. The GUI looks for the geometry files (e.g., "box.vtp") in the Geometry folder of the OpenSim installation.

Copy the following block of code into your source file. Place the code after the setGravity function and before the print function, as shown in the block of code in the Setup section.

// Section: Create the Platform
double mass = 1;

// Location of the center of mass from the body origin, expressed in the body frame
Vec3 comLocInBody(0.0, 0.0, 0.0); 

// Inertia of the body expressed in the body frame
Inertia bodyInertia(1.0, 1.0, 1.0, 0.0, 0.0, 0.0);

// Create the body
OpenSim::Body* platform = new OpenSim::Body("Platform", 
                                            mass, comLocInBody, bodyInertia);

// TODO: Construct Joint Here 
// ********** BEGIN CODE **********


// **********  END CODE  **********

// Add and scale model for display in GUI
platform->addDisplayGeometry("box.vtp");
platform->updDisplayer()->setScaleFactors(Vec3(1, 0.05, 1));

// Add the platform to the Model
osimModel.addBody(platform);

// TODO: Construct the next body and joint
// ********** BEGIN CODE **********


// **********  END CODE  **********
Note: These functions are assuming that you are using units of kilograms, meters, and seconds.



Adding the Joint for the Platform Body

The figure at left is Figure 2 is from Seth, A., Sherman, M., Eastman, P., Delp, S. Minimal formulation of joint motion for biomechanisms. Nonlinear Dyn 62:291–303.

To describe the configuration of each body you create, you will need to create an OpenSim::Joint between your new Body and a parent Body. Every model in OpenSim begins with a Ground body. To create a model in OpenSim, you will iteratively create and connect bodies together in a tree topology.

Each body has its own origin point and a body-fixed reference frame. A joint is created by prescribing a set of mobilities that describe the kinematic relationship between a point and reference frame on the parent body and a point and reference frame on the child body.

For example, to create a pin joint, you would specify that between the parent and child frames, there is no translation as a result of the mobilizer (i.e., point P is collocated with point B), and that the orientation of the two bodies changes about a single shared axis as a function of the mobilizer parameter. In the figure to the left, this specification is the mobilizer (shown as a bold, dashed arrow).

Because the location and orientation of the mobilizer can be chosen freely, we also need to specify the transformation from the body's origin and reference frame to the joint's origin and reference frame in each of the two bodies.

In the OpenSim API, these transformations are represented by a series of three-element "Vectors" (SimTK::Vec3). The first SimTK::Vec3 holds the measures of the the translation vector from the Body origin to the Joint origin. The second SimTK::Vec3 holds the angular measures (Euler angles) describing the orientation, assuming an X-Y-Z body-fixed rotation sequence.

 

 

To create a joint, you must specify:

  • Parent body

  • Location of the joint origin in the parent body, as measured from the parent origin and expressed in the parent frame

  • Orientation of the joint frame in the parent body, as measured with respect to the parent frame

  • Child body

  • Location of the joint origin in the child body, as measured from the child origin and expressed in the child frame

  • Orientation of the joint frame in the child body, as measured with respect to the child frame

The following API function is used to create the pin joint:

 

// Section: Create the Platform Joint
// Create the joint connecting the platform to the ground
Vec3 locationInParent(0.0, 0.0, 0.0);
Vec3 orientationInParent(0.0, 0.0, 0.0);
Vec3 locationInChild(0.0, 0.0, 0.0);
Vec3 orientationInChild(0.0, 0.0, 0.0);
PinJoint* platformToGround = new PinJoint("PlatformToGround",
        ground, locationInParent, orientationInParent,
        *platform, locationInChild, orientationInChild);

// TODO: Set the properties of the coordinates in the Pin Joint here  
// ********** BEGIN CODE **********


// **********  END CODE  **********

Note: These functions are assuming that you are using units of kilograms, meters, and seconds.

 

Setting the Properties of the Coordinates in the Pin Joint

The Coordinate objects that define a joint are held in the CoordinateSet of the Joint. You can access the Coordinates inside the CoordinateSet using "get" functions or by indexing into the CoordinateSet like an array. The various OpenSim Joints have different numbers of degrees of freedom; the number of Coordinates in a Joint's CoordinateSet is equal to the number of degrees of freedom of the joint. For example, a PinJoint's CoordinateSet has only one Coordinate.

"get…" and "upd…" methods

Several naming conventions are used in the OpenSim API (e.g., see the table under "Coding Standards / Naming conventions" in the instructions for developers here). Note, in particular, the difference between "get…" and "upd…" methods. Many methods have both "get" and "upd" versions, such as Joint::getParentBody() and Joint::updParentBody(). In this example, the "get" version returns a const reference to the parent OpenSim::Body ("const" for constant) and would be used to read the properties of the parent body (e.g., its name); the "upd" version returns a writable reference to the parent OpenSim::Body and would be used if the parent body's properties were being changed.

 

The following API function can be used on a Joint to get access to its CoordinateSet:

The following API functions are used to set the properties of the coordinates in the joint:

  • OpenSim::CoordinateSet::setName(const std::string& name)

  • OpenSim::CoordinateSet::setRange(double* aRange)

  • OpenSim::CoordinateSet::setDefaultValue(double aDefaultValue_IN_RADIANS)

  • OpenSim::CoordinateSet::setDefaultLocked(bool aLocked)

 

Compile the code and run the project executable to create your model; it should be located in your build directory. Check your model in the OpenSim GUI.

Create the Pelvis Body

In this section you will create the Pelvis body and connect the platform to the pelvis via a FreeJoint, which is a 6-degree-of-freedom joint. The starter code below sets up the construction of the Pelvis and the display for the GUI. Copy the following block of code into your source file after the code creating the platform and before the print command. In the marked section, set up the coordinates of the joint.

A modeling choice was made here to connect the pelvis to the platform so that pelvis height in relation to the contact surface does not change when the platform rotates. The SimTK core can handle any tree topology for the bodies in the system. For example, an alternative model could be created where both the pelvis and platform are connected to Ground.

The CoordinateSet for a FreeJoint contains 6 coordinates. The coordinates are ordered X-Y-Z, starting first with rotations and then translations.

Set up the six coordinates of the FreeJoint as follows:

  • Coordinate 0:  Name(pelvis_rx), Range(-pi, pi) rad, DefaultValue(0) rad, DefaultLocked(true)

  • Coordinate 1:  Name(pelvis_ry), Range(-pi, pi) rad, DefaultValue(0) rad, DefaultLocked(true)

  • Coordinate 2:  Name(pelvis_rz), Range(-pi, pi) rad, DefaultValue(0) rad, DefaultLocked(true)

  • Coordinate 3:  Name(pelvis_tx), Range(-10, 10) m, DefaultValue(0) m

  • Coordinate 4:  Name(pelvis_ty), Range(-1, 2) m, DefaultValue(1.0) m

  • Coordinate 5:  Name(pelvis_tz), Range(-1, 1) m, DefaultValue(0) m, DefaultLocked(true)

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.