@@ -2,7 +2,9 @@ using Revise
22using JLD2
33using SparseArrays
44using MathOptInterface. Nonlinear. SymbolicAD
5+ import MathOptInterface as MOI
56using JuMP, Ipopt, HSL_jll
7+ using LinearAlgebra
68
79# include(joinpath(@__DIR__, "../mpc_utils.jl"))
810include (joinpath (@__DIR__ , " atlas_utils.jl" ))
@@ -138,7 +140,8 @@ function atlas_dynamics(xu::T...) where {T<:Real}
138140 h = 0.01
139141 x = collect (xu[1 : atlas. nx])
140142 u = collect (xu[atlas. nx+ 1 : end ])
141- return rk4 (atlas, x, u, h)
143+ # return rk4(atlas, x, u, h)
144+ return explicit_euler (atlas, x, u, h)
142145end
143146
144147# Lets define a jump model that solves the MPC problem
149152function build_and_solve_mpc (atlas_obj:: Atlas , x_ref:: Vector{Float64} , X_start:: Vector{Float64} , N:: Int ; optimizer= optimizer_with_attributes (Ipopt. Optimizer,
150153 # "print_level" => 0,
151154 " hsllib" => HSL_jll. libhsl_path,
152- " linear_solver" => " MA27"
155+ " linear_solver" => " MA27" ,
156+ " max_iter" => 20 ,
153157 )
154158)
155159 model = Model ()
@@ -167,23 +171,66 @@ function build_and_solve_mpc(atlas_obj::Atlas, x_ref::Vector{Float64}, X_start::
167171
168172 # Build the memoized function array for the multi-output dynamics
169173 # so we can call the i-th dimension of next state individually
170- dyn_ops = memoize_dynamics_and_jacobian (atlas_dynamics,
171- atlas_obj. nx + atlas_obj. nu,
172- atlas_obj. nx)
174+ # dyn_ops = memoize_dynamics_and_jacobian(atlas_dynamics,
175+ # atlas_obj.nx + atlas_obj.nu,
176+ # atlas_obj.nx)
177+
178+ VNO_dim = 2 * atlas_obj. nx + atlas_obj. nu
179+ # The input variable to this function:
180+ # [ x[t+1,:], x[t,:], u[t,:] ]
181+ jacobian_structure = Tuple{Int,Int}[]
182+ append! (jacobian_structure, map (i -> (i,i), 1 : atlas_obj. nx))
183+ for i in 1 : atlas_obj. nx, j in 1 : (atlas_obj. nx + atlas_obj. nu)
184+ push! (jacobian_structure, (i, j + atlas_obj. nx))
185+ end
186+ hessian_lagrangian_structure = [
187+ (i, j)
188+ for i in atlas_obj. nx+ 1 : VNO_dim
189+ for j in atlas_obj. nx+ 1 : VNO_dim
190+ ]
191+ VNO = MOI. VectorNonlinearOracle (;
192+ dimension = VNO_dim,
193+ l = zeros (atlas_obj. nx),
194+ u = zeros (atlas_obj. nx),
195+ eval_f = (ret, x) -> begin
196+ ret[1 : atlas_obj. nx] .= x[1 : atlas_obj. nx] - atlas_dynamics (x[atlas_obj. nx + 1 : VNO_dim]. .. )
197+ return
198+ end ,
199+ jacobian_structure,
200+ eval_jacobian = (ret, x) -> begin
201+ dyn_jac = ForwardDiff. jacobian (x -> atlas_dynamics (x... ), x[atlas_obj. nx + 1 : VNO_dim])
202+ jnnz = length (jacobian_structure)
203+ ret[1 : atlas_obj. nx] .= ones (atlas_obj. nx)
204+ nx = atlas_obj. nx; nu = atlas_obj. nu
205+ ret[atlas_obj. nx+ 1 : jnnz] .= - reshape (dyn_jac' , nx * (nx + nu))
206+ return
207+ end ,
208+ hessian_lagrangian_structure,
209+ eval_hessian_lagrangian = (ret, x, λ) -> begin
210+ hess = ForwardDiff. hessian (x -> dot (λ, atlas_dynamics (x... )), x[atlas_obj. nx + 1 : VNO_dim])
211+ hnnz = length (hessian_lagrangian_structure)
212+ ret[1 : hnnz] .= - reshape (hess, hnnz)
213+ return
214+ end
215+ )
216+ for t in 1 : (N- 1 )
217+ vars = vcat (x[t+ 1 ,:], x[t, :], u[t, :])
218+ JuMP. @constraint (model, vars in VNO)
219+ end
173220
174221 # Add constraints: x[t+1] = f(x[t], u[t]) for t=1 to N-1
175- for t in 1 : (N- 1 ), i in 1 : atlas_obj. nx
176- scalar_fun, grad_fun! = dyn_ops[i]
177- op = add_nonlinear_operator (
178- model,
179- atlas_obj. nx + atlas_obj. nu, # number of inputs
180- scalar_fun, # f_i
181- grad_fun!, # gradient callback
182- name = Symbol (" dyn_$(t) _$(i) " )
183- )
184- # Bind the operator to the constraint x[t+1, i] == f_i( [x[t,:]; u[t,:]]... )
185- @constraint (model, x[t+ 1 ,i] == op ( [x[t, :]; u[t, :]]. .. ))
186- end
222+ # for t in 1:(N-1), i in 1:atlas_obj.nx
223+ # scalar_fun, grad_fun! = dyn_ops[i]
224+ # op = add_nonlinear_operator(
225+ # model,
226+ # atlas_obj.nx + atlas_obj.nu, # number of inputs
227+ # scalar_fun, # f_i
228+ # grad_fun!, # gradient callback
229+ # name = Symbol("dyn_$(t)_$(i)")
230+ # )
231+ # # Bind the operator to the constraint x[t+1, i] == f_i( [x[t,:]; u[t,:]]... )
232+ # @constraint(model, x[t+1,i] == op( [x[t, :]; u[t, :]]... ))
233+ # end
187234
188235 # Possibly set an initial condition constraint: x[1, :] = x_ref
189236 @constraint (model, [j in 1 : atlas_obj. nx], x[1 ,j] == X_start[j])
@@ -193,15 +240,16 @@ function build_and_solve_mpc(atlas_obj::Atlas, x_ref::Vector{Float64}, X_start::
193240 return model, value .(x), value .(u)
194241end
195242
196- N = 100
243+ N = 10
197244X_start = deepcopy (x_ref)
198245X_start[atlas. nq + 5 ] = 1.3
199246model, X_solve, U_solve = build_and_solve_mpc (atlas, x_ref, X_start, N; optimizer= optimizer_with_attributes (Ipopt. Optimizer,
200247 # "print_level" => 0,
201248 " linear_solver" => " ma97" ,
202- " hessian_approximation" => " limited-memory" ,
203- " max_iter" => 300 ,
249+ # "hessian_approximation" => "limited-memory",
250+ # "max_iter" => 20 ,
204251 " mu_target" => 1e-8 ,
252+ " print_user_options" => " yes" ,
205253 )
206254)
207255termination_status (model)
@@ -269,92 +317,95 @@ error = sum( norm(X_solve[t,:] .- X[t]) for t in 1:N )
269317 # global X[k + 1] = rk4(atlas, X[k], clamp.(u_ref + U[k], -atlas.torque_limits, atlas.torque_limits), h)
270318# end
271319
272- X = [value .(x[t,:]) for t= 1 : N]
273- animate! (atlas, mvis, X, Δt= h);
274- readline ()
275-
276-
277- # ##############
278- include (joinpath (@__DIR__ , " rigidbodyutils.jl" ))
279- include (joinpath (@__DIR__ , " atlas_utils.jl" ))
280-
281- model = Model ()
282- optimizer= optimizer_with_attributes (Ipopt. Optimizer,
283- # "print_level" => 0,
284- " hsllib" => HSL_jll. libhsl_path,
285- " linear_solver" => " MA27"
286- )
287- set_optimizer (model, optimizer)
288-
289- @variable (model, x[1 : N, 1 : atlas. nx])
290- @variable (model, - atlas. torque_limits[i] <= u[t= 1 : N- 1 ,i= 1 : atlas. nu] <= atlas. torque_limits[i])
291-
292- dyn_result = dynamics (atlas, convert .(NonlinearExpr, x[1 , :]), convert .(NonlinearExpr,u[1 , :]))
293-
294- function atlas_dynamics (x, u)
295- h = 0.01
296- return rk4 (atlas, x, u, h)
297- end
298-
299- function build_and_solve_mpc (atlas_obj:: Atlas , x_ref:: Vector{Float64} , X_start:: Vector{Float64} , N:: Int ; optimizer= optimizer_with_attributes (Ipopt. Optimizer,
300- # "print_level" => 0,
301- " hsllib" => HSL_jll. libhsl_path,
302- " linear_solver" => " MA27"
303- )
304- )
305- model = Model ()
306- set_optimizer (model, optimizer)
307-
308- # For demonstration, let's define:
309- # x[t=1:N, 1:atlas.nx]
310- # u[t=1:N-1, 1:atlas.nu]
311- @variable (model, x[1 : N, 1 : atlas_obj. nx])
312- @variable (model, - atlas_obj. torque_limits[i] <= u[t= 1 : N- 1 ,i= 1 : atlas. nu] <= atlas_obj. torque_limits[i])
313- # ^ adapt the indexing if torque_limits is an array of length nu, etc.
314-
315- # Objective: sum of squared error from x_ref
316- @objective (model, Min, sum ( (x[t,j] - x_ref[j])^ 2 for t in 2 : N for j in 1 : atlas_obj. nx ) )
317-
318- for t in 1 : (N- 1 )
319- # Bindconstraint x[t+1, i] == f_i( [x[t,:]; u[t,:]]... )
320- @constraint (model, x[t+ 1 , :] == atlas_dynamics ( convert .(NonlinearExpr, x[t, :]), convert .(NonlinearExpr,u[t, :])))
321- end
322-
323- # Possibly set an initial condition constraint: x[1, :] = x_ref
324- @constraint (model, [j in 1 : atlas_obj. nx], x[1 ,j] == X_start[j])
325-
326- # Solve
327- optimize! (model)
328- return model, value .(x), value .(u)
329- end
330-
331- N = 2
332- X_start = deepcopy (x_ref)
333- X_start[atlas. nq + 5 ] = 1.3
334- model, X_solve, U_solve = build_and_solve_mpc (atlas, x_ref, X_start, N; optimizer= optimizer_with_attributes (Ipopt. Optimizer,
335- # "print_level" => 0,
336- " linear_solver" => " ma97" ,
337- " hessian_approximation" => " limited-memory" ,
338- " max_iter" => 300 ,
339- " mu_target" => 1e-8 ,
340- )
341- )
342- termination_status (model)
343-
344- # Now we can simulate the system with the controls U_solve
345- X = [zeros (atlas. nx) for _ = 1 : N];
346- X[1 ] = deepcopy (x_ref);
347- X[1 ][atlas. nq + 5 ] = 1.3 ; # Perturb i.c.
348-
349- for k = 1 : N - 1
350- X[k + 1 ] = rk4 (atlas, X[k], U_solve[k, :], h)
351- end
352-
353- animate! (atlas, mvis, X, Δt= h);
354-
355- error = sum ( norm (X_solve[t,:] .- X[t]) for t in 1 : N )
356-
357-
358- function foo! (q:: AbstractVector{T} ) where {T}
359- q .= zero (T)
360- end
320+ # X = [value.(x[t,:]) for t=1:N]
321+ # X = [X_solve[t, :] for t=1:N]
322+ #
323+ # animate!(atlas, mvis, X, Δt=h);
324+ # readline()
325+ #
326+ #
327+ # ###############
328+ # include(joinpath(@__DIR__, "rigidbodyutils.jl"))
329+ # include(joinpath(@__DIR__, "atlas_utils.jl"))
330+ #
331+ # model = Model()
332+ # optimizer=optimizer_with_attributes(Ipopt.Optimizer,
333+ # # "print_level" => 0,
334+ # "hsllib" => HSL_jll.libhsl_path,
335+ # "linear_solver" => "MA27"
336+ # )
337+ # set_optimizer(model, optimizer)
338+ #
339+ # @variable(model, x[1:N, 1:atlas.nx])
340+ # @variable(model, -atlas.torque_limits[i] <= u[t=1:N-1,i=1:atlas.nu] <= atlas.torque_limits[i])
341+ #
342+ # dyn_result = dynamics(atlas, convert.(NonlinearExpr, x[1, :]), convert.(NonlinearExpr,u[1, :]))
343+ #
344+ # function atlas_dynamics(x, u)
345+ # h = 0.01
346+ # return rk4(atlas, x, u, h)
347+ # end
348+ #
349+ # #function build_and_solve_mpc(atlas_obj::Atlas, x_ref::Vector{Float64}, X_start::Vector{Float64}, N::Int; optimizer=optimizer_with_attributes(Ipopt.Optimizer,
350+ # # # "print_level" => 0,
351+ # # "hsllib" => HSL_jll.libhsl_path,
352+ # # "linear_solver" => "MA27"
353+ # # )
354+ # #)
355+ # # model = Model()
356+ # # set_optimizer(model, optimizer)
357+ # #
358+ # # # For demonstration, let's define:
359+ # # # x[t=1:N, 1:atlas.nx]
360+ # # # u[t=1:N-1, 1:atlas.nu]
361+ # # @variable(model, x[1:N, 1:atlas_obj.nx])
362+ # # @variable(model, -atlas_obj.torque_limits[i] <= u[t=1:N-1,i=1:atlas.nu] <= atlas_obj.torque_limits[i])
363+ # # # ^ adapt the indexing if torque_limits is an array of length nu, etc.
364+ # #
365+ # # # Objective: sum of squared error from x_ref
366+ # # @objective(model, Min, sum( (x[t,j] - x_ref[j])^2 for t in 2:N for j in 1:atlas_obj.nx ) )
367+ # #
368+ # # for t in 1:(N-1)
369+ # # # Bindconstraint x[t+1, i] == f_i( [x[t,:]; u[t,:]]... )
370+ # # @constraint(model, x[t+1, :] == atlas_dynamics( convert.(NonlinearExpr, x[t, :]), convert.(NonlinearExpr,u[t, :])))
371+ # # end
372+ # #
373+ # # # Possibly set an initial condition constraint: x[1, :] = x_ref
374+ # # @constraint(model, [j in 1:atlas_obj.nx], x[1,j] == X_start[j])
375+ # #
376+ # # # Solve
377+ # # optimize!(model)
378+ # # return model, value.(x), value.(u)
379+ # #end
380+ #
381+ # N = 2
382+ # X_start = deepcopy(x_ref)
383+ # X_start[atlas.nq + 5] = 1.3
384+ # model, X_solve, U_solve = build_and_solve_mpc(atlas, x_ref, X_start, N; optimizer=optimizer_with_attributes(Ipopt.Optimizer,
385+ # # "print_level" => 0,
386+ # "linear_solver" => "ma97",
387+ # "hessian_approximation" => "limited-memory",
388+ # "max_iter" => 300,
389+ # "mu_target" => 1e-8,
390+ # "print_info_string" => "yes",
391+ # )
392+ # )
393+ # termination_status(model)
394+ #
395+ # # Now we can simulate the system with the controls U_solve
396+ # X = [zeros(atlas.nx) for _ = 1:N];
397+ # X[1] = deepcopy(x_ref);
398+ # X[1][atlas.nq + 5] = 1.3; # Perturb i.c.
399+ #
400+ # for k = 1:N - 1
401+ # X[k + 1] = rk4(atlas, X[k], U_solve[k, :], h)
402+ # end
403+ #
404+ # animate!(atlas, mvis, X, Δt=h);
405+ #
406+ # error = sum( norm(X_solve[t,:] .- X[t]) for t in 1:N )
407+ #
408+ #
409+ # function foo!(q::AbstractVector{T}) where {T}
410+ # q .= zero(T)
411+ # end
0 commit comments