Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding SynergyController #3796

Merged
merged 29 commits into from
Jun 19, 2024
Merged

Adding SynergyController #3796

merged 29 commits into from
Jun 19, 2024

Conversation

nickbianco
Copy link
Member

@nickbianco nickbianco commented May 31, 2024

Fixes issue #3677

Brief summary of changes

Adds SynergyController, a controller that computes controls for a model based on a linear combination of a set of Input control signals and a set of synergy vectors. These changes also include a new utility function for performing basic non-negative matrix factorization, and a sub-example in exampleMocoInverse demostrating how to use SynergyController with Moco.

Other changes include:

  • A new CommonUtilities function for performing basic non-negative factorization, to support the new example.
  • An update to report.py and osimMocoTrajectoryReport.m for plotting Input controls.
  • A bug fix in MocoTrajectory where the getValuesTrajectory, getSpeedsTrajectory, etc., functions were returning the incorrect matrices if the trajectory was not organized a specific way.
  • Updated the bindings to support MocoTrajectory methods for Input controls (e.g., getInputControl()).

Testing I've completed

testMocoControllers.cpp was refactored to use SynergyController.cpp in favor of the custom TriplePendulumController class. Added unit tests specific SynergyController.

Looking for feedback on...

CHANGELOG.md (choose one)

  • updated.

[perf-win]

Performance analysis

Platform: Windows, self-hosted runner

Test Name lhs [secs] stderr [secs] rhs [secs] stderr [secs] Speedup
Arm26 0.37 0.00 0.36 0.00 1.03
ellipsoid_wrap 4.36 0.01 4.34 0.00 1.00
ellipsoid_wrap_function_based_paths 3.61 0.01 3.63 0.01 0.99
Gait2354 0.44 0.00 0.43 0.00 1.02
MocoSlidingMass 1.60 0.01 1.63 0.02 0.98
MocoSquatToStand 14.94 0.04 14.94 0.07 1.00
passive_dynamic 5.88 0.01 5.85 0.02 1.00
passive_dynamic_noanalysis 3.63 0.06 3.54 0.02 1.03
RajagopalModel 9.53 0.10 9.70 0.13 0.98
ToyDropLanding 14.53 0.14 14.52 0.06 1.00
ToyDropLanding_fbp_stepwisereg 13.03 0.06 12.93 0.06 1.01
ToyDropLanding_function_based_paths 13.28 0.04 13.42 0.09 0.99
ToyDropLanding_nomuscles 0.60 0.00 0.63 0.00 0.95

This change is Reviewable

@nickbianco nickbianco marked this pull request as ready for review June 3, 2024 23:03
@nickbianco
Copy link
Member Author

@aymanhab or @carmichaelong, do either of you have bandwidth for a review?

@AlbertoCasasOrtiz, if you're interested in learning more about opensim-core, tagging along as a second reviewer here would be a good place to start!

@AlbertoCasasOrtiz AlbertoCasasOrtiz self-requested a review June 4, 2024 17:51
@nickbianco
Copy link
Member Author

I've made a few updates: added Python and Matlab versions of the new example, and changes to the bindings to support these examples (and InputController-derived types in Moco in general).

@nickbianco nickbianco requested a review from carmichaelong June 11, 2024 21:58
@nickbianco
Copy link
Member Author

@carmichaelong @AlbertoCasasOrtiz I made a few small changes, but this is now ready for review.

Copy link
Member

@carmichaelong carmichaelong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @nickbianco, I left some comments throughout and had two higher level comments:

  1. It looks like users only interact with SynergyVector by passing in and using SimTK::Vector or SimTK:Matrix and don't use the names (though I may have missed a use case somewhere). What's the rationale for adding the SynergyVector class, since it looks like the controller deal with indexing for users?
  2. Did you consider adding the synergy examples as different files (even within the same example folders)?

Reviewed 12 of 15 files at r1, 7 of 10 files at r2, 4 of 5 files at r3, 3 of 3 files at r4, 1 of 1 files at r5.
Reviewable status: all files reviewed (commit messages unreviewed), 7 unresolved discussions (waiting on @AlbertoCasasOrtiz and @nickbianco)


Bindings/Java/Matlab/examples/Moco/example3DWalking/exampleMocoInverse.m line 275 at r4 (raw file):

[Wr, Hr] = nnmf(Ar, numSynergies);

% Scale W and H assuming that the elements of H are all 0.5.

could be helpful for users to give them a better sense of what the scaling is trying to do (on a high level), either in the comment block just above for SynergyController, or right here


Bindings/Java/Matlab/examples/Moco/example3DWalking/exampleMocoInverse.m line 275 at r4 (raw file):

[Wr, Hr] = nnmf(Ar, numSynergies);

% Scale W and H assuming that the elements of H are all 0.5.

how different is scaling after performing nnmf, rather than scaling within each loop of nnmf (as in the function in CommonUtilities.cpp?


Bindings/Python/examples/Moco/example3DWalking/exampleMocoInverse.py line 249 at r4 (raw file):

    Hr = nmf.components_

    # Scale W and H assuming that the elements of H are all 0.5.

same comment as in matlab example


OpenSim/Common/CommonUtilities.cpp line 255 at r4 (raw file):

        }
    }
    for (int i = 0; i < H0.nrow(); ++i) {

based on the paper for alternating least squares (and subsequent code), does H0 need to be initialized systematically randomly (or just be a matrix of the correct size with arbitrary values)?


OpenSim/Common/CommonUtilities.cpp line 298 at r4 (raw file):

        // Compute error using the Frobenius norm.
        error = A - W * H;
        normError = std::sqrt(error.scalarNormSqr()) / 

interesting way to normalize this error and makes sense. is this a typical way to do so? i didn't see it in the cited paper


OpenSim/Common/CommonUtilities.cpp line 318 at r4 (raw file):

        normError0 = normError;
        W0 = W;
        H0 = H;

actually, i think H0 could be removed all together?


OpenSim/Moco/MocoProblemRep.cpp line 49 at r5 (raw file):

    // Mute non-critical Logger output.
    Logger::Level origLoggerLevel = Logger::getLevel();
    Logger::setLevel(Logger::Level::Warn);

is there any reason a developer (or advanced user) may want to see these, or is it all actually clutter for common users?

Copy link
Member Author

@nickbianco nickbianco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @carmichaelong.

  1. The helper class SynergyVector is mainly to make the XML more readable. If I instead used made the synergy_vectors property a list of SimTK::Vector, in XML, all the synergy vector weights get concatenated into one long vector. Using the SynergyVector type as an intermediate class breaks up each synergy vector into a readable XML component.
  2. I did, but since the synergy example uses the solution from the first sub-example in exampleMocoInverse, I think it makes sense to keep them in the same file.

Reviewable status: 22 of 27 files reviewed, 7 unresolved discussions (waiting on @AlbertoCasasOrtiz and @carmichaelong)


Bindings/Java/Matlab/examples/Moco/example3DWalking/exampleMocoInverse.m line 275 at r4 (raw file):

Previously, carmichaelong wrote…

could be helpful for users to give them a better sense of what the scaling is trying to do (on a high level), either in the comment block just above for SynergyController, or right here

Updated the comment.


Bindings/Java/Matlab/examples/Moco/example3DWalking/exampleMocoInverse.m line 275 at r4 (raw file):

Previously, carmichaelong wrote…

how different is scaling after performing nnmf, rather than scaling within each loop of nnmf (as in the function in CommonUtilities.cpp?

I don't think it really matters much. All three examples will give slightly different synergy vectors and weights, but I think that's fine. These aren't supposed to be rigorous examples for extracting synergies, just a demo of the new controller.


Bindings/Python/examples/Moco/example3DWalking/exampleMocoInverse.py line 249 at r4 (raw file):

Previously, carmichaelong wrote…

same comment as in matlab example

Done.


OpenSim/Common/CommonUtilities.cpp line 255 at r4 (raw file):

Previously, carmichaelong wrote…

based on the paper for alternating least squares (and subsequent code), does H0 need to be initialized systematically randomly (or just be a matrix of the correct size with arbitrary values)?

I've removed H0 based on your other comment.


OpenSim/Common/CommonUtilities.cpp line 298 at r4 (raw file):

Previously, carmichaelong wrote…

interesting way to normalize this error and makes sense. is this a typical way to do so? i didn't see it in the cited paper

I did this based on a recommendation from B.J. It's just a way to scale synergy weights and excitations into "reasonable" ranges.


OpenSim/Common/CommonUtilities.cpp line 318 at r4 (raw file):

Previously, carmichaelong wrote…

actually, i think H0 could be removed all together?

Ah yes, it is totally unused!


OpenSim/Moco/MocoProblemRep.cpp line 49 at r5 (raw file):

Previously, carmichaelong wrote…

is there any reason a developer (or advanced user) may want to see these, or is it all actually clutter for common users?

I've removed these changes. The InputController changes add some clutter because until the ControlDistributor is connected to the model's InputControllers, the controller will print a message saying that the controller will be ignored. I'd like to keep this message for the component in general, but I also don't want to confuse users in Moco. But I also don't necessarily want to ignore all print outs in MocoProblemRep either.

For now I think we keep them, I can patch in a better solution later, maybe after some user feedback.

@nickbianco nickbianco requested a review from carmichaelong June 17, 2024 16:08
Copy link
Member

@carmichaelong carmichaelong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the responses throughout sound good to me, thanks for all the explanations/clarifications!

Reviewed 6 of 6 files at r6, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @AlbertoCasasOrtiz)

@nickbianco
Copy link
Member Author

Thanks @carmichaelong!

@nickbianco
Copy link
Member Author

The failing tests on the Windows2022 are unrelated, so I'm merging.

Thanks @carmichaelong and @AlbertoCasasOrtiz!

@nickbianco nickbianco merged commit d33c213 into main Jun 19, 2024
9 of 12 checks passed
@nickbianco nickbianco deleted the synergy_controller branch June 19, 2024 00:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants