opensim-org / opensim-core

SimTK OpenSim C++ libraries and command-line applications, and Java/Python wrapping.
https://opensim.stanford.edu
Apache License 2.0
763 stars 309 forks source link

Unexpected behaviour of Model::setControls() #1351

Open mitkof6 opened 7 years ago

mitkof6 commented 7 years ago

With regards to a previous comment 644. The use of setControls is not working properly, thus if someone would like to assign controls to the model, should implement a Controller. On the other hand, there are cases when we want to apply the controls ourself.

The setControls caches the controls, and when they are applied to the model, it checks whether they are valid. Despite the fact that the setControls is called (and we can validate the controls) the controls are invalidated anyway, thus default values are assigned and a call to the computeControls is issued in getControls. In order to find a workaround one should assign default controls instead of calling the setControls.

The following code demonstrates the problem (I use the tag_of_war model).

void test(string modelPath, string resultDir) { Model model(modelPath);

Kinematics* kin = new Kinematics(&model);
model.addAnalysis(kin);

ForceReporter* forces = new ForceReporter(&model);
model.addAnalysis(forces);

model.buildSystem();
State& s = model.initializeState();

SimTK::RungeKuttaMersonIntegrator integrator(model.getMultibodySystem());
//manager
OpenSim::Manager manager(model, integrator);
double t0 = 0, dt = 0.1, tf = 1;
manager.setInitialTime(t0);

double t = 0;
while (t < tf)
{
    //*/ works
    Vector modelContorls(model.getNumControls(), 0.0);
    model.updMuscles()[0].addInControls(Vector(1, s.getTime() / 2), modelContorls);
    model.updMuscles()[1].addInControls(Vector(1, s.getTime() / 4), modelContorls);
    model.setDefaultControls(modelContorls);
    /**/

    /*/ does not work
    Vector modelContorls(model.getNumControls(), 0.0);
    model.updMuscles()[0].addInControls(Vector(1, s.getTime() / 2), modelContorls);
    model.updMuscles()[1].addInControls(Vector(1, s.getTime() / 4), modelContorls);
    model.realizeVelocity(s);
    model.setControls(s, modelContorls);
    model.markControlsAsValid(s);
    /**/

    manager.setInitialTime(t);
    manager.setFinalTime(t + dt);
    manager.integrate(s);
    t += dt;
}

kin->printResults("", resultDir);
forces->printResults("", resultDir);

OpenSim::Storage stateReporter(manager.getStateStorage());
model.updSimbodyEngine().convertRadiansToDegrees(stateReporter);
stateReporter.print(resultDir+ "State.sto");}`
chrisdembia commented 7 years ago

Thanks for reporting. I had not thought of this as an expected workflow, but it seems it would be nice if it worked.

mitkof6 commented 7 years ago

@chrisdembia what is the purpose of setControls? The other way as far as I know is to add a controller.

chrisdembia commented 7 years ago

@chrisdembia what is the purpose of setControls?

Others will have to respond. I had assumed that creating a Controller was the only proper way to generate controls. But the behavior you describe sounds like a bug.

aseth1 commented 7 years ago

@mitkof6 the use case you indicate is the intended use of Model::setControls() without the need for a Controller. The Dynamic Walking Challenge tutorial does this in the OpenSimPlantFunction.m which is used by the MATLAB integrator.

The use of model.setDefaultControls(modelContorls) should in fact be illegal since the default_controls are properties and property editing should invalidate the System but that enforcement mechanism is not currently in place. Realizing to velocity prior to setControls is the correct thing to do. It is also unnecessary to markControlsValid() but that should have no effect.

At the time of the above MATLAB example, Model::realizeVelocity(state) was not exposed, so it was necessary to call Model::computeStateVariableDerivatives(state) to ensure that the System was realized to at least velocity. Perhaps their were unexpected (favorable) side-effects from doing that and we can look into that.

The issue that I see with your code, is that setControls is not being called on every integration time step but only being set every dt. So the controls will be whatever the default value is for all remaining timesteps. You will need to add an EventHandler such that you can set the controls after every successful timestep or used fixed time stepping and set dt to the fixed-time step. Depending on how small the timsteps are the control spike at each dt could be very short-lived and thus have very subtle results on muscle forces. Can you also report the controls? or see any small changes in activation very dt interval to verify it is working this way?

mitkof6 commented 7 years ago

@aseth1 Yes the controls are set and hold for the interval and the activation is stepwise in my case with the appropriate delay. Nevertheless, if the dt is small enough then there will not be a problem.

I think that from the time of the Dynamic Walking Challenge something changed in the invalidation mechanism that causes the cached controls to be invalid during integration. Moreover, I believe the default values are hidden complexity, which the user is not aware.

MechAmini commented 3 years ago

Hi mitkof6 I would try to use addincontrol in Matlab code. I want to define control signal for one actuator. thus I write this code. but it isn't work. why?

     modelcontrols = walkerModel.getDefaultControls

     model.updActuators().get("knee_motor_r").addInControls(0,modelcontrols)

please guide me.