Open nathantpickle opened 2 years ago
Hi @nathantpickle, we provide the initialize()
method to return the internal MocoStudy
that is pre-configured by MocoTrack
. Is there a reason that this wouldn't be sufficient for your purposes?
Hi @nickbianco - I have a model that contains a ClutchedPathSpring
, which I would like to use with MocoTrack
. However, when I call MocoTrack::initialize()
it complains that
No info available for state '/forceset/clutched_spring/stretch'
Because initialize()
creates the MocoProblem
, I can't call MocoProblem::setStateInfo()
for the ClutchedPathSpring to provide the required state info.
My thought was to just replicate the setup that occurs inside MocoTrack::initialize()
so that I can set the state info right after creating the MocoProblem
. However, the state and marker processing that occurs inside MocoTrack
would be fairly complicated to replicate (I'm working in Python), and there doesn't seem to be another (easy) way to perform the operations that occur inside configureStateTracking()
and configureMarkerTracking()
.
Maybe there's another way around the issue I'm having?
Ah, I see. Just checking, when you call initialize()
it completely fails? Because if you get the MocoStudy
back you could call problem = study.updProblem()
and then set the state info, but I'm not sure if that would work off the top of my head.
If that doesn't work, I don't really have a better alternative at the moment. This is definitely a limitation of the current MocoTrack
interface, one I would like to improve. Exposing configureStateTracking()
and configureMarkerTracking()
could be a nice intermediate option, but I'd have to think about if that would make sense.
In the meantime, it shouldn't be too hard to convert configureStateTracking
to python. It's maybe a bit cumbersome, but all the OpenSim API stuff would stay the same (except for adding the osim
prefix or however you've imported the opensim
module) and then convert all the control logic and variable definitions to Python. And the stuff past adding the goal to the problem can be ignored.
Correct, it fails to return from initialize()
, so I'm a bit stuck.
Thanks for the tip on converting to Python. I'll go that route as a workaround for now.
No problem @nathantpickle. Let me know if you have any questions about getting your workaround going. I'll leave this issue open until we implement a proper fix.
Just to follow up, I was able to replicate the functionality I need in Python without too much trouble.
Also, I haven't tested this code, but here is roughly what I would expect a utility function to look like. This is the section of code I ended up replicating in Python:
OpenSim::MocoWeightSet updateSpeedsAndWeights(
TimeSeriesTable& states,
const MocoWeightSet& userWeights,
const Model& model,
const bool trackStateDerivatives
)
{
auto stateSplines = GCVSplineSet(states, states.getColumnLabels());
// Loop through all coordinates and compare labels in the reference data
// to coordinate variable names.
auto time = states.getIndependentColumn();
auto labels = states.getColumnLabels();
int numRefStates = (int)states.getNumColumns();
MocoWeightSet weights;
for (const auto& coord : model.getComponentList<Coordinate>()) {
std::string coordPath = coord.getAbsolutePathString();
std::string valueName = coordPath + "/value";
std::string speedName = coordPath + "/speed";
bool trackingValue = false;
bool trackingSpeed = false;
int valueIdx = -1;
for (int i = 0; i < numRefStates; ++i) {
if (labels[i] == valueName) {
trackingValue = true;
valueIdx = i;
} else if (labels[i] == speedName) {
trackingSpeed = true;
}
}
// If a coordinate value was provided to track in the reference data,
// but no corresponding speed, append the derivative of the coordinate
// value to the tracking reference.
if (trackingValue && !trackingSpeed &&
trackStateDerivatives) {
auto value = states.getDependentColumnAtIndex(valueIdx);
auto* valueSpline = stateSplines.getGCVSpline(valueIdx);
SimTK::Vector speed((int)time.size());
for (int j = 0; j < (int)time.size(); ++j) {
speed[j] = valueSpline->calcDerivative(
{0}, SimTK::Vector(1, time[j]));
}
states.appendColumn(speedName, speed);
trackingSpeed = true;
}
// Unless the user already specified weights, don't track state
// variables that are already constrained.
bool isConstrained = coord.isConstrained(model.getWorkingState());
// Value weights.
if (userWeights.contains(valueName)) {
auto uw = userWeights.get(valueName);
weights.cloneAndAppend({valueName, uw.getWeight()});
} else if (isConstrained) {
weights.cloneAndAppend({valueName, 0});
}
// Speed weights.
if (trackingSpeed) {
if (userWeights.contains(speedName)) {
auto uw = userWeights.get(speedName);
weights.cloneAndAppend({speedName, uw.getWeight()});
} else if (isConstrained) {
weights.cloneAndAppend({speedName, 0});
}
}
}
return weights;
}
If updateSpeedsAndWeights()
was publicly available somewhere, users could just call it to add the position derivatives and update weights accordingly. In that case, configureStateTracking()
could be modified to:
TimeSeriesTable MocoTrack::configureStateTracking(
MocoProblem& problem, Model& model) {
// Read in the states reference data and spline.
// TODO convert Degrees to Radians.
TimeSeriesTable states = get_states_reference().processAndConvertToRadians(
getDocumentDirectory(), model);
MocoWeightSet weights = updateSpeedsAndWeights(states,
get_states_weight_set(), model, get_track_reference_position_derivatives());
...
Something similar could be done for configureMarkerTracking()
, though after looking at it more closely it really isn't that complicated.
These are kind of nitpicky changes, but would save people a bit of time if they want to set up state tracking (including derivatives) with a generic MocoStudy
.
It would be useful to be able to replicate a MocoTrack optimization using a generic MocoStudy. Pretty much everything in MocoTrack can be easily replicated, except for these two methods, which contain some fairly complicated processing of state and marker trajectories.
Could the functionality in these methods be made available somewhere else, for example in MocoUtilities, so it can be used with a generic MocoStudy?