1 /* -------------------------------------------------------------------------- *
2  *                       OpenSim:  buildHopperModel.cpp                       *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Copyright (c) 2005-2017 Stanford University and the Authors                *
11  * Author(s): Chris Dembia, Shrinidhi K. Lakshmikanth, Ajay Seth,             *
12  *            Thomas Uchida                                                   *
13  *                                                                            *
14  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
15  * not use this file except in compliance with the License. You may obtain a  *
16  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
17  *                                                                            *
18  * Unless required by applicable law or agreed to in writing, software        *
19  * distributed under the License is distributed on an "AS IS" BASIS,          *
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
21  * See the License for the specific language governing permissions and        *
22  * limitations under the License.                                             *
23  * -------------------------------------------------------------------------- */
24 
25 /* Build an OpenSim model of a single-legged hopping mechanism. The mechanism
26 consists of a brick (pelvis) and two cylinders (thigh and shank segments), all
27 pin-connected, and is constrained to remain upright. A contact sphere (foot)
28 prevents the end of the lower cylinder from passing through the floor. A vastus
29 muscle wraps around a cylinder (patella) at the knee and a controller (brain)
30 generates the muscle excitation signal required to produce a small hop when the
31 mechanism is simulated. Two PhysicalOffsetFrames have been defined for attaching
32 an assistive device (to help the hopper hop higher); their absolute path names
33 are:
34 
35     - on the thigh: "/Dennis/bodyset/thigh/deviceAttachmentPoint"
36     - on the shank: "/Dennis/bodyset/shank/deviceAttachmentPoint"
37 
38 You don't need to add anything in this file, but you should know what
39 buildHopper() is doing. */
40 
41 #include <OpenSim/OpenSim.h>
42 
43 namespace OpenSim {
44 
buildHopper(bool showVisualizer)45 Model buildHopper(bool showVisualizer) {
46     using SimTK::Vec3;
47     using SimTK::Inertia;
48 
49     // Create a new OpenSim model on earth.
50     auto hopper = Model();
51     hopper.setName("Dennis");
52     hopper.setGravity(Vec3(0, -9.80665, 0));
53 
54     // Create the pelvis, thigh, and shank bodies.
55     double pelvisMass = 30., pelvisSideLength = 0.2;
56     auto pelvisInertia = pelvisMass * Inertia::brick(Vec3(pelvisSideLength/2.));
57     auto pelvis = new Body("pelvis", pelvisMass, Vec3(0), pelvisInertia);
58 
59     double linkMass = 10., linkLength = 0.5, linkRadius = 0.035;
60     auto linkInertia = linkMass *
61                        Inertia::cylinderAlongY(linkRadius, linkLength/2.);
62     auto thigh = new Body("thigh", linkMass, Vec3(0), linkInertia);
63     auto shank = new Body("shank", linkMass, Vec3(0), linkInertia);
64 
65     // Add the bodies to the model (the model takes ownership).
66     hopper.addBody(pelvis);
67     hopper.addBody(thigh);
68     hopper.addBody(shank);
69 
70     // Attach the pelvis to ground with a vertical slider joint, and attach the
71     // pelvis, thigh, and shank bodies to each other with pin joints.
72     Vec3 sliderOrientation(0, 0, SimTK::Pi/2.);
73     auto sliderToGround = new SliderJoint("slider", hopper.getGround(), Vec3(0),
74                         sliderOrientation, *pelvis, Vec3(0), sliderOrientation);
75     Vec3 linkDistalPoint(0, -linkLength/2., 0);
76     Vec3 linkProximalPoint(0, linkLength/2., 0);
77     // Define the pelvis as the parent so the reported value is hip flexion.
78     auto hip = new PinJoint("hip", *pelvis, Vec3(0), Vec3(0), *thigh,
79                             linkProximalPoint, Vec3(0));
80     // Define the shank as the parent so the reported value is knee flexion.
81     auto knee = new PinJoint("knee", *shank, linkProximalPoint, Vec3(0), *thigh,
82                              linkDistalPoint, Vec3(0));
83 
84     // Add the joints to the model.
85     hopper.addJoint(sliderToGround);
86     hopper.addJoint(hip);
87     hopper.addJoint(knee);
88 
89     // Set the coordinate names and default values. Note that we need "auto&"
90     // here so that we get a reference to the Coordinate rather than a copy.
91     auto& sliderCoord =
92         sliderToGround->updCoordinate(SliderJoint::Coord::TranslationX);
93     sliderCoord.setName("yCoord");
94     sliderCoord.setDefaultValue(1.);
95 
96     auto& hipCoord = hip->updCoordinate(PinJoint::Coord::RotationZ);
97     hipCoord.setName("hipFlexion");
98     hipCoord.setDefaultValue(0.35);
99 
100     auto& kneeCoord = knee->updCoordinate(PinJoint::Coord::RotationZ);
101     kneeCoord.setName("kneeFlexion");
102     kneeCoord.setDefaultValue(0.75);
103 
104     // Limit the range of motion for the hip and knee joints.
105     double hipRange[2] = {110., -90.};
106     double hipStiff[2] = {20., 20.}, hipDamping = 5., hipTransition = 10.;
107     auto hipLimitForce = new CoordinateLimitForce("hipFlexion", hipRange[0],
108         hipStiff[0], hipRange[1], hipStiff[1], hipDamping, hipTransition);
109     hip->addComponent(hipLimitForce);
110 
111     double kneeRange[2] = {140., 10.};
112     double kneeStiff[2] = {50., 40.}, kneeDamping = 2., kneeTransition = 10.;
113     auto kneeLimitForce = new CoordinateLimitForce("kneeFlexion", kneeRange[0],
114         kneeStiff[0], kneeRange[1], kneeStiff[1], kneeDamping, kneeTransition);
115     knee->addComponent(kneeLimitForce);
116 
117     // Create a constraint to keep the foot (distal end of the shank) directly
118     // beneath the pelvis (the Y-axis points upwards).
119     auto constraint = new PointOnLineConstraint(hopper.getGround(), Vec3(0,1,0),
120                       Vec3(0), *shank, linkDistalPoint);
121     shank->addComponent(constraint);
122 
123     // Use a contact model to prevent the foot (ContactSphere) from passing
124     // through the floor (ContactHalfSpace).
125     auto floor = new ContactHalfSpace(Vec3(0), Vec3(0, 0, -SimTK::Pi/2.),
126                                       hopper.getGround(), "floor");
127     double footRadius = 0.1;
128     auto foot = new ContactSphere(footRadius, linkDistalPoint, *shank, "foot");
129 
130     double stiffness = 1.e8, dissipation = 0.5, friction[3] = {0.9, 0.9, 0.6};
131     auto contactParams = new HuntCrossleyForce::ContactParameters(stiffness,
132                          dissipation, friction[0], friction[1], friction[2]);
133     contactParams->addGeometry("floor");
134     contactParams->addGeometry("foot");
135     auto contactForce = new HuntCrossleyForce(contactParams);
136 
137     // Add the contact-related components to the model.
138     hopper.addContactGeometry(floor);
139     hopper.addContactGeometry(foot);
140     hopper.addForce(contactForce);
141 
142     // Create the vastus muscle and set its origin and insertion points.
143     double mclFmax = 4000., mclOptFibLen = 0.55, mclTendonSlackLen = 0.25,
144            mclPennAng = 0.;
145     auto vastus = new Thelen2003Muscle("vastus", mclFmax, mclOptFibLen,
146                                        mclTendonSlackLen, mclPennAng);
147     vastus->addNewPathPoint("origin", *thigh, Vec3(linkRadius, 0.1, 0));
148     vastus->addNewPathPoint("insertion", *shank, Vec3(linkRadius, 0.15, 0));
149     hopper.addForce(vastus);
150 
151     // Attach a cylinder (patella) to the distal end of the thigh over which the
152     // vastus muscle can wrap.
153     auto patellaFrame = new PhysicalOffsetFrame("patellaFrame",
154         *thigh, SimTK::Transform(linkDistalPoint));
155     auto patella = new WrapCylinder();
156     patella->setName("patella");
157     patella->set_radius(0.08);
158     patella->set_length(linkRadius*2.);
159     patella->set_quadrant("x");
160 
161     patellaFrame->addWrapObject(patella);
162     thigh->addComponent(patellaFrame);
163 
164     // Configure the vastus muscle to wrap over the patella.
165     vastus->updGeometryPath().addPathWrap(*patella);
166 
167     // Create a controller to excite the vastus muscle.
168     auto brain = new PrescribedController();
169     brain->setActuators(hopper.updActuators());
170     double t[3] = {0.0, 2.0, 3.9}, x[3] = {0.3, 1.0, 0.1};
171     auto controlFunction = new PiecewiseConstantFunction(3, t, x);
172     brain->prescribeControlForActuator("vastus", controlFunction);
173     hopper.addController(brain);
174 
175     // Create frames on the thigh and shank segments for attaching the device.
176     auto thighAttachment = new PhysicalOffsetFrame("deviceAttachmentPoint",
177                            *thigh, SimTK::Transform(Vec3(linkRadius, 0.15, 0)));
178     auto shankAttachment = new PhysicalOffsetFrame("deviceAttachmentPoint",
179                            *shank, SimTK::Transform(Vec3(linkRadius, 0, 0)));
180     thigh->addComponent(thighAttachment);
181     shank->addComponent(shankAttachment);
182 
183     // Attach geometry to the bodies and enable the visualizer.
184     auto pelvisGeometry = new Brick(Vec3(pelvisSideLength/2.));
185     pelvisGeometry->setColor(Vec3(0.8, 0.1, 0.1));
186     pelvis->attachGeometry(pelvisGeometry);
187 
188     auto linkGeometry = new Cylinder(linkRadius, linkLength/2.);
189     linkGeometry->setColor(Vec3(0.8, 0.1, 0.1));
190     thigh->attachGeometry(linkGeometry);
191     shank->attachGeometry(linkGeometry->clone());
192 
193     if(showVisualizer)
194         hopper.setUseVisualizer(true);
195 
196     return hopper;
197 }
198 
199 } // end of namespace OpenSim
200