API  4.5
For C++ developers
exampleMocoTrack.py

This is an example using the MocoTrack tool with a complex model to track walking.

1 # -------------------------------------------------------------------------- #
2 # OpenSim Moco: exampleMocoTrack.py #
3 # -------------------------------------------------------------------------- #
4 # Copyright (c) 2023 Stanford University and the Authors #
5 # #
6 # Author(s): Nicholas Bianco #
7 # #
8 # Licensed under the Apache License, Version 2.0 (the "License") you may #
9 # not use this file except in compliance with the License. You may obtain a #
10 # copy of the License at http://www.apache.org/licenses/LICENSE-2.0 #
11 # #
12 # Unless required by applicable law or agreed to in writing, software #
13 # distributed under the License is distributed on an "AS IS" BASIS, #
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15 # See the License for the specific language governing permissions and #
16 # limitations under the License. #
17 # -------------------------------------------------------------------------- #
18 
19 # This example features two different tracking problems solved using the
20 # MocoTrack tool.
21 # - The first problem demonstrates the basic usage of the tool interface
22 # to solve a torque-driven marker tracking problem.
23 # - The second problem shows how to customize a muscle-driven state tracking
24 # problem using more advanced features of the tool interface.
25 #
26 # See the README.txt next to this file for more information.
27 
28 import os
29 import opensim as osim
30 
31 def torqueDrivenMarkerTracking():
32 
33  # Create and name an instance of the MocoTrack tool.
34  track = osim.MocoTrack()
35  track.setName("torque_driven_marker_tracking")
36 
37  # Construct a ModelProcessor and add it to the tool. ModelProcessors
38  # accept a base model and allow you to easily modify the model by appending
39  # ModelOperators. Operations are performed in the order that they are
40  # appended to the model.
41  # Create the base Model by passing in the model file.
42  modelProcessor = osim.ModelProcessor("subject_walk_scaled.osim")
43  # Add ground reaction external loads in lieu of a ground-contact model.
44  modelProcessor.append(osim.ModOpAddExternalLoads("grf_walk.xml"))
45  # Remove all the muscles in the model's ForceSet.
46  modelProcessor.append(osim.ModOpRemoveMuscles())
47  # Add CoordinateActuators to the model degrees-of-freedom. This ignores the
48  # pelvis coordinates which already have residual CoordinateActuators.
49  modelProcessor.append(osim.ModOpAddReserves(250.0, 1.0))
50  track.setModel(modelProcessor)
51 
52  # Use this convenience function to set the MocoTrack markers reference
53  # directly from a TRC file. By default, the markers data is filtered at
54  # 6 Hz and if in millimeters, converted to meters.
55  track.setMarkersReferenceFromTRC("marker_trajectories.trc")
56 
57  # There is marker data in the 'marker_trajectories.trc' associated with
58  # model markers that no longer exists (i.e. markers on the arms). Set this
59  # flag to avoid an exception from being thrown.
60  track.set_allow_unused_references(True)
61 
62  # Increase the global marker tracking weight, which is the weight
63  # associated with the internal MocoMarkerTrackingCost term.
64  track.set_markers_global_tracking_weight(10)
65 
66  # Increase the tracking weights for individual markers in the data set
67  # placed on bony landmarks compared to markers located on soft tissue.
68  markerWeights = osim.MocoWeightSet()
69  markerWeights.cloneAndAppend(osim.MocoWeight("R.ASIS", 20))
70  markerWeights.cloneAndAppend(osim.MocoWeight("L.ASIS", 20))
71  markerWeights.cloneAndAppend(osim.MocoWeight("R.PSIS", 20))
72  markerWeights.cloneAndAppend(osim.MocoWeight("L.PSIS", 20))
73  markerWeights.cloneAndAppend(osim.MocoWeight("R.Knee", 10))
74  markerWeights.cloneAndAppend(osim.MocoWeight("R.Ankle", 10))
75  markerWeights.cloneAndAppend(osim.MocoWeight("R.Heel", 10))
76  markerWeights.cloneAndAppend(osim.MocoWeight("R.MT5", 5))
77  markerWeights.cloneAndAppend(osim.MocoWeight("R.Toe", 2))
78  markerWeights.cloneAndAppend(osim.MocoWeight("L.Knee", 10))
79  markerWeights.cloneAndAppend(osim.MocoWeight("L.Ankle", 10))
80  markerWeights.cloneAndAppend(osim.MocoWeight("L.Heel", 10))
81  markerWeights.cloneAndAppend(osim.MocoWeight("L.MT5", 5))
82  markerWeights.cloneAndAppend(osim.MocoWeight("L.Toe", 2))
83  track.set_markers_weight_set(markerWeights)
84 
85  # Initial time, final time, and mesh interval. The number of mesh points
86  # used to discretize the problem is computed internally using these values.
87  track.set_initial_time(0.48)
88  track.set_final_time(1.61)
89  track.set_mesh_interval(0.02)
90 
91  # Solve! Use track.solve() to skip visualizing.
92  solution = track.solveAndVisualize()
93 
94 def muscleDrivenStateTracking():
95 
96  # Create and name an instance of the MocoTrack tool.
97  track = osim.MocoTrack()
98  track.setName("muscle_driven_state_tracking")
99 
100  # Construct a ModelProcessor and set it on the tool. The default
101  # muscles in the model are replaced with optimization-friendly
102  # DeGrooteFregly2016Muscles, and adjustments are made to the default muscle
103  # parameters.
104  modelProcessor = osim.ModelProcessor("subject_walk_scaled.osim")
105  modelProcessor.append(osim.ModOpAddExternalLoads("grf_walk.xml"))
106  modelProcessor.append(osim.ModOpIgnoreTendonCompliance())
107  modelProcessor.append(osim.ModOpReplaceMusclesWithDeGrooteFregly2016())
108  # Only valid for DeGrooteFregly2016Muscles.
109  modelProcessor.append(osim.ModOpIgnorePassiveFiberForcesDGF())
110  # Only valid for DeGrooteFregly2016Muscles.
111  modelProcessor.append(osim.ModOpScaleActiveFiberForceCurveWidthDGF(1.5))
112  # Use a function-based representation for the muscle paths. This is
113  # recommended to speed up convergence, but if you would like to use
114  # the original GeometryPath muscle wrapping instead, simply comment out
115  # this line. To learn how to create a set of function-based paths for
116  # your model, see the example 'examplePolynomialPathFitter.py'.
117  modelProcessor.append(osim.ModOpReplacePathsWithFunctionBasedPaths(
118  "subject_walk_scaled_FunctionBasedPathSet.xml"))
119  track.setModel(modelProcessor)
120 
121  # Construct a TableProcessor of the coordinate data and pass it to the
122  # tracking tool. TableProcessors can be used in the same way as
123  # ModelProcessors by appending TableOperators to modify the base table.
124  # A TableProcessor with no operators, as we have here, simply returns the
125  # base table.
126  track.setStatesReference(osim.TableProcessor("coordinates.sto"))
127 
128  # This setting allows extra data columns contained in the states
129  # reference that don't correspond to model coordinates.
130  track.set_allow_unused_references(True)
131 
132  # Since there is only coordinate position data in the states references,
133  # this setting is enabled to fill in the missing coordinate speed data using
134  # the derivative of splined position data.
135  track.set_track_reference_position_derivatives(True)
136 
137  # Initial time, final time, and mesh interval.
138  track.set_initial_time(0.48)
139  track.set_final_time(1.61)
140  track.set_mesh_interval(0.02)
141 
142  # Instead of calling solve(), call initialize() to receive a pre-configured
143  # MocoStudy object based on the settings above. Use this to customize the
144  # problem beyond the MocoTrack interface.
145  study = track.initialize()
146 
147  # Get a reference to the MocoControlCost that is added to every MocoTrack
148  # problem by default.
149  problem = study.updProblem()
150  effort = osim.MocoControlGoal.safeDownCast(problem.updGoal("control_effort"))
151  effort.setWeight(0.1)
152 
153  # Put a large weight on the pelvis CoordinateActuators, which act as the
154  # residual, or 'hand-of-god', forces which we would like to keep as small
155  # as possible.
156  model = modelProcessor.process()
157  model.initSystem()
158  forceSet = model.getForceSet()
159  for i in range(forceSet.getSize()):
160  forcePath = forceSet.get(i).getAbsolutePathString()
161  if 'pelvis' in str(forcePath):
162  effort.setWeightForControl(forcePath, 10)
163 
164  # Constrain the muscle activations at the initial time point to equal
165  # the initial muscle excitation value.
166  problem.addGoal(osim.MocoInitialActivationGoal('initial_activation'))
167 
168  # Update the solver tolerances.
169  solver = osim.MocoCasADiSolver.safeDownCast(study.updSolver())
170  solver.set_optim_convergence_tolerance(1e-3)
171  solver.set_optim_constraint_tolerance(1e-4)
172 
173  # Solve and visualize.
174  solution = study.solve()
175  study.visualize(solution)
176 
177 
178 # Solve the torque-driven marker tracking problem.
179 torqueDrivenMarkerTracking()
180 
181 # Solve the muscle-driven state tracking problem.
182 muscleDrivenStateTracking()