-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Showing
5 changed files
with
231 additions
and
108 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |