casadi / casadi

CasADi is a symbolic framework for numeric optimization implementing automatic differentiation in forward and reverse modes on sparse matrix-valued computational graphs. It supports self-contained C-code generation and interfaces state-of-the-art codes such as SUNDIALS, IPOPT etc. It can be used from C++, Python or Matlab/Octave.
http://casadi.org
GNU Lesser General Public License v3.0
1.73k stars 384 forks source link

Opti within Simulink Systems block loses initialization #2845

Open T-Buesing opened 3 years ago

T-Buesing commented 3 years ago

Hi,

I've been trying to set up an NLMPC within Matlab Simulink by using the interpreted Matlab Systems method outlined in https://web.casadi.org/blog/mpc-simulink/ just with the Optistack syntax. Because my problem features multiple phases, free end time and changing model parameters, keeping as much readability for setting up the solver(s) would be nice. I'd like to reduce my problem definition once each phase has finished. So my idea was to initialize multiple Opti solvers with successively reduced phase count and then call the currently appropriate solver within a state machine.

Initial setupImpl works fine, and the system object becomes a complete Opti object that can be solved by calling it. 01

But once it transitions into stepImpl the system object becomes empty and unusable. 02

I've tried different inputs in the properties section, but the results are the same. Is Opti not meant for use within Simulink, or am I missing a certain setting in either the block parameters or the definitions of the system?

Best regards!

T-Buesing commented 2 years ago

After trying out different ways of defining system properties to store the opti object, I can get stepImpl to access a constructed Opti solver via nested structs. Problem is, it still loses the names of variables/parameters defined during initialization.

If we take the racecar example, it is possible to initialize in setupImpl and within stepImpl call

opti=obj.optstruct.opti1
sol=opti.solve

then sol does indeed contain the calculated solution, but commands like sol.value(speed) or sol.value(T) result in

Unrecognized function or variable 'speed'.
Unrecognized function or variable 'T'.

Inspecting the solution with sol.value_variables shows them as expressions.

val = 

(1.90463==opti39_x_3)

But it's not accessible through sol.value(opti39_x_3) So far I've only managed to extract the numeric value from sol.value_variables{i,j} by converting the expression containing it into a string and then using str2num on a slice of it.

The same declaration loss applies to parameters, they only appear in opti.value_parameters as expression, e.g.

ans = 

(123==opti40_p_1)

They can't be addressed with opti.set_value(opti40_p_1 , 234) before running opti.solve , so setting parameters to new values for a step doesn't seem possible. Setting new parameters per step was talked about in https://github.com/casadi/casadi/issues/2439 but there is no mention about how the OP employed Opti inside Simulink

Zheng-JIA commented 2 years ago
  1. In the matlab system script, declare a property in "properties (Access=private)", for example, "casadi_solver"
  2. In setupImpl, create opti by writing "opti = casadi.Opti()"
  3. Again in setupImpl, define solver used by opti, for example, "opti.solver('ipopt', opti_options, ipopt_options)"
  4. Again in setupImpl, assign the opti to casadi_solver, "obj.casadi_solver = opti"
  5. Then in stepImpl, you can invoke the solver by writing "obj.casadi_solver.solve()"
  6. You can even set initial value by "obj.casadi_solver.set_initial(your_problems_optimization_variables, your_guess);
OIEIyoon commented 2 years ago
  1. In the matlab system script, declare a property in "properties (Access=private)", for example, "casadi_solver"
  2. In setupImpl, create opti by writing "opti = casadi.Opti()"
  3. Again in setupImpl, define solver used by opti, for example, "opti.solver('ipopt', opti_options, ipopt_options)"
  4. Again in setupImpl, assign the opti to casadi_solver, "obj.casadi_solver = opti"
  5. Then in stepImpl, you can invoke the solver by writing "obj.casadi_solver.solve()"
  6. You can even set initial value by "obj.casadi_solver.set_initial(your_problems_optimization_variables, your_guess);

I have a question for you Zheng-JIA ! I did exactly same process you mentioned above. However, once I set value of parameter my Simulink cannot find parameter that I already set in setupImpl.

For example, the parameter 'X0' is defined in setupImpl. Next, I invoke the solver in stepImpl as mentioned, and I set the value of X0 in stepImpl. But the error comes up "X0 is not a recognizable function or variable" (This may not be an exact translation because I'm not using English version. Sorry for that). Here's the brief code below. Can you help me please?

function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    X0    = opti.parameter(n_x, 1);
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(X0, x_pre(:,1));
    ....
    sol = obj.casadi_solver.solve();
    ....
end
Zheng-JIA commented 2 years ago
  1. In the matlab system script, declare a property in "properties (Access=private)", for example, "casadi_solver"
  2. In setupImpl, create opti by writing "opti = casadi.Opti()"
  3. Again in setupImpl, define solver used by opti, for example, "opti.solver('ipopt', opti_options, ipopt_options)"
  4. Again in setupImpl, assign the opti to casadi_solver, "obj.casadi_solver = opti"
  5. Then in stepImpl, you can invoke the solver by writing "obj.casadi_solver.solve()"
  6. You can even set initial value by "obj.casadi_solver.set_initial(your_problems_optimization_variables, your_guess);

I have a question for you Zheng-JIA ! I did exactly same process you mentioned above. However, once I set value of parameter my Simulink cannot find parameter that I already set in setupImpl.

For example, the parameter 'X0' is defined in setupImpl. Next, I invoke the solver in stepImpl as mentioned, and I set the value of X0 in stepImpl. But the error comes up "X0 is not a recognizable function or variable" (This may not be an exact translation because I'm not using English version. Sorry for that). Here's the brief code below. Can you help me please?

function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    X0    = opti.parameter(n_x, 1);
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(X0, x_pre(:,1));
    ....
    sol = obj.casadi_solver.solve();
    ....
end

Hi, First of all, obj refers to the object of your MATLAB system object. Similar to any object oriented programming, you need to set variable to its properties so the object's function/method knows there exist such variable in the object. In you problem, X0 is locally declared in setupImpl. You need to

  1. declare obj.X0 in the properties(Access = private)
    properties (Access = private)
        casadi_solver
        X0
    end
  2. write obj.X0 = opti.parameter(n_x, 1); in setupImpl
    function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    obj.X0    = opti.parameter(n_x, 1); % save the parameter varaible to object's properties
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
    end
  3. write obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); in setpImpl
    function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); % use obj.X0 that is the parameter saved in object's properties
    ....
    sol = obj.casadi_solver.solve();
    ....
    end

Try it out and see if it works. Zheng

OIEIyoon commented 2 years ago
  1. In the matlab system script, declare a property in "properties (Access=private)", for example, "casadi_solver"
  2. In setupImpl, create opti by writing "opti = casadi.Opti()"
  3. Again in setupImpl, define solver used by opti, for example, "opti.solver('ipopt', opti_options, ipopt_options)"
  4. Again in setupImpl, assign the opti to casadi_solver, "obj.casadi_solver = opti"
  5. Then in stepImpl, you can invoke the solver by writing "obj.casadi_solver.solve()"
  6. You can even set initial value by "obj.casadi_solver.set_initial(your_problems_optimization_variables, your_guess);

I have a question for you Zheng-JIA ! I did exactly same process you mentioned above. However, once I set value of parameter my Simulink cannot find parameter that I already set in setupImpl. For example, the parameter 'X0' is defined in setupImpl. Next, I invoke the solver in stepImpl as mentioned, and I set the value of X0 in stepImpl. But the error comes up "X0 is not a recognizable function or variable" (This may not be an exact translation because I'm not using English version. Sorry for that). Here's the brief code below. Can you help me please?

function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    X0    = opti.parameter(n_x, 1);
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(X0, x_pre(:,1));
    ....
    sol = obj.casadi_solver.solve();
    ....
end

Hi, First of all, obj refers to the object of your MATLAB system object. Similar to any object oriented programming, you need to set variable to its properties so the object's function/method knows there exist such variable in the object. In you problem, X0 is locally declared in setupImpl. You need to

  1. declare obj.X0 in the properties(Access = private)
properties (Access = private)
        casadi_solver
        X0
end
  1. write obj.X0 = opti.parameter(n_x, 1); in setupImpl
function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    obj.X0    = opti.parameter(n_x, 1); % save the parameter varaible to object's properties
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
  1. write obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); in setpImpl
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); % use obj.X0 that is the parameter saved in object's properties
    ....
    sol = obj.casadi_solver.solve();
    ....
end

Try it out and see if it works. Zheng

Thank you, Zheng It works! And I've figured out that not only parameters but also variables must be declared in the properties(Access = private). Appreciate it!

Zheng-JIA commented 2 years ago
  1. In the matlab system script, declare a property in "properties (Access=private)", for example, "casadi_solver"
  2. In setupImpl, create opti by writing "opti = casadi.Opti()"
  3. Again in setupImpl, define solver used by opti, for example, "opti.solver('ipopt', opti_options, ipopt_options)"
  4. Again in setupImpl, assign the opti to casadi_solver, "obj.casadi_solver = opti"
  5. Then in stepImpl, you can invoke the solver by writing "obj.casadi_solver.solve()"
  6. You can even set initial value by "obj.casadi_solver.set_initial(your_problems_optimization_variables, your_guess);

I have a question for you Zheng-JIA ! I did exactly same process you mentioned above. However, once I set value of parameter my Simulink cannot find parameter that I already set in setupImpl. For example, the parameter 'X0' is defined in setupImpl. Next, I invoke the solver in stepImpl as mentioned, and I set the value of X0 in stepImpl. But the error comes up "X0 is not a recognizable function or variable" (This may not be an exact translation because I'm not using English version. Sorry for that). Here's the brief code below. Can you help me please?

function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    X0    = opti.parameter(n_x, 1);
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(X0, x_pre(:,1));
    ....
    sol = obj.casadi_solver.solve();
    ....
end

Hi, First of all, obj refers to the object of your MATLAB system object. Similar to any object oriented programming, you need to set variable to its properties so the object's function/method knows there exist such variable in the object. In you problem, X0 is locally declared in setupImpl. You need to

  1. declare obj.X0 in the properties(Access = private)
properties (Access = private)
        casadi_solver
        X0
end
  1. write obj.X0 = opti.parameter(n_x, 1); in setupImpl
function setupImpl(obj,~,~)
    import casadi.*    
    ....
    opti = casadi.Opti();
    ....
    obj.X0    = opti.parameter(n_x, 1); % save the parameter varaible to object's properties
    ....
    opti.minimize(cost);
    p_opts = struct('expand',true);
    s_opts = struct('max_iter',100);
    opti.solver('ipopt',p_opts, s_opts)
    obj.casadi_solver = opti;
end
  1. write obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); in setpImpl
function [x_pred, u_pred, lat_caltime, MPC_fail] = stepImpl(obj, x_pre, u_pre, x_ref, curv1, curv2, VX1)
    ....
    obj.casadi_solver.set_value(obj.X0, x_pre(:,1)); % use obj.X0 that is the parameter saved in object's properties
    ....
    sol = obj.casadi_solver.solve();
    ....
end

Try it out and see if it works. Zheng

Thank you, Zheng It works! And I've figured out that not only parameters but also variables must be declared in the properties(Access = private). Appreciate it!

Great! I also observed that stepImpl will be called more than once in simulink. Currently I have no clue to force this function to be called only once at every simulation step. If you later know how to do this, let me know.