FunctionBasedPath
- Pasha van Bijlert
- Posts: 226
- Joined: Sun May 10, 2020 3:15 am
FunctionBasedPath
Hi All,
The newest version of OpenSim (v4.5) apparently added polynomial muscle paths (as FunctionBasedPath). Congratulations, and thank you for developing this! I didn't know this was in the works, but I'm eager to try it out once I have the time to do some proper experimenting (but I have to first finish running some extra simulations for a project that's nearly finished, that didn't use FunctionBasedPath in the models). Has anybody tried A/B ing it yet with their models?
Cheers,
Pasha
The newest version of OpenSim (v4.5) apparently added polynomial muscle paths (as FunctionBasedPath). Congratulations, and thank you for developing this! I didn't know this was in the works, but I'm eager to try it out once I have the time to do some proper experimenting (but I have to first finish running some extra simulations for a project that's nearly finished, that didn't use FunctionBasedPath in the models). Has anybody tried A/B ing it yet with their models?
Cheers,
Pasha
Re: FunctionBasedPath
Hey Pasha,
There were some people tinkering with this at the OpenSim workshop earlier this week. Can't recall anyone exactly stating what sort of speed increases they were seeing, but there was plenty of talk about it - so I suspect someone might be able to respond to you soon.
Aaron
There were some people tinkering with this at the OpenSim workshop earlier this week. Can't recall anyone exactly stating what sort of speed increases they were seeing, but there was plenty of talk about it - so I suspect someone might be able to respond to you soon.
Aaron
- Nicholas Bianco
- Posts: 1041
- Joined: Thu Oct 04, 2012 8:09 pm
Re: FunctionBasedPath
Hi Pasha,
I updated exampleMocoTrack to use FunctionBasedPaths and saw a pretty significant speed-up, about 5x. While making that change, I noticed that the PolynomialPathFitter struggled to fit well to the gastroc and glut max paths (documented in the fitting example). These fitting errors were due to discontinutities in these paths due to how the current wrapping algorithm works (in the gastroc case, the path sliding and going in and out of contact with a wrap surface) and an issue with the glut max wrap surface configuration (which is really just a flaw in the wrapping algorithm itself.
Fixing these discontinuities (I think) is the main reason for the speed-up, in addition to making all paths smooth and differentiable and removing the wrap surface calculations. I believe this because the number of iterations in the example went down from ~250 to ~40.
When A/B'ing paths in other models I think the speed-ups will mainly be a function of 1) the number of wrap surfaces in your model, 2) the number of muscles in your model, and 3) the quality of the muscle wrapping in the original model. I think that using FunctionBasedPaths and PolynomialPathFitter will help reveal issues with existing wrapping surfaces in your model and let you replace them with functions that are friendly to gradient-based optimization. If PolynomialPathFitter is not able to give you a good fit to your original paths, it is likely that those paths were not in a good state for use in Moco.
There are some improvements to these tools that could lead to bigger speed-ups. Specifically, using step-wise regression to eliminate coefficients that are unnecessary for fitting the muscle paths. I'm hoping to test and incorporate these improvements in upcoming OpenSim/Moco releases.
Best,
Nick
I updated exampleMocoTrack to use FunctionBasedPaths and saw a pretty significant speed-up, about 5x. While making that change, I noticed that the PolynomialPathFitter struggled to fit well to the gastroc and glut max paths (documented in the fitting example). These fitting errors were due to discontinutities in these paths due to how the current wrapping algorithm works (in the gastroc case, the path sliding and going in and out of contact with a wrap surface) and an issue with the glut max wrap surface configuration (which is really just a flaw in the wrapping algorithm itself.
Fixing these discontinuities (I think) is the main reason for the speed-up, in addition to making all paths smooth and differentiable and removing the wrap surface calculations. I believe this because the number of iterations in the example went down from ~250 to ~40.
When A/B'ing paths in other models I think the speed-ups will mainly be a function of 1) the number of wrap surfaces in your model, 2) the number of muscles in your model, and 3) the quality of the muscle wrapping in the original model. I think that using FunctionBasedPaths and PolynomialPathFitter will help reveal issues with existing wrapping surfaces in your model and let you replace them with functions that are friendly to gradient-based optimization. If PolynomialPathFitter is not able to give you a good fit to your original paths, it is likely that those paths were not in a good state for use in Moco.
There are some improvements to these tools that could lead to bigger speed-ups. Specifically, using step-wise regression to eliminate coefficients that are unnecessary for fitting the muscle paths. I'm hoping to test and incorporate these improvements in upcoming OpenSim/Moco releases.
Best,
Nick
- John Davis
- Posts: 59
- Joined: Mon Aug 26, 2019 7:42 am
Re: FunctionBasedPath
Hi Nick, thanks for the great new feature! One quick thought/question - do you think the FunctionBasedPath class could also be used to "port in" muscle lengths and moment arms that are calculated elsewhere? From the docs it sounds like it should work as long as you can create a Function object for all the necessary outputs.
I'm thinking of things along the lines of Silvia Blemker's 3D finite element models, or some of Luca Modenese's recent work with highly discretized muscle models. Or even manually-measured Achilles tendon moment arms (via ultrasound). The idea being you pre-calculate your muscle fiber and moment arm lengths from a time-intensive method (like FEM or manual measurement), then approximate them with polynomials or B-splines or some other differentiable function, then use them in your Moco model.
Would this kind of approach work with the FunctionBasedPath API as-is? Or would it require some hacking around in C++? Seems like this could be a really promising direction for muscles that are tough to model with standard geometric paths, like the sartorius or the various glute muscles.
I'm thinking of things along the lines of Silvia Blemker's 3D finite element models, or some of Luca Modenese's recent work with highly discretized muscle models. Or even manually-measured Achilles tendon moment arms (via ultrasound). The idea being you pre-calculate your muscle fiber and moment arm lengths from a time-intensive method (like FEM or manual measurement), then approximate them with polynomials or B-splines or some other differentiable function, then use them in your Moco model.
Would this kind of approach work with the FunctionBasedPath API as-is? Or would it require some hacking around in C++? Seems like this could be a really promising direction for muscles that are tough to model with standard geometric paths, like the sartorius or the various glute muscles.
- Ross Miller
- Posts: 375
- Joined: Tue Sep 22, 2009 2:02 pm
Re: FunctionBasedPath
5x wow, I'm surprised it makes that much of a difference. I will have to try this.
Nick, is there a way of assessing/reporting the goodness-of-fit of the polys? Apologies if this is obvious, nothing jumped out at me in the documentation.
My struggle when exclusively using polynomial functions for muscle lengths and moment arms in the Before Times (pre-Moco) was that there is no straightforward way of scaling them like you can do with the path-based definitions. You can do hand-wavy things like scaling the coefficients that represent the neutral lengths by height or whatever, but I found that unsatisfactory.
Ross
Nick, is there a way of assessing/reporting the goodness-of-fit of the polys? Apologies if this is obvious, nothing jumped out at me in the documentation.
My struggle when exclusively using polynomial functions for muscle lengths and moment arms in the Before Times (pre-Moco) was that there is no straightforward way of scaling them like you can do with the path-based definitions. You can do hand-wavy things like scaling the coefficients that represent the neutral lengths by height or whatever, but I found that unsatisfactory.
Ross
- Nicholas Bianco
- Posts: 1041
- Joined: Thu Oct 04, 2012 8:09 pm
Re: FunctionBasedPath
John -- yes, that is definitely possible! As long as you using OpenSim Functions to define the lengths, lengthening speeds, and moment arms functions. OpenSim currently does not have a B-spline Function class, but that would be nice to add at some point. You could design polynomial functions directly using the MultivariatePolynomialFunction class (which PolynomialPathFitter uses), or, if you can get away with simpler, 1-dof functions, any of the other OpenSim Function classes.
Ross -- when using PolynomialPathFitter, it will log the progress of all steps of the fitting process to the console (or the log file, if it is not appearing the the console), including the goodness-of-fit values for the lengths and moment arms. There is also a helper function called 'evaluateFunctionBasedPaths' which you can used to re-run the goodness-of-fit between the original model and model with FunctionBasedPaths for a given motion file. This may be useful for checking if your model extrapolates well beyond the reference motion used during the fitting process.
By the way, the default tolerances and sampling settings might not be optimal depending on your use case. For example, you might loosen the tolerances a bit to produce polynomials with lower order, which will have fewer (possibly many fewer) coefficients, which will be faster to evaluate in simulation, etc. You also might decide to upload your own coordinates file that samples your model's coordinate space in a specific (rather than sampling around, e.g., the trajectory of a walking motion) if you're concerned about extrapolation beyond a reference trajectory.
Looking forward to getting everyone's feedback on this tool so I can target some key improvements for the next OpenSim release.
-Nick
Ross -- when using PolynomialPathFitter, it will log the progress of all steps of the fitting process to the console (or the log file, if it is not appearing the the console), including the goodness-of-fit values for the lengths and moment arms. There is also a helper function called 'evaluateFunctionBasedPaths' which you can used to re-run the goodness-of-fit between the original model and model with FunctionBasedPaths for a given motion file. This may be useful for checking if your model extrapolates well beyond the reference motion used during the fitting process.
By the way, the default tolerances and sampling settings might not be optimal depending on your use case. For example, you might loosen the tolerances a bit to produce polynomials with lower order, which will have fewer (possibly many fewer) coefficients, which will be faster to evaluate in simulation, etc. You also might decide to upload your own coordinates file that samples your model's coordinate space in a specific (rather than sampling around, e.g., the trajectory of a walking motion) if you're concerned about extrapolation beyond a reference trajectory.
Looking forward to getting everyone's feedback on this tool so I can target some key improvements for the next OpenSim release.
-Nick
Re: FunctionBasedPath
Hi Nick,
A couple of points to add for discussion on this new feature. I did a comparative test between a model that didn't have wrapping surfaces but modelled muscle paths via path points using this method vs. the polynomials. I'll admit that I didn't tune the polynomials with much effort and there were still some discontinuities in the original muscle moment arms using the path point approach, and I also don't know if this model was well suited to the sprinting motion I tested it on. Nonetheless, I still think there were some interesting points from this experiment:
1) The model with polynomial function path mapping solved in less iterations than the original moodel (321 iterations vs. 496 iterations), but was slower on a whole and therefore per iteration basis (1 hour 27 minutes vs. 1 hour 6 minutes). Would you think this is expected behaviour (i.e. a slow down per iteration when using the polynomials?).
2) As usual I found a way to produce 'nans' when using a model with locked coordinates when doing the polynomial fitting. The actual PolynomialPathFitter seems robust to locked coordinates, perhaps because the coordinates I used set these as zeros, but the evaluateFunctionBasedPaths method seems to struggle when coordinates in the model are locked and produces nans for those coordinate moment arms - resulting in a nan value for the overall error value.
Aaron
A couple of points to add for discussion on this new feature. I did a comparative test between a model that didn't have wrapping surfaces but modelled muscle paths via path points using this method vs. the polynomials. I'll admit that I didn't tune the polynomials with much effort and there were still some discontinuities in the original muscle moment arms using the path point approach, and I also don't know if this model was well suited to the sprinting motion I tested it on. Nonetheless, I still think there were some interesting points from this experiment:
1) The model with polynomial function path mapping solved in less iterations than the original moodel (321 iterations vs. 496 iterations), but was slower on a whole and therefore per iteration basis (1 hour 27 minutes vs. 1 hour 6 minutes). Would you think this is expected behaviour (i.e. a slow down per iteration when using the polynomials?).
2) As usual I found a way to produce 'nans' when using a model with locked coordinates when doing the polynomial fitting. The actual PolynomialPathFitter seems robust to locked coordinates, perhaps because the coordinates I used set these as zeros, but the evaluateFunctionBasedPaths method seems to struggle when coordinates in the model are locked and produces nans for those coordinate moment arms - resulting in a nan value for the overall error value.
Aaron
- Nicholas Bianco
- Posts: 1041
- Joined: Thu Oct 04, 2012 8:09 pm
Re: FunctionBasedPath
Hi Aaron,
Thanks for the feedback!
1) When I tested it some of the Moco walking examples, I also noticed (in some cases) an increase in iteration-to-iteration time. But an increase in iteration time doesn't necessarily suggest an increase in muscle function evaluation time (Antoine suggested Ipopt might be spending more time during the line search to take bigger step sizes). You could try posing the model in random poses and compare the cost to evaluate muscle length with and without the function-based paths to get a better comparison of the "raw" differences. (I'd be curious to know the differences and how many wrap surfaces your model has.)
I tried to set reasonable defaults for the fitter, but you may want to experiment with wider sampling ranges around the reference data and more sample points; these choices will have a significant impact the quality of fit and performance during optimization. You could also try reducing the default fitting tolerance (which is probably too strict to begin with) to try and reduce the number of polynomial coefficents needed for a good fit to your data, which could decrease the iteration-to-iteration time (and maybe help reduce overfitting as a by-product).
I'd like to make some improvements to PolynomialPathFitter to automatically remove redundant polynomial coefficients, sample more efficiently, and improve the extrapolation ability of each function.
2) Ah darn, I replace locked coordinates with WeldJoints in the main workflow, but not in 'evaluateFunctionBasedPaths'. Try using ModelOperator with ModOpReplaceJointsWithWelds for your joints with locked coordinates for now until I make a fix for this.
-Nick
Thanks for the feedback!
1) When I tested it some of the Moco walking examples, I also noticed (in some cases) an increase in iteration-to-iteration time. But an increase in iteration time doesn't necessarily suggest an increase in muscle function evaluation time (Antoine suggested Ipopt might be spending more time during the line search to take bigger step sizes). You could try posing the model in random poses and compare the cost to evaluate muscle length with and without the function-based paths to get a better comparison of the "raw" differences. (I'd be curious to know the differences and how many wrap surfaces your model has.)
I tried to set reasonable defaults for the fitter, but you may want to experiment with wider sampling ranges around the reference data and more sample points; these choices will have a significant impact the quality of fit and performance during optimization. You could also try reducing the default fitting tolerance (which is probably too strict to begin with) to try and reduce the number of polynomial coefficents needed for a good fit to your data, which could decrease the iteration-to-iteration time (and maybe help reduce overfitting as a by-product).
I'd like to make some improvements to PolynomialPathFitter to automatically remove redundant polynomial coefficients, sample more efficiently, and improve the extrapolation ability of each function.
2) Ah darn, I replace locked coordinates with WeldJoints in the main workflow, but not in 'evaluateFunctionBasedPaths'. Try using ModelOperator with ModOpReplaceJointsWithWelds for your joints with locked coordinates for now until I make a fix for this.
-Nick
Re: FunctionBasedPath
Hi Nick,
Perhaps one other bug to check (or for another user to see if they can replicate my issue). I was using a model with Thelen type muscles, which I converted to DeGrooteFregly using the model operator, ran the path fitting, applied the path functions - everything was fine. In a separate pipeline I ran the path fitting on the original Thelen muscles, applied the path functions, and then later tried to convert to DeGrooteFregly using the model operator - crashed my Python kernel. Taking out the model operator from later and converting before the path functions had been applied fixed this issue - so there may be something happening when converting the muscles with path functions included that causes a problem.
Aaron
Perhaps one other bug to check (or for another user to see if they can replicate my issue). I was using a model with Thelen type muscles, which I converted to DeGrooteFregly using the model operator, ran the path fitting, applied the path functions - everything was fine. In a separate pipeline I ran the path fitting on the original Thelen muscles, applied the path functions, and then later tried to convert to DeGrooteFregly using the model operator - crashed my Python kernel. Taking out the model operator from later and converting before the path functions had been applied fixed this issue - so there may be something happening when converting the muscles with path functions included that causes a problem.
Aaron
Re: FunctionBasedPath
Continually adding to this thread to keep everything in the one place - I think any muscle-related model operator seems to have issues when the muscles have the function based path. I ran into the same kernel crashing issue when trying to use the delete muscles operator on muscles with function based paths included, yet don't have this issue when they are not. It seems that an appropriate process right now is to add the function based paths in as the final step in using a model processor when considering other adjustments to muscles.
Aaron
Aaron