Hi Nick!
Thank you so much for your response, it couldn't have come at a better time for my project! The recent release was exactly what I needed.
I just updated my Conda environment to the latest version and downloaded the new Moco inverse example from the official OpenSim documentation. Then, I adapted the code to fit the specific needs of my application.
To make things more manageable, I created a more straightforward example (for my application), which you can check out here:
https://github.com/anacsousa1/moco-tuto ... rs.py#L300
In this example, I define muscle groups that work together as synergies.
Here's a quick summary of what I did:
First, I defined the muscle groups (I can also exclude some later):
Code: Select all
muscle_groups = {
"hamst": ["/forceset/bifemlh_r", "/forceset/bifemsh_r"],
"glut": ["/forceset/glut_max2_r"],
"quads": ["/forceset/rect_fem_r", "/forceset/vas_int_r"],
"psoas": ["/forceset/psoas_r"],
"gast": ["/forceset/med_gas_r", "/forceset/soleus_r"],
"tibialis": ["/forceset/tib_ant_r"],
}
# Specify the muscle groups to be excluded from activation
exclude_groups = {"glut", "tibialis", "psoas"}
# The number of synergies is equal to the number of muscle groups.
numSynergies = len(muscle_groups)
I built a list to store the names of all muscles in the model and then exported the control signals (muscle activations). Afterwards, I looped over each muscle group, combining them with the controllers.
Later, one of the more challenging parts was applying Non-negative Matrix Factorization (NNMF) to generate the synergies. This is new to me, but I think I got somewhere interesting. My approach is a bit different from the official example because I used muscle groups and excluded certain groups from activation:
Code: Select all
# Apply Non-negative Matrix Factorization (NNMF) to the aggregated control data to find synergies.
nmf = NMF(n_components=numSynergies, init='random', random_state=0)
W = nmf.fit_transform(A) # Synergy excitations over time
H = nmf.components_ # Synergy vectors that combine muscle controls
# Scale the synergies for normalisation.
scaleVec = 0.5 * np.ones(H.shape[1])
for i in range(numSynergies):
scale_r = np.linalg.norm(scaleVec) / np.linalg.norm(H[i, :])
H[i, :] *= scale_r
W[:, i] /= scale_r
# Create a SynergyController to control the muscles based on synergies.
right_controller = osim.SynergyController()
right_controller.setName("synergy_controller_right_leg")
# Link each muscle in the right leg to the SynergyController.
for name in right_control_names:
right_controller.addActuator(osim.Muscle.safeDownCast(model.getComponent(name)))
# Expand synergy vectors to match the number of actuators
for i in range(numSynergies):
synergy_vector = osim.Vector(len(right_control_names), 0.0)
idx = 0
for group_name, muscle_list in muscle_groups.items():
if group_name in exclude_groups:
# Zero out the synergy vector for excluded groups
continue
for muscle in muscle_list:
if muscle in right_control_names:
synergy_vector.set(right_control_names.index(muscle), H[i, idx])
idx += 1
right_controller.addSynergyVector(synergy_vector)
Since it's not easy to share code in full here, I've explained the process in greater detail here:
https://anacsousa.notion.site/Moco-inve ... 6ba?pvs=74
I hope this gives you a better idea of how I implemented these if others need it.
Anyway, what I don't understand is that after updating to OpenSim 4.5.1, I've started getting the following error, even in examples that don't use controllers:
Code: Select all
[error] Model unable to assemble: SimTK Exception thrown at Optimizer.h:133:
Value out of range in OptimizerSystem Constructor: expected 1 <= number of parameters <= 2.14748e+09 but number of parameters=0..Model relaxing constraints and trying again.
While this error doesn't seem to affect the optimisation process (at least from what I can tell), I'm curious why it's happening.
Thanks again for your help!