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