Skip to content

Commit

Permalink
Model building demo scripts (#1081)
Browse files Browse the repository at this point in the history
* Add pendulum and generic model building scripts.

* Rename replace ModelBuilder.py (class) with functions: ModelBuildingFunctions.py

* Update comments for creating single rod.

* offSetName -> offsetName

* Use childFrame instead of childOnBody as input argument

* Add OpenSim header.

* Mention that createRodForPendulum.py must be run first.

* Update connectRodToGroundWithPinJoint.py
  • Loading branch information
aseth1 authored and aymanhab committed Nov 13, 2018
1 parent cfb58bc commit 87398e2
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 108 deletions.
108 changes: 0 additions & 108 deletions Gui/opensim/Scripts/ModelBuilder.py

This file was deleted.

164 changes: 164 additions & 0 deletions Gui/opensim/Scripts/ModelBuildingFunctions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# --------------------------------------------------------------------------- #
# OpenSim: ModelBuildingFunctions.py #
# --------------------------------------------------------------------------- #
# OpenSim is a toolkit for musculoskeletal modeling and simulation, #
# developed as an open source project by a worldwide community. Development #
# and support is coordinated from Stanford University, with funding from the #
# U.S. NIH and DARPA. See http://opensim.stanford.edu and the README file #
# for more information including specific grant numbers. #
# #
# Copyright (c) 2005-2018 Stanford University and the Authors #
# Author(s): Ayman Habib, Carmichael Ong, Ajay Seth #
# #
# 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. #
# --------------------------------------------------------------------------- #

import org.opensim.modeling
import sys

"""Use the Model Building functions to augment a copy of the current model
or to build a model from scratch in the GUI scripting shell.
This file provides convenenient model building functions which invoke
related API calls and supply default property values. The API requires
component attributes to be specified: e.g. mass, Inertia, location vectors,
..., of specific types that can be cumbersome to translate between
scripting and the C++ API. Instead, you can use these functions and
edit the related properties in the GUI's property editor.
"""

def addBodyToModel(model, bodyName):
"""Create a Body, with the provided bodyName, and add it to the
provided model.
The Body has a mass of 1 and inertia (0.1, 0.1, 0.1, 0,0,0) with
the mass center at the origin of the Body frame.
If a body with the same name already exists in the model, then this
body will be automatically renamed, and a warning will appear in the
Scripting Shell.
This function returns the new body added to the model.
"""
body = modeling.Body(bodyName, 1.0, modeling.Vec3(0.0),
modeling.Inertia(0.1, 0.1, 0.1, 0, 0, 0))
model.addBody(body)
return body

def connectBodyWithJoint(model, parentFrame, childFrame, jointName, jointType):
"""Connect a childFrame on a Body to a parentFrame (on another Body or Ground)
in the model using a Joint of the specified type.
Arguments:
model: model to be modified.
parentFrame: the Body (or affixed offset) to be connected as the parent frame;
any PhysicalFrame already in the model is suitable.
childFrame: the Body (or affixed offset) to be connected as the child frame;
can be any PhysicalFrame that is not the parent Frame.
jointName: name to be given to the newly-created Joint.
jointType is one of:
'PinJoint', 'FreeJoint', 'WeldJoint', 'PlanarJoint', 'SliderJoint',
'UniversalJoint'
returns the Joint added to connect the Body to the model
"""
validJointTypes = [
'PinJoint',
'FreeJoint',
'WeldJoint',
'PlanarJoint',
'SliderJoint',
'UniversalJoint',
]
if not jointType in validJointTypes:
raise Exception('Provided jointType %s is not valid.' %
jointType)
module = sys.modules['org.opensim.modeling']
JointClass = getattr(module, jointType)
# Instantiate the user-requested Joint class.
joint = JointClass(jointName, parentFrame, childFrame)
model.addJoint(joint)
return joint

def addOffsetToFrame(baseFrame, offsetName, trans=None, rot=None):
""" Define a PhysicalOffsetFrame in terms of its translational and rotational
offset with respect to a base (Physical) frame and add it to the model.
Arguments:
baseFrame: the PhysicalFrame in the model to offset.
offsetName: the name (string) of the OffsetFrame to be added
trans: Translational offset (Vec3) in base frame coordinates
rot: Rotational offset in the base frame as body fixed X-Y-Z Euler angles (Vec3)
return:
offset: the PhysicalOffsetFrame added to the model
"""
offset = modeling.PhysicalOffsetFrame()
offset.setName(offsetName)
if not trans is None:
offset.set_translation(trans)

if not rot is None:
offset.set_orientation(rot)

if (model.hasComponent(baseFrame.getAbsolutePathString())):
offset.connectSocket_parent(baseFrame)
baseFrame.addComponent(offset)
else:
print("baseFrame does not exist as a PhysicalFrame. No offset frame was added.")
return offset

def attachGeometryWithOffset(frame, geometry):
"""Attach geometry to provided frame. This function adds an
intermediate offset frame between the geometry and the provided frame
to permit editing its properties to move the geometry around.
This function returns the intermediate offset frame so that you can
modify its location and orientation.
"""
offset = modeling.PhysicalOffsetFrame()
offset.setName(frame.getName() + '_offset')
offset.set_translation(modeling.Vec3(0.))
offset.set_orientation(modeling.Vec3(0.))
frame.addComponent(offset);
bf = modeling.PhysicalFrame.safeDownCast(frame)
offset.connectSocket_parent(bf)
offset.attachGeometry(geometry)
return offset

def addMuscleToModel(model, muscleName, muscleType):
"""Add a muscle to the model with the provided muscleName and
specified muscleType, which is either 'Thelen2003Muscle' or
'Millard2012EquilibriumMuscle'.
Returns the muscle that was added to the model.
"""
validMuscleTypes = [
'Thelen2003Muscle',
'Millard2012EquilibriumMuscle'
]
if not muscleType in validMuscleTypes:
raise Exception('Provided muscleType %s is not valid.' %
muscleType)
module = sys.modules['org.opensim.modeling']
MuscleClass = getattr(module, muscleType)
# Instantiate the requested muscle class.
muscle = MuscleClass()
muscle.setName(muscleName);
muscle.addNewPathPoint("muscle-pt1", model.getGround(),
modeling.Vec3(0,0,0));
muscle.addNewPathPoint("muscle-pt2", model.getGround(),
modeling.Vec3(0,0.5,0));
model.addForce(muscle)
return muscle

""" Here is an example of copying the GUI's current model, adding components to
the copied model, and loading the copied model in the GUI"""
# modelCopy = getCurrentModel().clone()
# ball = addBodyToModel(modelCopy, 'ball')
# sphere = modeling.Sphere(0.15)
# attachGeometryWithOffset(newBody, sphere)
# handle = connectBodyWithJoint(modelCopy, ball, 'handle', 'SliderJoint')
# loadModel(modelCopy)

19 changes: 19 additions & 0 deletions Gui/opensim/Scripts/connectRodToGroundWithPinJoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#connectRodToGroundWithPinJoint.py
# This script must be executed following createRodForPendulum.py
# in order for the 'rod' body to exist.


# define Frames where joints will attach
P_in_g = addOffsetToFrame(ground, 'P_in_ground', modeling.Vec3(0,length,0))
C_in_rod = addOffsetToFrame(rod, 'C_in_rod')

# connect rod to the ground
pin = connectBodyWithJoint(model, P_in_g, C_in_rod, 'pin', 'PinJoint')
pin.getCoordinate().setName('theta')

model.finalizeConnections()

guiModel = model.clone()
guiModel.initSystem()

loadModel(guiModel)
34 changes: 34 additions & 0 deletions Gui/opensim/Scripts/createDoublePendulum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Create an empty model
model = modeling.Model()
model.setName("pendulum")
ground = model.getGround()

# rod dimensions
length = 1.0;
radius = 0.025;

# add body segments which are the rigid rods of the pendulum
rod1 = addBodyToModel(model, 'rod1')
attachGeometryWithOffset(rod1, modeling.Cylinder(radius, length/2))

# define Frames where joints will attach
j1_in_ground = addOffsetToFrame(ground, 'j1_in_ground', modeling.Vec3(0,2,0))
j1_in_rod1 = addOffsetToFrame(rod1, 'j1_in_rod1')
j2_in_rod1 = addOffsetToFrame(rod1, 'j2_in_rod1')

# repeat to add rod2
rod2 = addBodyToModel(model, 'rod2')
attachGeometryWithOffset(rod2, modeling.Cylinder(radius, length/2))
j2_in_rod2 = addOffsetToFrame(rod2, 'j2_in_rod2')

# connect rods to the ground and each other by Joints
j1 = connectBodyWithJoint(model, j1_in_ground, j1_in_rod1, 'j1', 'PinJoint')
j1.getCoordinate().setName('theta1')
j2 = connectBodyWithJoint(model, ground, j2_in_rod2, 'j2', 'PinJoint')
j2.getCoordinate().setName('theta2')

# create the underlying system of equations
state = model.initSystem()

loadModel(model)

14 changes: 14 additions & 0 deletions Gui/opensim/Scripts/createRodForPendulum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Create an empty model for pendulum
model = modeling.Model()
model.setName("pendulum")

# get the model's Ground (inertial) reference frame
ground = model.getGround()

# define rod dimensions
length = 1.0;
radius = 0.025;

# add rigid rod (Body) of the pendulum with cylinder geometry
rod = addBodyToModel(model, 'rod')
attachGeometryWithOffset(rod, modeling.Cylinder(radius, length/2))

0 comments on commit 87398e2

Please sign in to comment.