Certainly, I will provide all the Code. My co-worker helps me to check the code, yet still find no error.
Thanks in advance.
besides: There are some other questions about the modified single pendulum on cart I want to consult.
Do I need create a new topic or do we here discuss later?
- code of mpc_model
double pendulum on cart model
from acados_template import AcadosModel
from casadi import SX, vertcat, sin, cos
def export_double_pendulum_ocp_model() -> AcadosModel:
model_name = 'double_pendulum_on_cart'
# constants
g = 9.81 # gravitational acceleration [m/s^2]
# Pendulum parameters
M = 2.0 # Mass of cart
L1, L2 = 0.4, 0.6 # lengths of pendulum [m]
l1, l2 = 0.2, 0.3 # distances to CoM [m]
m1, m2 = 1.0, 1.25 # masses of pendulum [kg]
I1, I2 = 0.0014, 0.0375 # moments of inertia [N·m·s^2]
# --- States ---
theta1 = SX.sym('theta1') # angle of first pendulum [rad]
theta2 = SX.sym('theta2') # angle of second pendulum [rad]
dtheta1 = SX.sym('dtheta1') # angular velocity of first pendulum [rad/s]
dtheta2 = SX.sym('dtheta2') # angular velocity of second pendulum [rad/s]
cart_x = SX.sym('cart_x') # cart position [m]
cart_v = SX.sym('cart_v') # cart velocity [m/s]
# Define state vector
x = vertcat(cart_x, theta1, theta2, cart_v, dtheta1, dtheta2)
# --- Controls ---
F = SX.sym('F') # cart acceleration (control input) [m/s^2]
u = vertcat(F)
# xdot
cart_x_dot = SX.sym('cart_x_dot')
cart_v_dot = SX.sym('cart_v_dot')
theta1_dot = SX.sym('theta1_dot')
dtheta1_dot = SX.sym('dtheta1_dot')
theta2_dot = SX.sym('theta2_dot')
dtheta2_dot = SX.sym('dtheta2_dot')
x_dot = vertcat(cart_x_dot, theta1_dot, theta2_dot, cart_v_dot, dtheta1_dot, dtheta2_dot)
# parameters
p = []
# dynamics equations
dd_x = (F*I1*I2 + F*I1*l2**2*m2 + F*I2*L1**2*m2 + F*I2*l1**2*m1 - F*L1**2*l2**2*m2**2*cos(theta1 - theta2)**2 + F*L1**2*l2**2*m2**2 + F*l1**2*l2**2*m1*m2 + I1*I2*L1*dtheta1**2*m2*sin(theta1) + I1*I2*dtheta1**2*l1*m1*sin(theta1) + I1*I2*dtheta2**2*l2*m2*sin(theta2) + I1*L1*dtheta1**2*l2**2*m2**2*sin(theta1) - I1*L1*dtheta1**2*l2**2*m2**2*sin(theta1 - theta2)*cos(theta2) + I1*dtheta1**2*l1*l2**2*m1*m2*sin(theta1) + I1*dtheta2**2*l2**3*m2**2*sin(theta2) - I1*g*l2**2*m2**2*sin(2*theta2)/2 + I2*L1**3*dtheta1**2*m2**2*sin(theta1) + I2*L1**2*dtheta1**2*l1*m1*m2*sin(theta1) + I2*L1**2*dtheta2**2*l2*m2**2*sin(theta2) + I2*L1**2*dtheta2**2*l2*m2**2*sin(theta1 - theta2)*cos(theta1) - I2*L1**2*g*m2**2*sin(2*theta1)/2 + I2*L1*dtheta1**2*l1**2*m1*m2*sin(theta1) + I2*L1*dtheta2**2*l1*l2*m1*m2*sin(theta1 - theta2)*cos(theta1) - I2*L1*g*l1*m1*m2*sin(2*theta1) + I2*dtheta1**2*l1**3*m1**2*sin(theta1) + I2*dtheta2**2*l1**2*l2*m1*m2*sin(theta2) - I2*g*l1**2*m1**2*sin(2*theta1)/2 + L1**3*dtheta1**2*l2**2*m2**3*(sin(theta1 - 2*theta2) + sin(3*theta1 - 2*theta2))/4 - L1**3*dtheta1**2*l2**2*m2**3*sin(theta1)*cos(theta1 - theta2)**2 + L1**3*dtheta1**2*l2**2*m2**3*sin(theta1) - L1**3*dtheta1**2*l2**2*m2**3*sin(theta1 - theta2)*cos(theta2) + L1**2*dtheta1**2*l1*l2**2*m1*m2**2*(sin(theta1 - 2*theta2) + sin(3*theta1 - 2*theta2))/4 - L1**2*dtheta1**2*l1*l2**2*m1*m2**2*sin(theta1)*cos(theta1 - theta2)**2 + L1**2*dtheta1**2*l1*l2**2*m1*m2**2*sin(theta1) - L1**2*dtheta2**2*l2**3*m2**3*(sin(2*theta1 - 3*theta2) + sin(2*theta1 - theta2))/4 - L1**2*dtheta2**2*l2**3*m2**3*sin(theta2)*cos(theta1 - theta2)**2 + L1**2*dtheta2**2*l2**3*m2**3*sin(theta2) + L1**2*dtheta2**2*l2**3*m2**3*sin(theta1 - theta2)*cos(theta1) + L1**2*g*l2**2*m2**3*sin(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*g*l2**2*m2**3*sin(2*theta1)/2 + L1**2*g*l2**2*m2**3*sin(theta2)*cos(theta1)*cos(theta1 - theta2) - L1**2*g*l2**2*m2**3*sin(2*theta2)/2 + L1*dtheta1**2*l1**2*l2**2*m1*m2**2*sin(theta1) - L1*dtheta1**2*l1**2*l2**2*m1*m2**2*sin(theta1 - theta2)*cos(theta2) + L1*dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 - theta2)*cos(theta1) + L1*g*l1*l2**2*m1*m2**2*sin(theta1)*cos(theta2)*cos(theta1 - theta2) - L1*g*l1*l2**2*m1*m2**2*sin(2*theta1) + L1*g*l1*l2**2*m1*m2**2*sin(theta2)*cos(theta1)*cos(theta1 - theta2) + dtheta1**2*l1**3*l2**2*m1**2*m2*sin(theta1) + dtheta2**2*l1**2*l2**3*m1*m2**2*sin(theta2) - g*l1**2*l2**2*m1**2*m2*sin(2*theta1)/2 - g*l1**2*l2**2*m1*m2**2*sin(2*theta2)/2)/(I1*I2*M + I1*I2*m1 + I1*I2*m2 + I1*M*l2**2*m2 + I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(theta2)**2 + I1*l2**2*m2**2 + I2*L1**2*M*m2 + I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(theta1)**2 + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(theta1)**2 + I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(theta1)**2 + I2*l1**2*m1**2 + I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(theta1 - theta2)**2 + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(theta1 - theta2)**2 + L1**2*l2**2*m1*m2**2 - L1**2*l2**2*m2**3*cos(theta1)**2 + 2*L1**2*l2**2*m2**3*cos(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*l2**2*m2**3*cos(theta2)**2 - L1**2*l2**2*m2**3*cos(theta1 - theta2)**2 + L1**2*l2**2*m2**3 - 2*L1*l1*l2**2*m1*m2**2*cos(theta1)**2 + 2*L1*l1*l2**2*m1*m2**2*cos(theta1)*cos(theta2)*cos(theta1 - theta2) + M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(theta1)**2 + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(theta2)**2 + l1**2*l2**2*m1*m2**2)
dd_theta1 = (-2*F*I2*L1*m2*cos(theta1) - 2*F*I2*l1*m1*cos(theta1) - F*L1*l2**2*m2**2*cos(theta1) + F*L1*l2**2*m2**2*cos(theta1 - 2*theta2) - 2*F*l1*l2**2*m1*m2*cos(theta1) - I2*L1**2*dtheta1**2*m2**2*sin(2*theta1) - 2*I2*L1*M*dtheta2**2*l2*m2*sin(theta1 - theta2) + 2*I2*L1*M*g*m2*sin(theta1) - 2*I2*L1*dtheta1**2*l1*m1*m2*sin(2*theta1) - 2*I2*L1*dtheta2**2*l2*m1*m2*sin(theta1 - theta2) - I2*L1*dtheta2**2*l2*m2**2*sin(theta1 - theta2) - I2*L1*dtheta2**2*l2*m2**2*sin(theta1 + theta2) + 2*I2*L1*g*m1*m2*sin(theta1) + 2*I2*L1*g*m2**2*sin(theta1) + 2*I2*M*g*l1*m1*sin(theta1) - I2*dtheta1**2*l1**2*m1**2*sin(2*theta1) + I2*dtheta2**2*l1*l2*m1*m2*sin(theta1 - theta2) - I2*dtheta2**2*l1*l2*m1*m2*sin(theta1 + theta2) + 2*I2*g*l1*m1**2*sin(theta1) + 2*I2*g*l1*m1*m2*sin(theta1) - L1**2*M*dtheta1**2*l2**2*m2**2*sin(2*theta1 - 2*theta2) - L1**2*dtheta1**2*l2**2*m1*m2**2*sin(2*theta1 - 2*theta2) - 2*L1*M*dtheta2**2*l2**3*m2**2*sin(theta1 - theta2) + L1*M*g*l2**2*m2**2*sin(theta1) + L1*M*g*l2**2*m2**2*sin(theta1 - 2*theta2) - L1*dtheta1**2*l1*l2**2*m1*m2**2*sin(2*theta1) + L1*dtheta1**2*l1*l2**2*m1*m2**2*sin(2*theta1 - 2*theta2) - 2*L1*dtheta2**2*l2**3*m1*m2**2*sin(theta1 - theta2) + L1*g*l2**2*m1*m2**2*sin(theta1) + L1*g*l2**2*m1*m2**2*sin(theta1 - 2*theta2) + 2*M*g*l1*l2**2*m1*m2*sin(theta1) - dtheta1**2*l1**2*l2**2*m1**2*m2*sin(2*theta1) + dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 - theta2) - dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 + theta2) + 2*g*l1*l2**2*m1**2*m2*sin(theta1) + g*l1*l2**2*m1*m2**2*sin(theta1) - g*l1*l2**2*m1*m2**2*sin(theta1 - 2*theta2))/(2*I1*I2*M + 2*I1*I2*m1 + 2*I1*I2*m2 + 2*I1*M*l2**2*m2 + 2*I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(2*theta2) + I1*l2**2*m2**2 + 2*I2*L1**2*M*m2 + 2*I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(2*theta1) + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(2*theta1) - 2*I2*L1*l1*m1*m2 + 2*I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(2*theta1) + I2*l1**2*m1**2 + 2*I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(2*theta1 - 2*theta2) + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(2*theta1 - 2*theta2) + L1**2*l2**2*m1*m2**2 - L1*l1*l2**2*m1*m2**2*cos(2*theta1) + L1*l1*l2**2*m1*m2**2*cos(2*theta2) + L1*l1*l2**2*m1*m2**2*cos(2*theta1 - 2*theta2) - L1*l1*l2**2*m1*m2**2 + 2*M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(2*theta1) + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(2*theta2) + l1**2*l2**2*m1*m2**2)
dd_theta2 = l2*m2*(-F*I1*cos(theta2) - F*L1**2*m2*cos(theta2)/2 + F*L1**2*m2*cos(2*theta1 - theta2)/2 + F*L1*l1*m1*cos(theta2)/2 + F*L1*l1*m1*cos(2*theta1 - theta2)/2 - F*l1**2*m1*cos(theta2) + I1*L1*M*dtheta1**2*sin(theta1 - theta2) + I1*L1*dtheta1**2*m1*sin(theta1 - theta2) + I1*L1*dtheta1**2*m2*sin(theta1 - theta2)/2 - I1*L1*dtheta1**2*m2*sin(theta1 + theta2)/2 + I1*M*g*sin(theta2) - I1*dtheta1**2*l1*m1*sin(theta1 - theta2)/2 - I1*dtheta1**2*l1*m1*sin(theta1 + theta2)/2 - I1*dtheta2**2*l2*m2*sin(2*theta2)/2 + I1*g*m1*sin(theta2) + I1*g*m2*sin(theta2) + L1**3*M*dtheta1**2*m2*sin(theta1 - theta2) + L1**3*dtheta1**2*m1*m2*sin(theta1 - theta2) + L1**2*M*dtheta2**2*l2*m2*sin(2*theta1 - 2*theta2)/2 + L1**2*M*g*m2*sin(theta2)/2 - L1**2*M*g*m2*sin(2*theta1 - theta2)/2 - 3*L1**2*dtheta1**2*l1*m1*m2*sin(theta1 - theta2)/2 + L1**2*dtheta1**2*l1*m1*m2*sin(theta1 + theta2)/2 + L1**2*dtheta2**2*l2*m1*m2*sin(2*theta1 - 2*theta2)/2 + L1**2*g*m1*m2*sin(theta2)/2 - L1**2*g*m1*m2*sin(2*theta1 - theta2)/2 + L1*M*dtheta1**2*l1**2*m1*sin(theta1 - theta2) - L1*M*g*l1*m1*sin(theta2)/2 - L1*M*g*l1*m1*sin(2*theta1 - theta2)/2 + L1*dtheta1**2*l1**2*m1**2*sin(theta1 - theta2)/2 + L1*dtheta1**2*l1**2*m1**2*sin(theta1 + theta2)/2 + L1*dtheta1**2*l1**2*m1*m2*sin(theta1 - theta2)/2 - L1*dtheta1**2*l1**2*m1*m2*sin(theta1 + theta2)/2 + L1*dtheta2**2*l1*l2*m1*m2*sin(2*theta2)/2 - L1*dtheta2**2*l1*l2*m1*m2*sin(2*theta1 - 2*theta2)/2 - L1*g*l1*m1**2*sin(theta2)/2 - L1*g*l1*m1**2*sin(2*theta1 - theta2)/2 - 3*L1*g*l1*m1*m2*sin(theta2)/2 + L1*g*l1*m1*m2*sin(2*theta1 - theta2)/2 + M*g*l1**2*m1*sin(theta2) - dtheta1**2*l1**3*m1**2*sin(theta1 - theta2)/2 - dtheta1**2*l1**3*m1**2*sin(theta1 + theta2)/2 - dtheta2**2*l1**2*l2*m1*m2*sin(2*theta2)/2 + g*l1**2*m1**2*sin(theta2)/2 + g*l1**2*m1**2*sin(2*theta1 - theta2)/2 + g*l1**2*m1*m2*sin(theta2))/(I1*I2*M + I1*I2*m1 + I1*I2*m2 + I1*M*l2**2*m2 + I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(theta2)**2 + I1*l2**2*m2**2 + I2*L1**2*M*m2 + I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(theta1)**2 + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(theta1)**2 + I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(theta1)**2 + I2*l1**2*m1**2 + I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(theta1 - theta2)**2 + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(theta1 - theta2)**2 + L1**2*l2**2*m1*m2**2 - L1**2*l2**2*m2**3*cos(theta1)**2 + 2*L1**2*l2**2*m2**3*cos(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*l2**2*m2**3*cos(theta2)**2 - L1**2*l2**2*m2**3*cos(theta1 - theta2)**2 + L1**2*l2**2*m2**3 - 2*L1*l1*l2**2*m1*m2**2*cos(theta1)**2 + 2*L1*l1*l2**2*m1*m2**2*cos(theta1)*cos(theta2)*cos(theta1 - theta2) + M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(theta1)**2 + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(theta2)**2 + l1**2*l2**2*m1*m2**2)
f_expl = vertcat(cart_v,
dtheta1,
dtheta2,
dd_x,
dd_theta1,
dd_theta2
)
f_impl = x_dot - f_expl
model = AcadosModel()
model.f_expl_expr = f_expl
model.f_impl_expr = f_impl
model.x = x
model.xdot = x_dot
model.u = u
model.p = p
model.name = model_name
return model
- Code of mpc solver
import numpy as np
import scipy
from acados_template import AcadosOcpSolver, AcadosOcp
from casadi import vertcat
def double_pendulum_ocp_solver(model, N, h, Q, R, umax,use_cython=False):
dp_ocp = AcadosOcp()
# set model
dp_ocp.model = model
# get dimension of variables
Tf = N*h
nx = model.x.rows()
nu = model.u.rows()
ny = nx + nu
ny_e = nx
dp_ocp.solver_options.N_horizon = N
# set cost
dp_ocp.cost.cost_type = 'NONLINEAR_LS'
dp_ocp.cost.cost_type_e = 'NONLINEAR_LS'
dp_ocp.cost.W = scipy.linalg.block_diag(R, Q)
dp_ocp.cost.W_e = Q
x = dp_ocp.model.x
u = dp_ocp.model.u
dp_ocp.model.cost_y_expr = vertcat(u, x)
dp_ocp.model.cost_y_expr_e = x
dp_ocp.cost.yref = np.zeros((ny,))
dp_ocp.cost.yref_e = np.zeros((ny_e,))
# set constraints
dp_ocp.constraints.idxbu = np.array([0])
dp_ocp.constraints.lbu = np.array([-umax])
dp_ocp.constraints.ubu = np.array([umax])
dp_ocp.constraints.x0 = np.array([0.0, -np.pi, -np.pi, 0.0, 0.0, 0.0])
# set QP solver
# dp_ocp.solver_options.qp_solver = 'PARTIAL_CONDENSING_HPIPM'
dp_ocp.solver_options.qp_solver = 'FULL_CONDENSING_QPOASES'
dp_ocp.solver_options.hessian_approx = 'GAUSS_NEWTON'
dp_ocp.solver_options.integrator_type = 'ERK'
# set prediction horizon
dp_ocp.solver_options.tf = Tf
# dp_ocp.solver_options.nlp_solver_type = 'SQP'
dp_ocp.solver_options.nlp_solver_type = 'SQP_RTI'
dp_ocp.solver_options.nlp_solver_max_iter = 200
if use_cython:
AcadosOcpSolver.generate(dp_ocp, json_file='acados_dp_ocp.json')
AcadosOcpSolver.build(dp_ocp.code_export_directory, with_cython=True)
acados_solver = AcadosOcpSolver.create_cython_solver('acados_dp_ocp.json')
else:
acados_solver = AcadosOcpSolver(dp_ocp, json_file='acados_dp_ocp.json')
return acados_solver
- Code of mhe model
double pendulum on cart model
from acados_template import AcadosModel
from casadi import SX, vertcat, sin, cos
def export_double_pendulum_mhe_model() -> AcadosModel:
model_name = 'double_pendulum_on_cart'
# constants
g = 9.81 # gravitational acceleration [m/s^2]
# Pendulum parameters
M = 2.0 # Mass of cart
L1, L2 = 0.4, 0.6 # lengths of pendulum [m]
l1, l2 = 0.2, 0.3 # distances to CoM [m]
m1, m2 = 1.0, 1.25 # masses of pendulum [kg]
I1, I2 = 0.0014, 0.0375 # moments of inertia [N·m·s^2]
#d1, d2 = 0.005, 0.005 # damping coefficients [N·m·s]
# --- States ---
theta1 = SX.sym('theta1') # angle of first pendulum [rad]
theta2 = SX.sym('theta2') # angle of second pendulum [rad]
dtheta1 = SX.sym('dtheta1') # angular velocity of first pendulum [rad/s]
dtheta2 = SX.sym('dtheta2') # angular velocity of second pendulum [rad/s]
cart_x = SX.sym('cart_x') # cart position [m]
cart_v = SX.sym('cart_v') # cart velocity [m/s]
# Define state vector
x = vertcat(cart_x, theta1, theta2, cart_v, dtheta1, dtheta2)
# state noise
w_theta1 = SX.sym('w_theta1')
w_theta2 = SX.sym('w_theta2')
w_dtheta1 = SX.sym('w_dtheta1')
w_dtheta2 = SX.sym('w_dtheta2')
w_cart_x = SX.sym('w_cart_x')
w_cart_v = SX.sym('w_cart_v')
w = vertcat(w_cart_x, w_theta1, w_theta2, w_cart_v, w_dtheta1, w_dtheta2)
# xdot
cart_x_dot = SX.sym('cart_x_dot')
cart_v_dot = SX.sym('cart_v_dot')
theta1_dot = SX.sym('theta1_dot')
dtheta1_dot = SX.sym('dtheta1_dot')
theta2_dot = SX.sym('theta2_dot')
dtheta2_dot = SX.sym('dtheta2_dot')
x_dot = vertcat(cart_x_dot, theta1_dot, theta2_dot, cart_v_dot, dtheta1_dot, dtheta2_dot)
# algebraic variables
z = []
# controls as parameters
F = SX.sym('F') # cart acceleration (control input) [m/s^2]
p = vertcat(F)
# dynamics equations
dd_x = (F*I1*I2 + F*I1*l2**2*m2 + F*I2*L1**2*m2 + F*I2*l1**2*m1 - F*L1**2*l2**2*m2**2*cos(theta1 - theta2)**2 + F*L1**2*l2**2*m2**2 + F*l1**2*l2**2*m1*m2 + I1*I2*L1*dtheta1**2*m2*sin(theta1) + I1*I2*dtheta1**2*l1*m1*sin(theta1) + I1*I2*dtheta2**2*l2*m2*sin(theta2) + I1*L1*dtheta1**2*l2**2*m2**2*sin(theta1) - I1*L1*dtheta1**2*l2**2*m2**2*sin(theta1 - theta2)*cos(theta2) + I1*dtheta1**2*l1*l2**2*m1*m2*sin(theta1) + I1*dtheta2**2*l2**3*m2**2*sin(theta2) - I1*g*l2**2*m2**2*sin(2*theta2)/2 + I2*L1**3*dtheta1**2*m2**2*sin(theta1) + I2*L1**2*dtheta1**2*l1*m1*m2*sin(theta1) + I2*L1**2*dtheta2**2*l2*m2**2*sin(theta2) + I2*L1**2*dtheta2**2*l2*m2**2*sin(theta1 - theta2)*cos(theta1) - I2*L1**2*g*m2**2*sin(2*theta1)/2 + I2*L1*dtheta1**2*l1**2*m1*m2*sin(theta1) + I2*L1*dtheta2**2*l1*l2*m1*m2*sin(theta1 - theta2)*cos(theta1) - I2*L1*g*l1*m1*m2*sin(2*theta1) + I2*dtheta1**2*l1**3*m1**2*sin(theta1) + I2*dtheta2**2*l1**2*l2*m1*m2*sin(theta2) - I2*g*l1**2*m1**2*sin(2*theta1)/2 + L1**3*dtheta1**2*l2**2*m2**3*(sin(theta1 - 2*theta2) + sin(3*theta1 - 2*theta2))/4 - L1**3*dtheta1**2*l2**2*m2**3*sin(theta1)*cos(theta1 - theta2)**2 + L1**3*dtheta1**2*l2**2*m2**3*sin(theta1) - L1**3*dtheta1**2*l2**2*m2**3*sin(theta1 - theta2)*cos(theta2) + L1**2*dtheta1**2*l1*l2**2*m1*m2**2*(sin(theta1 - 2*theta2) + sin(3*theta1 - 2*theta2))/4 - L1**2*dtheta1**2*l1*l2**2*m1*m2**2*sin(theta1)*cos(theta1 - theta2)**2 + L1**2*dtheta1**2*l1*l2**2*m1*m2**2*sin(theta1) - L1**2*dtheta2**2*l2**3*m2**3*(sin(2*theta1 - 3*theta2) + sin(2*theta1 - theta2))/4 - L1**2*dtheta2**2*l2**3*m2**3*sin(theta2)*cos(theta1 - theta2)**2 + L1**2*dtheta2**2*l2**3*m2**3*sin(theta2) + L1**2*dtheta2**2*l2**3*m2**3*sin(theta1 - theta2)*cos(theta1) + L1**2*g*l2**2*m2**3*sin(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*g*l2**2*m2**3*sin(2*theta1)/2 + L1**2*g*l2**2*m2**3*sin(theta2)*cos(theta1)*cos(theta1 - theta2) - L1**2*g*l2**2*m2**3*sin(2*theta2)/2 + L1*dtheta1**2*l1**2*l2**2*m1*m2**2*sin(theta1) - L1*dtheta1**2*l1**2*l2**2*m1*m2**2*sin(theta1 - theta2)*cos(theta2) + L1*dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 - theta2)*cos(theta1) + L1*g*l1*l2**2*m1*m2**2*sin(theta1)*cos(theta2)*cos(theta1 - theta2) - L1*g*l1*l2**2*m1*m2**2*sin(2*theta1) + L1*g*l1*l2**2*m1*m2**2*sin(theta2)*cos(theta1)*cos(theta1 - theta2) + dtheta1**2*l1**3*l2**2*m1**2*m2*sin(theta1) + dtheta2**2*l1**2*l2**3*m1*m2**2*sin(theta2) - g*l1**2*l2**2*m1**2*m2*sin(2*theta1)/2 - g*l1**2*l2**2*m1*m2**2*sin(2*theta2)/2)/(I1*I2*M + I1*I2*m1 + I1*I2*m2 + I1*M*l2**2*m2 + I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(theta2)**2 + I1*l2**2*m2**2 + I2*L1**2*M*m2 + I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(theta1)**2 + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(theta1)**2 + I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(theta1)**2 + I2*l1**2*m1**2 + I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(theta1 - theta2)**2 + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(theta1 - theta2)**2 + L1**2*l2**2*m1*m2**2 - L1**2*l2**2*m2**3*cos(theta1)**2 + 2*L1**2*l2**2*m2**3*cos(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*l2**2*m2**3*cos(theta2)**2 - L1**2*l2**2*m2**3*cos(theta1 - theta2)**2 + L1**2*l2**2*m2**3 - 2*L1*l1*l2**2*m1*m2**2*cos(theta1)**2 + 2*L1*l1*l2**2*m1*m2**2*cos(theta1)*cos(theta2)*cos(theta1 - theta2) + M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(theta1)**2 + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(theta2)**2 + l1**2*l2**2*m1*m2**2)
dd_theta1 = (-2*F*I2*L1*m2*cos(theta1) - 2*F*I2*l1*m1*cos(theta1) - F*L1*l2**2*m2**2*cos(theta1) + F*L1*l2**2*m2**2*cos(theta1 - 2*theta2) - 2*F*l1*l2**2*m1*m2*cos(theta1) - I2*L1**2*dtheta1**2*m2**2*sin(2*theta1) - 2*I2*L1*M*dtheta2**2*l2*m2*sin(theta1 - theta2) + 2*I2*L1*M*g*m2*sin(theta1) - 2*I2*L1*dtheta1**2*l1*m1*m2*sin(2*theta1) - 2*I2*L1*dtheta2**2*l2*m1*m2*sin(theta1 - theta2) - I2*L1*dtheta2**2*l2*m2**2*sin(theta1 - theta2) - I2*L1*dtheta2**2*l2*m2**2*sin(theta1 + theta2) + 2*I2*L1*g*m1*m2*sin(theta1) + 2*I2*L1*g*m2**2*sin(theta1) + 2*I2*M*g*l1*m1*sin(theta1) - I2*dtheta1**2*l1**2*m1**2*sin(2*theta1) + I2*dtheta2**2*l1*l2*m1*m2*sin(theta1 - theta2) - I2*dtheta2**2*l1*l2*m1*m2*sin(theta1 + theta2) + 2*I2*g*l1*m1**2*sin(theta1) + 2*I2*g*l1*m1*m2*sin(theta1) - L1**2*M*dtheta1**2*l2**2*m2**2*sin(2*theta1 - 2*theta2) - L1**2*dtheta1**2*l2**2*m1*m2**2*sin(2*theta1 - 2*theta2) - 2*L1*M*dtheta2**2*l2**3*m2**2*sin(theta1 - theta2) + L1*M*g*l2**2*m2**2*sin(theta1) + L1*M*g*l2**2*m2**2*sin(theta1 - 2*theta2) - L1*dtheta1**2*l1*l2**2*m1*m2**2*sin(2*theta1) + L1*dtheta1**2*l1*l2**2*m1*m2**2*sin(2*theta1 - 2*theta2) - 2*L1*dtheta2**2*l2**3*m1*m2**2*sin(theta1 - theta2) + L1*g*l2**2*m1*m2**2*sin(theta1) + L1*g*l2**2*m1*m2**2*sin(theta1 - 2*theta2) + 2*M*g*l1*l2**2*m1*m2*sin(theta1) - dtheta1**2*l1**2*l2**2*m1**2*m2*sin(2*theta1) + dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 - theta2) - dtheta2**2*l1*l2**3*m1*m2**2*sin(theta1 + theta2) + 2*g*l1*l2**2*m1**2*m2*sin(theta1) + g*l1*l2**2*m1*m2**2*sin(theta1) - g*l1*l2**2*m1*m2**2*sin(theta1 - 2*theta2))/(2*I1*I2*M + 2*I1*I2*m1 + 2*I1*I2*m2 + 2*I1*M*l2**2*m2 + 2*I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(2*theta2) + I1*l2**2*m2**2 + 2*I2*L1**2*M*m2 + 2*I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(2*theta1) + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(2*theta1) - 2*I2*L1*l1*m1*m2 + 2*I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(2*theta1) + I2*l1**2*m1**2 + 2*I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(2*theta1 - 2*theta2) + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(2*theta1 - 2*theta2) + L1**2*l2**2*m1*m2**2 - L1*l1*l2**2*m1*m2**2*cos(2*theta1) + L1*l1*l2**2*m1*m2**2*cos(2*theta2) + L1*l1*l2**2*m1*m2**2*cos(2*theta1 - 2*theta2) - L1*l1*l2**2*m1*m2**2 + 2*M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(2*theta1) + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(2*theta2) + l1**2*l2**2*m1*m2**2)
dd_theta2 = l2*m2*(-F*I1*cos(theta2) - F*L1**2*m2*cos(theta2)/2 + F*L1**2*m2*cos(2*theta1 - theta2)/2 + F*L1*l1*m1*cos(theta2)/2 + F*L1*l1*m1*cos(2*theta1 - theta2)/2 - F*l1**2*m1*cos(theta2) + I1*L1*M*dtheta1**2*sin(theta1 - theta2) + I1*L1*dtheta1**2*m1*sin(theta1 - theta2) + I1*L1*dtheta1**2*m2*sin(theta1 - theta2)/2 - I1*L1*dtheta1**2*m2*sin(theta1 + theta2)/2 + I1*M*g*sin(theta2) - I1*dtheta1**2*l1*m1*sin(theta1 - theta2)/2 - I1*dtheta1**2*l1*m1*sin(theta1 + theta2)/2 - I1*dtheta2**2*l2*m2*sin(2*theta2)/2 + I1*g*m1*sin(theta2) + I1*g*m2*sin(theta2) + L1**3*M*dtheta1**2*m2*sin(theta1 - theta2) + L1**3*dtheta1**2*m1*m2*sin(theta1 - theta2) + L1**2*M*dtheta2**2*l2*m2*sin(2*theta1 - 2*theta2)/2 + L1**2*M*g*m2*sin(theta2)/2 - L1**2*M*g*m2*sin(2*theta1 - theta2)/2 - 3*L1**2*dtheta1**2*l1*m1*m2*sin(theta1 - theta2)/2 + L1**2*dtheta1**2*l1*m1*m2*sin(theta1 + theta2)/2 + L1**2*dtheta2**2*l2*m1*m2*sin(2*theta1 - 2*theta2)/2 + L1**2*g*m1*m2*sin(theta2)/2 - L1**2*g*m1*m2*sin(2*theta1 - theta2)/2 + L1*M*dtheta1**2*l1**2*m1*sin(theta1 - theta2) - L1*M*g*l1*m1*sin(theta2)/2 - L1*M*g*l1*m1*sin(2*theta1 - theta2)/2 + L1*dtheta1**2*l1**2*m1**2*sin(theta1 - theta2)/2 + L1*dtheta1**2*l1**2*m1**2*sin(theta1 + theta2)/2 + L1*dtheta1**2*l1**2*m1*m2*sin(theta1 - theta2)/2 - L1*dtheta1**2*l1**2*m1*m2*sin(theta1 + theta2)/2 + L1*dtheta2**2*l1*l2*m1*m2*sin(2*theta2)/2 - L1*dtheta2**2*l1*l2*m1*m2*sin(2*theta1 - 2*theta2)/2 - L1*g*l1*m1**2*sin(theta2)/2 - L1*g*l1*m1**2*sin(2*theta1 - theta2)/2 - 3*L1*g*l1*m1*m2*sin(theta2)/2 + L1*g*l1*m1*m2*sin(2*theta1 - theta2)/2 + M*g*l1**2*m1*sin(theta2) - dtheta1**2*l1**3*m1**2*sin(theta1 - theta2)/2 - dtheta1**2*l1**3*m1**2*sin(theta1 + theta2)/2 - dtheta2**2*l1**2*l2*m1*m2*sin(2*theta2)/2 + g*l1**2*m1**2*sin(theta2)/2 + g*l1**2*m1**2*sin(2*theta1 - theta2)/2 + g*l1**2*m1*m2*sin(theta2))/(I1*I2*M + I1*I2*m1 + I1*I2*m2 + I1*M*l2**2*m2 + I1*l2**2*m1*m2 - I1*l2**2*m2**2*cos(theta2)**2 + I1*l2**2*m2**2 + I2*L1**2*M*m2 + I2*L1**2*m1*m2 - I2*L1**2*m2**2*cos(theta1)**2 + I2*L1**2*m2**2 - 2*I2*L1*l1*m1*m2*cos(theta1)**2 + I2*M*l1**2*m1 - I2*l1**2*m1**2*cos(theta1)**2 + I2*l1**2*m1**2 + I2*l1**2*m1*m2 - L1**2*M*l2**2*m2**2*cos(theta1 - theta2)**2 + L1**2*M*l2**2*m2**2 - L1**2*l2**2*m1*m2**2*cos(theta1 - theta2)**2 + L1**2*l2**2*m1*m2**2 - L1**2*l2**2*m2**3*cos(theta1)**2 + 2*L1**2*l2**2*m2**3*cos(theta1)*cos(theta2)*cos(theta1 - theta2) - L1**2*l2**2*m2**3*cos(theta2)**2 - L1**2*l2**2*m2**3*cos(theta1 - theta2)**2 + L1**2*l2**2*m2**3 - 2*L1*l1*l2**2*m1*m2**2*cos(theta1)**2 + 2*L1*l1*l2**2*m1*m2**2*cos(theta1)*cos(theta2)*cos(theta1 - theta2) + M*l1**2*l2**2*m1*m2 - l1**2*l2**2*m1**2*m2*cos(theta1)**2 + l1**2*l2**2*m1**2*m2 - l1**2*l2**2*m1*m2**2*cos(theta2)**2 + l1**2*l2**2*m1*m2**2)
f_expl = vertcat(cart_v,
dtheta1,
dtheta2,
dd_x,
dd_theta1,
dd_theta2
)
f_expl = f_expl + w
f_impl = x_dot - f_expl
model = AcadosModel()
model.f_expl_expr = f_expl
model.f_impl_expr = f_impl
model.x = x
model.xdot = x_dot
model.u = w
model.z = z
model.p = p
model.name = model_name
return model
- Code of mhe solver
import numpy as np
from scipy.linalg import block_diag
from acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
from casadi import vertcat
def double_pendulum_mhe_solver(model:AcadosModel, N: int, h, Q, P, R) -> AcadosOcpSolver:
dp_mhe = AcadosOcp()
dp_mhe.model = model
x = dp_mhe.model.x
u = dp_mhe.model.u
nx = x.rows()
nu = u.rows()
nparam = model.p.rows()
ny_0 = nx + nu + nx # h(x), w and arrival cost
ny = nx + nu # h(x), w
dp_mhe.dims.N = N
## set inital cost
dp_mhe.cost.cost_type_0 = 'NONLINEAR_LS'
dp_mhe.cost.W_0 = block_diag(R, Q, P)
dp_mhe.model.cost_y_expr_0 = vertcat(x, u, x)
dp_mhe.cost.yref_0 = np.zeros((ny_0,))
# intermediate
dp_mhe.cost.cost_type = 'NONLINEAR_LS'
dp_mhe.cost.W = block_diag(R, Q)
dp_mhe.model.cost_y_expr = vertcat(x, u)
dp_mhe.cost.yref = np.zeros((ny,))
dp_mhe.parameter_values = np.zeros((nparam, ))
# terminal
dp_mhe.cost.cost_type_e = 'LINEAR_LS'
dp_mhe.cost.yref_e = np.zeros((0, ))
# Solver options
dp_mhe.solver_options.qp_solver = 'FULL_CONDENSING_QPOASES'
dp_mhe.solver_options.hessian_approx = 'GAUSS_NEWTON'
dp_mhe.solver_options.integrator_type = 'ERK'
dp_mhe.solver_options.tf = N*h # set prediction horizon
# dp_mhe.solver_options.nlp_solver_type = 'SQP'
dp_mhe.solver_options.nlp_solver_type = 'SQP_RTI'
dp_mhe.solver_options.nlp_solver_max_iter = 200
acados_solver_mhe = AcadosOcpSolver(dp_mhe, json_file = 'acados_dp_mhe.json')
return acados_solver_mhe
5 Code for test
from export_double_pendulum_ocp_model import export_double_pendulum_ocp_model
from export_double_pendulum_ocp_solver import double_pendulum_ocp_solver
from export_double_pendulum_mhe_model import export_double_pendulum_mhe_model
from export_double_pendulum_mhe_solver import double_pendulum_mhe_solver
import numpy as np
from utils import plot_pendulum
# general information
Tf = 1.0 # Prediction horizon
N = 20 # Number of shooting intervals
h = Tf/N
# ocp model and solver
model_ocp = export_double_pendulum_ocp_model()
nx = model_ocp.x.rows()
nu = model_ocp.u.rows()
Q_ocp = np.diag([1e4, 1e4, 1e2, 1e-2, 1e-2, 1e-3])
R_ocp = 1e-3 *np.eye(1)
acados_solver_ocp = double_pendulum_ocp_solver(model_ocp, N, h, Q_ocp, R_ocp, umax=50)
# -------------
# mhe model and solver
model_mhe = export_double_pendulum_mhe_model()
nx = model_mhe.x.rows()
nw = model_mhe.u.rows()
ny = nx
P_mhe = 10*np.eye((nx))
Q_mhe = 0.1*np.eye(nw)
R_mhe = 0.1*np.eye(ny)
acados_solver_mhe = double_pendulum_mhe_solver(model_mhe, N, h, Q_mhe, P_mhe, R_mhe)
# simulation
v_stds = [0.1, 0.5, 0.5, 0.01, 0.02, 0.01]
simX = np.zeros((N+1, nx))
simU = np.zeros((N, nu))
simY = np.zeros((N+1, nx))
simXest = np.zeros((N+1, nx))
simWest = np.zeros((N, nw))
# arrival cost mean
x0_bar = np.array([0.0, -np.pi, -np.pi, 0.0, 0.0, 0.0])
# solve ocp problem
status = acados_solver_ocp.solve()
if status != 0:
raise Exception(f'acados returned status {status}.')
# get solution
for i in range(N):
simX[i,:] = acados_solver_ocp.get(i, "x")
simU[i,:] = acados_solver_ocp.get(i, "u")
simY[i,:] = simX[i,:] + np.transpose(np.diag(v_stds) @ np.random.standard_normal((ny, 1)))
simX[N,:] = acados_solver_ocp.get(N, "x")
simY[N,:] = simX[N,:] + np.transpose(np.diag(v_stds) @ np.random.standard_normal((ny, 1)))
# set measurements and controls
yref_0 = np.zeros((ny+nw+nx, ))
yref_0[:ny] = simY[0, :]
yref_0[ny+nw:] = x0_bar
acados_solver_mhe.set(0, "yref", yref_0)
acados_solver_mhe.set(0, "p", simU[0,:])
yref = np.zeros((ny+nx, ))
for j in range(1,N):
yref[:ny] = simY[j, :]
acados_solver_mhe.set(j, "yref", yref)
acados_solver_mhe.set(j, "p", simU[j,:])
acados_solver_mhe.set(j, "x", simX[j,:])
# solve mhe problem
status = acados_solver_mhe.solve()
if status != 0 and status != 2:
raise Exception(f'acados returned status {status}.')
# get solution
for i in range(N):
simXest[i,:] = acados_solver_mhe.get(i, "x")
simWest[i,:] = acados_solver_mhe.get(i, "u")
simXest[N, :] = acados_solver_mhe.get(N, "x")
print('difference |x0_est - x0_bar|', np.linalg.norm(x0_bar - simXest[0, :]))
print('difference |x_est - x_true|', np.linalg.norm(simXest - simX))
#plot_pendulum(np.linspace(0, Tf, N+1), simU, simX, simXest, simY, latexify=False)