len_sim = 40 # simulation length (s) nsim = int(len_sim / Ts) # simulation length(timesteps) xsim = np.zeros((nsim, nx)) usim = np.zeros((nsim, nu)) tsim = np.arange(0, nsim) * Ts time_start = time.time() t_step = t0 uMPC = uminus1 for i in range(nsim): xsim[i, :] = system_dyn.y # MPC update and step. Could be in just one function call xref = r_fun(t_step) # reference state K.update(system_dyn.y, uMPC, xref=xref) # update with measurement uMPC = K.output() # MPC step (u_k value) usim[i, :] = uMPC # System simulation step system_dyn.set_f_params(uMPC) # set current input value system_dyn.integrate(t_step + Ts) # Update simulation time t_step += Ts time_sim = time.time() - time_start fig, axes = plt.subplots(3, 1, figsize=(10, 10)) axes[0].plot(tsim, xsim[:, 0], "k", label='p') axes[0].plot(tsim, xref[0] * np.ones(np.shape(tsim)), "r--", label="p_ref")
uMPC = uminus1 for i in range(nsim): # System output # Save system data xsim[i, :] = xstep ystep = Cd.dot(x0) # + noise ? ysim[i, :] = ystep # Save estimator data xest[i, :] = xstep_est # MPC update and step. Could be in just one function call K.update(xstep_est, uMPC) # update with measurement uMPC, infoMPC = K.output(return_x_seq=True) # MPC step (u_k value) usim[i, :] = uMPC xpred[i, :, :] = infoMPC['x_seq'] # save predictions for further use # System simulation step F = uMPC v = xstep[1] theta = xstep[2] omega = xstep[3] der = np.zeros(nx) der[0] = v der[1] = (m * l * np.sin(theta) * omega**2 - m * g * np.sin(theta) * np.cos(theta) + m * ftheta * np.cos(theta) * omega + F - b * v) / (M + m * (1 - np.cos(theta)**2)) der[2] = omega
v = x_step[1] theta = x_step[2] omega = x_step[3] der = np.zeros(nx) der[0] = v der[1] = (m*l*np.sin(theta)*omega**2 -m*g*np.sin(theta)*np.cos(theta) + m*ftheta*np.cos(theta)*omega + F - b*v)/(M+m*(1-np.cos(theta)**2)) der[2] = omega der[3] = ((M+m)*(g*np.sin(theta) - ftheta*omega) - m*l*omega**2*np.sin(theta)*np.cos(theta) -(F-b*v)*np.cos(theta))/(l*(M + m*(1-np.cos(theta)**2)) ) x_step = x_step + der * Ts # x[k+1] #x_step = Ad.dot(x_step) + Bd.dot(uMPC) # Estimator x_step_est = Ad.dot(x_step_est) + Bd.dot(uMPC) # x[k+1|k] x_step_est = x_step_est + L @ (ymeas_step - yest_step) # x[k+1|k+1] # MPC update K.update(x_step_est, uMPC) # update with measurement # time_sim = time.time() - time_start fig,axes = plt.subplots(5,1, figsize=(10,10), sharex=True) axes[0].plot(t_vec, x_vec[:, 0], "k", label='p') axes[0].plot(t_vec, xref[0] * np.ones(np.shape(t_vec)), "r--", label="p_ref") axes[0].plot(t_vec, x_est_vec[:, 0], "b", label="p_est") axes[0].set_ylabel("Position (m)") axes[1].plot(t_vec, x_vec[:, 1], "k", label='v') axes[1].plot(t_vec, x_est_vec[:, 1], "b", label="v_est") axes[1].set_ylabel("Velocity (m/s)") axes[2].plot(t_vec, x_vec[:, 2] * 360 / 2 / np.pi, label="phi") axes[2].plot(t_vec, x_est_vec[:, 2] * 360 / 2 / np.pi, "b", label="phi_est")
def simulate_pendulum_MPC(sim_options): use_NN_model = get_parameter(sim_options, 'use_NN_model') if use_NN_model: Ts_fit = 10e-3 # model was fitted with this sampling time ss_model = CartPoleStateSpaceModel(Ts=Ts_fit) nn_solution = NeuralStateSpaceSimulator(ss_model, Ts=Ts_fit) model_name = "model_SS_1step_nonoise.pkl" nn_solution.ss_model.load_state_dict( torch.load(os.path.join("models", model_name))) f_ODE = nn_solution.f_ODE else: f_ODE = f_ODE_wrapped seed_val = get_parameter(sim_options, 'seed_val') if seed_val is not None: np.random.seed(seed_val) Ac = get_parameter(sim_options, 'Ac') Bc = get_parameter(sim_options, 'Bc') Cc = np.array([[1., 0., 0., 0.], [0., 0., 1., 0.]]) Dc = np.zeros((2, 1)) [nx, nu] = Bc.shape # number of states and number or inputs ny = np.shape(Cc)[0] Ts_MPC = get_parameter(sim_options, 'Ts_MPC') ratio_Ts = int(Ts_MPC // Ts_fast) Ts_MPC = ( (Ts_MPC // Ts_fast)) * Ts_fast # make Ts_MPC an integer multiple of Ts_fast # Brutal forward euler discretization Ad = np.eye(nx) + Ac * Ts_MPC Bd = Bc * Ts_MPC Cd = Cc Dd = Dc # Standard deviation of the measurement noise on position and angle std_npos = get_parameter(sim_options, 'std_npos') std_nphi = get_parameter(sim_options, 'std_nphi') # Force disturbance std_dF = get_parameter(sim_options, 'std_dF') # disturbance power spectrum w_F = get_parameter(sim_options, 'w_F') # bandwidth of the force disturbance tau_F = 1 / w_F Hu = control.TransferFunction([1], [1 / w_F, 1]) Hu = Hu * Hu Hud = control.matlab.c2d(Hu, Ts_MPC) N_sim_imp = tau_F / Ts_MPC * 20 t_imp = np.arange(N_sim_imp) * Ts_MPC t, y = control.impulse_response(Hud, t_imp) y = y[0] std_tmp = np.sqrt(np.sum(y**2)) # np.sqrt(trapz(y**2,t)) Hu = Hu / (std_tmp) * std_dF N_skip = 1 #int(20 * tau_F // Ts_MPC) # skip initial samples to get a regime sample of d t_sim_d = get_parameter(sim_options, 'len_sim') # simulation length (s) N_sim_d = int(t_sim_d // Ts_MPC) N_sim_d = N_sim_d + N_skip + 1 e = np.random.randn(N_sim_d) te = np.arange(N_sim_d) * Ts_MPC _, d, _ = control.forced_response(Hu, te, e) d = d.ravel() p_ref = d[N_skip:] #td = np.arange(len(d)) * Ts_fast Np = get_parameter(sim_options, 'Np') Nc = get_parameter(sim_options, 'Nc') Qx = get_parameter(sim_options, 'Qx') QxN = get_parameter(sim_options, 'QxN') Qu = get_parameter(sim_options, 'Qu') QDu = get_parameter(sim_options, 'QDu') # Reference input and states xref_fun = get_parameter(sim_options, 'xref_fun') # reference state xref_fun_v = np.vectorize(xref_fun, signature='()->(n)') t0 = 0 xref_MPC = xref_fun(t0) uref = get_parameter(sim_options, 'uref') uminus1 = np.array( [0.0] ) # input at time step negative one - used to penalize the first delta u at time instant 0. Could be the same as uref. # Constraints xmin = np.array([-1000, -1000, -1000, -1000]) xmax = np.array([1000, 1000.0, 1000, 1000]) umin = np.array([-10]) umax = np.array([10]) Dumin = np.array([-100 * Ts_MPC]) Dumax = np.array([100 * Ts_MPC]) # Initialize simulation system phi0 = 0.0 #10*2*np.pi/360 x0 = np.array([0, 0, phi0, 0]) # initial state # system_dyn = ode(f_ODE_wrapped).set_integrator('vode', method='bdf') # dopri5 system_dyn = ode(f_ODE).set_integrator('dopri5') system_dyn.set_initial_value(x0, t0) system_dyn.set_f_params(0.0) QP_eps_rel = get_parameter(sim_options, 'QP_eps_rel') QP_eps_abs = get_parameter(sim_options, 'QP_eps_abs') # Emergency exit conditions EMERGENCY_STOP = False EMERGENCY_POS = 30.0 EMERGENCY_ANGLE = 90 * DEG_TO_RAD K = MPCController(Ad, Bd, Np=Np, Nc=Nc, x0=x0, xref=xref_MPC, uminus1=uminus1, Qx=Qx, QxN=QxN, Qu=Qu, QDu=QDu, xmin=xmin, xmax=xmax, umin=umin, umax=umax, Dumin=Dumin, Dumax=Dumax, eps_feas=1e3, eps_rel=QP_eps_rel, eps_abs=QP_eps_abs) try: K.setup(solve=True) # setup initial problem and also solve it except: EMERGENCY_STOP = True if not EMERGENCY_STOP: if K.res.info.status != 'solved': EMERGENCY_STOP = True # Basic Kalman filter design Q_kal = get_parameter(sim_options, 'Q_kal') R_kal = get_parameter(sim_options, 'R_kal') L, P, W = kalman_design_simple(Ad, Bd, Cd, Dd, Q_kal, R_kal, type='predictor') x0_est = x0 KF = LinearStateEstimator(x0_est, Ad, Bd, Cd, Dd, L) # Simulate in closed loop len_sim = get_parameter(sim_options, 'len_sim') # simulation length (s) nsim = int( np.ceil(len_sim / Ts_MPC) ) # simulation length(timesteps) # watch out! +1 added, is it correct? t_vec = np.zeros((nsim, 1)) t_calc_vec = np.zeros( (nsim, 1)) # computational time to get MPC solution (+ estimator) status_vec = np.zeros((nsim, 1)) x_vec = np.zeros((nsim, nx)) x_ref_vec = np.zeros((nsim, nx)) y_vec = np.zeros((nsim, ny)) y_meas_vec = np.zeros((nsim, ny)) y_est_vec = np.zeros((nsim, ny)) x_est_vec = np.zeros((nsim, nx)) u_vec = np.zeros((nsim, nu)) x_MPC_pred = np.zeros( (nsim, Np + 1, nx)) # on-line predictions from the Kalman Filter nsim_fast = int(len_sim // Ts_fast) t_vec_fast = np.zeros((nsim_fast, 1)) x_vec_fast = np.zeros( (nsim_fast, nx)) # finer integration grid for performance evaluation x_ref_vec_fast = np.zeros( (nsim_fast, nx)) # finer integration grid for performance evaluatio u_vec_fast = np.zeros( (nsim_fast, nu)) # finer integration grid for performance evaluatio Fd_vec_fast = np.zeros((nsim_fast, nu)) # t_int_vec_fast = np.zeros((nsim_fast, 1)) emergency_vec_fast = np.zeros((nsim_fast, 1)) # t_pred_all = t0 + np.arange(nsim + Np + 1) * Ts_MPC Xref_MPC_all = xref_fun_v(t_pred_all) t_step = t0 x_step = x0 u_MPC = None for idx_fast in range(nsim_fast): ## Determine step type: fast simulation only or MPC step idx_MPC = idx_fast // ratio_Ts run_MPC = (idx_fast % ratio_Ts) == 0 # Output for step i # Ts_MPC outputs if run_MPC: # it is also a step of the simulation at rate Ts_MPC t_vec[idx_MPC, :] = t_step x_vec[idx_MPC, :] = x_step #system_dyn.y xref_MPC = np.array([p_ref[idx_MPC], 0.0, 0.0, 0.0]) #xref_MPC = xref_fun(t_step) # reference state t_pred = t_step + np.arange(Np + 1) * Ts_MPC x_ref_vec[idx_MPC, :] = xref_MPC if not EMERGENCY_STOP: # u_MPC, info_MPC = K.output(return_x_seq=True, return_status=True) # u[i] = k(\hat x[i]) possibly computed at time instant -1 u_MPC, info_MPC = K.output( return_status=True, return_x_seq=True ) # u[i] = k(\hat x[i]) possibly computed at time instant -1 else: u_MPC = np.zeros(nu) if not EMERGENCY_STOP: if info_MPC['status'] != 'solved': EMERGENCY_STOP = True if not EMERGENCY_STOP: #pass x_MPC_pred[idx_MPC, :, :] = info_MPC[ 'x_seq'] # x_MPC_pred[i,i+1,...| possibly computed at time instant -1] u_vec[idx_MPC, :] = u_MPC y_step = Cd.dot(x_step) # y[i] from the system ymeas_step = np.copy(y_step) ymeas_step[0] += std_npos * np.random.randn() ymeas_step[1] += std_nphi * np.random.randn() y_vec[idx_MPC, :] = y_step y_meas_vec[idx_MPC, :] = ymeas_step if not EMERGENCY_STOP: status_vec[idx_MPC, :] = (info_MPC['status'] != 'solved') if np.abs(ymeas_step[0]) > EMERGENCY_POS or np.abs( ymeas_step[1]) > EMERGENCY_ANGLE: EMERGENCY_STOP = True # Ts_fast outputs t_vec_fast[idx_fast, :] = t_step x_vec_fast[idx_fast, :] = x_step #system_dyn.y x_ref_vec_fast[idx_fast, :] = xref_MPC u_fast = u_MPC u_vec_fast[idx_fast, :] = u_fast Fd_vec_fast[idx_fast, :] = 0.0 emergency_vec_fast[idx_fast, :] = EMERGENCY_STOP ## Update to step i+1 # Controller simulation step at rate Ts_MPC if run_MPC: time_calc_start = time.perf_counter() # Kalman filter: update and predict #KF.update(ymeas_step) # \hat x[i|i] #KF.predict(u_MPC) # \hat x[i+1|i] KF.predict_update(u_MPC, ymeas_step) # MPC update if not EMERGENCY_STOP: #Xref_MPC = Xref_MPC_all[idx_MPC:idx_MPC + Np + 1] K.update( KF.x, u_MPC, xref=xref_MPC) # update with measurement and reference #K.update(KF.x, u_MPC, xref=xref_MPC) # update with measurement and reference t_calc_vec[idx_MPC, :] = time.perf_counter() - time_calc_start if t_calc_vec[idx_MPC, :] > 2 * Ts_MPC: EMERGENCY_STOP = True # System simulation step at rate Ts_fast time_integrate_start = time.perf_counter() system_dyn.set_f_params(u_fast) system_dyn.integrate(t_step + Ts_fast) x_step = system_dyn.y #x_step = x_step + f_ODE(t_step, x_step, u_fast)*Ts_fast t_int_vec_fast[ idx_fast, :] = time.perf_counter() - time_integrate_start # Time update t_step += Ts_fast simout = { 't': t_vec, 'x': x_vec, 'u': u_vec, 'y': y_vec, 'y_meas': y_meas_vec, 'x_ref': x_ref_vec, 'x_MPC_pred': x_MPC_pred, 'status': status_vec, 'Fd_fast': Fd_vec_fast, 't_fast': t_vec_fast, 'x_fast': x_vec_fast, 'x_ref_fast': x_ref_vec_fast, 'u_fast': u_vec_fast, 'emergency_fast': emergency_vec_fast, 'KF': KF, 'K': K, 'nsim': nsim, 'Ts_MPC': Ts_MPC, 't_calc': t_calc_vec, 't_int_fast': t_int_vec_fast, 'use_NN_model': use_NN_model } return simout
system_dyn.integrate(system_dyn.t + Ts) #x_step = system_dyn.y # der = f_ODE(0,x_step,uMPC) #x_step = x_step + der * Ts # true system evolves to x[i+1] #system_dyn.set_initial_value(x_step, 0) # Kalman filter: update and predict KF.update( ymeas_step ) # update \hat x[i|i-1] to \hat x[i|i] updated using ymeas[i] KF.predict(uMPC) # predict \hat x[i+1|i] using u[i] # MPC update for step i+1 time_MPC_start = time.time() K.update( KF.x, uMPC) # update with measurement (and possibly pre-compute u[i+1]) t_MPC_CPU[i] = time.time() - time_MPC_start time_sim = time.time() - time_start fig, axes = plt.subplots(3, 1, figsize=(10, 10)) axes[0].plot(t_vec, x_vec[:, 0], "k", label='p') axes[0].plot(t_vec, xref[0] * np.ones(np.shape(t_vec)), "r--", label="p_ref") #axes[0].plot(t_vec, x_vec_EA[:,0]*np.ones(np.shape(t_vec)), "r--", label="p_EA") axes[0].set_title("Position (m)") axes[1].plot(t_vec, x_vec[:, 2] * 360 / 2 / np.pi, label="phi")
x_ref_vec_fast[idx_fast, :] = xref_MPC u_fast = u_MPC + d_fast[idx_fast] u_vec_fast[idx_fast, :] = u_fast Fd_vec_fast[idx_fast, :] = d_fast[idx_fast] ## Update to step i+1 # Controller simulation step at rate Ts_MPC if run_MPC: time_calc_start = time.time() # Kalman filter: update and predict KF.update(ymeas_step) # \hat x[i|i] KF.predict(u_MPC) # \hat x[i+1|i] # MPC update #K.update(system_dyn.y, u_MPC, xref=xref_MPC) # update with measurement K.update(KF.x, u_MPC, xref=xref_MPC) # update with measurement t_calc_vec[idx_MPC, :] = time.time() - time_calc_start # System simulation step at rate Ts_fast system_dyn.set_f_params(u_fast) system_dyn.integrate(t_step + Ts_sim) # Time update t_step += Ts_sim y_OL_pred = np.zeros((nsim - Np - 1, Np + 1, ny)) # on-line predictions from the Kalman Filter y_MPC_pred = x_MPC_pred[:, :, [0, 2]] # how to vectorize C * x_MPC_pred?? y_MPC_err = np.zeros(np.shape(y_OL_pred)) y_OL_err = np.zeros(np.shape(y_OL_pred)) for i in range(nsim - Np - 1):
xsim = np.zeros((nsim, nx)) usim = np.zeros((nsim, nu)) tsim = np.arange(0, nsim) * Ts J_opt = np.zeros((nsim, 1)) time_start = time.time() xstep = x0 for i in range(nsim): uMPC, info = K.output(return_u_seq=True, return_x_seq=True, return_eps_seq=True, return_status=True, return_obj_val=True) xstep = Ad.dot(xstep) + Bd.dot(uMPC) # system step J_opt[i, :] = info['obj_val'] K.update(xstep) # update with measurement K.solve() xsim[i, :] = xstep usim[i, :] = uMPC time_sim = time.time() - time_start #K.__controller_function__(np.array([0,0]), np.array([0])) fig, axes = plt.subplots(4, 1, figsize=(10, 10)) axes[0].plot(tsim, xsim[:, 0], "k", label='p') axes[0].plot(tsim, xref[0] * np.ones(np.shape(tsim)), "r--", label="pref") axes[0].set_title("Position (m)") axes[1].plot(tsim, xsim[:, 1], label="v") axes[1].plot(tsim, xref[1] * np.ones(np.shape(tsim)), "r--", label="vref")
def simulate_pendulum_MPC(sim_options): seed_val = get_parameter(sim_options,'seed_val') if seed_val is not None: np.random.seed(seed_val) # In[Sample times] Ts_MPC = get_parameter(sim_options, 'Ts_MPC') ratio_Ts = int(Ts_MPC // Ts_PID) # In[Real System] Cc = np.array([[1., 0., 0., 0.], [0., 0., 1., 0.]]) Cd = np.copy(Cc) nx, nu = 4,1 ny = 2 # In[initialize simulation system] t0 = 0 phi0 = -0.0 * 2 * np.pi / 360 # initial angle x0 = np.array([0, 0, phi0, 0]) # initial state #system_dyn = ode(f_ODE_wrapped).set_integrator('vode', method='bdf') # dopri5 system_dyn = ode(f_ODE_wrapped).set_integrator('dopri5') # dopri5 # system_dyn = ode(f_ODE_wrapped).set_integrator('dopri5') system_dyn.set_initial_value(x0, t0) system_dyn.set_f_params(0.0) #In[MPC params --model] Acl_c = get_parameter(sim_options, 'Acl_c') Bcl_c = get_parameter(sim_options, 'Bcl_c') Ccl_c = np.array([[1., 0., 0], [0., 0., 1]]) Dcl_c = np.zeros((2, 1)) ncl_x, ncl_u = Bcl_c.shape # number of states and number or inputs #ncl_y = np.shape(Ccl_c)[0] #In[MPC matrices discretization] Acl_d = np.eye(ncl_x) + Acl_c*Ts_MPC Bcl_d = Bcl_c*Ts_MPC Ccl_d = Ccl_c Dcl_d = Dcl_c x0_cl = np.array([0,0,phi0]) M_cl = LinearStateSpaceSystem(A=Acl_d, B=Bcl_d, C=Ccl_d, D=Dcl_d, x0=x0_cl) # MPC parameters Np = get_parameter(sim_options, 'Np') Nc = get_parameter(sim_options, 'Nc') Qx = get_parameter(sim_options, 'Qx') QxN = get_parameter(sim_options, 'QxN') Qr = get_parameter(sim_options, 'Qr') QDr = get_parameter(sim_options, 'QDr') # Constraints #xmin = np.array([-1.5, -100, -100]) #xmax = np.array([1.5, 100.0, 100]) #umin = np.array([-10]) #umax = np.array([10]) #Dumin = np.array([-100 * Ts_MPC_def]) #Dumax = np.array([100 * Ts_MPC_def]) QP_eps_rel = get_parameter(sim_options, 'QP_eps_rel') QP_eps_abs = get_parameter(sim_options, 'QP_eps_abs') # Emergency exit conditions EMERGENCY_STOP = False EMERGENCY_POS = 2.0 EMERGENCY_ANGLE = 30 * DEG_TO_RAD # Reference input and states xref_cl_fun = get_parameter(sim_options, 'xref_cl_fun') # reference state xref_cl_fun_v = np.vectorize(xref_cl_fun, signature='()->(n)') t0 = 0 xref_MPC = xref_cl_fun(t0) uref = get_parameter(sim_options, 'uref') uminus1 = np.array([0.0]) # input at time step negative one - used to penalize the first delta u at time instant 0. Could be the same as uref. kMPC = MPCController(Acl_d, Bcl_d, Np=Np, Nc=Nc, x0=x0_cl, xref=xref_MPC, uminus1=uminus1, Qx=Qx, QxN=QxN, Qu=Qr, QDu=QDr, eps_feas=1e3, eps_rel=QP_eps_rel, eps_abs=QP_eps_abs) try: kMPC.setup(solve=True) # setup initial problem and also solve it except: EMERGENCY_STOP = True if not EMERGENCY_STOP: if kMPC.res.info.status != 'solved': EMERGENCY_STOP = True # In[initialize PID] # Default controller parameters - P = -100.0 I = -1 D = -20 N = 100.0 kP = control.tf(P,1, Ts_PID) kI = I*Ts_PID*control.tf([0, 1], [1,-1], Ts_PID) kD = D*control.tf([N, -N], [1.0, Ts_PID*N - 1], Ts_PID) PID_tf = kP + kD + kI PID_ss = control.ss(PID_tf) k_PID = LinearStateSpaceSystem(A=PID_ss.A, B=PID_ss.B, C=PID_ss.C, D=PID_ss.D) # In[initialize noise] # Standard deviation of the measurement noise on position and angle std_npos = get_parameter(sim_options, 'std_npos') std_nphi = get_parameter(sim_options, 'std_nphi') # Force disturbance std_dF = get_parameter(sim_options, 'std_dF') # Disturbance power spectrum w_F = get_parameter(sim_options, 'w_F') # bandwidth of the force disturbance tau_F = 1 / w_F Hu = control.TransferFunction([1], [1 / w_F, 1]) Hu = Hu * Hu Hud = control.matlab.c2d(Hu, Ts_PID) N_sim_imp = tau_F / Ts_PID * 20 t_imp = np.arange(N_sim_imp) * Ts_PID t, y = control.impulse_response(Hud, t_imp) y = y[0] std_tmp = np.sqrt(np.sum(y ** 2)) # np.sqrt(trapz(y**2,t)) Hu = Hu / (std_tmp) * std_dF N_skip = int(20 * tau_F // Ts_PID) # skip initial samples to get a regime sample of d t_sim_d = get_parameter(sim_options, 'len_sim') # simulation length (s) N_sim_d = int(t_sim_d // Ts_PID) N_sim_d = N_sim_d + N_skip + 1 e = np.random.randn(N_sim_d) te = np.arange(N_sim_d) * Ts_PID _, d, _ = control.forced_response(Hu, te, e) d = d.ravel() # Simulate in closed loop len_sim = get_parameter(sim_options, 'len_sim') # simulation length (s) nsim = int(len_sim // Ts_MPC) #int(np.ceil(len_sim / Ts_MPC)) # simulation length(timesteps) # watch out! +1 added, is it correct? t_vec = np.zeros((nsim, 1)) status_vec = np.zeros((nsim,1)) x_vec = np.zeros((nsim, nx)) x_ref_vec = np.zeros((nsim, ncl_x)) y_vec = np.zeros((nsim, ny)) y_meas_vec = np.zeros((nsim, ny)) u_vec = np.zeros((nsim, nu)) x_model_vec = np.zeros((nsim,3)) nsim_fast = int(len_sim // Ts_PID) t_vec_fast = np.zeros((nsim_fast, 1)) x_vec_fast = np.zeros((nsim_fast, nx)) # finer integration grid for performance evaluation ref_phi_vec_fast = np.zeros((nsim_fast, 1)) y_meas_vec_fast = np.zeros((nsim_fast, ny)) x_ref_vec_fast = np.zeros((nsim_fast, nx)) # finer integration grid for performance evaluatio u_vec_fast = np.zeros((nsim_fast, nu)) # finer integration grid for performance evaluatio Fd_vec_fast = np.zeros((nsim_fast, nu)) # t_int_vec_fast = np.zeros((nsim_fast, 1)) emergency_vec_fast = np.zeros((nsim_fast, 1)) # t_step = t0 x_step = x0 u_PID = None t_pred_all = t0 + np.arange(nsim + Np + 1) * Ts_MPC Xref_MPC_all = xref_cl_fun_v(t_pred_all) for idx_fast in range(nsim_fast): ## Determine step type: fast simulation only or MPC step idx_MPC = idx_fast // ratio_Ts run_MPC_controller = (idx_fast % ratio_Ts) == 0 y_step = Cd.dot(x_step) # y[i] from the system ymeas_step = np.copy(y_step) ymeas_step[0] += std_npos * np.random.randn() ymeas_step[1] += std_nphi * np.random.randn() y_meas_vec_fast[idx_fast,:] = ymeas_step # Output for step i # Ts_MPC outputs if run_MPC_controller: # it is also a step of the simulation at rate Ts_MPC if idx_MPC < nsim: t_vec[idx_MPC, :] = t_step y_vec[idx_MPC,:] = y_step y_meas_vec[idx_MPC,:] = ymeas_step u_vec[idx_MPC, :] = u_PID x_model_vec[idx_MPC, :] = M_cl.x.ravel() xref_MPC = xref_cl_fun(t_step) x_ref_vec[idx_MPC,:] = xref_MPC.ravel() if not EMERGENCY_STOP: phi_ref_MPC, info_MPC = kMPC.output(return_status=True) # u[i] = k(\hat x[i]) possibly computed at time instant -1 else: phi_ref_MPC = np.zeros(nu) # PID angle CONTROLLER ref_phi = phi_ref_MPC.ravel() error_phi = ref_phi - ymeas_step[1] u_PID = k_PID.output(error_phi) u_PID[u_PID > 10.0] = 10.0 u_PID[u_PID < -10.0] = -10.0 u_TOT = u_PID # Ts_fast outputs t_vec_fast[idx_fast,:] = t_step x_vec_fast[idx_fast, :] = x_step #system_dyn.y u_vec_fast[idx_fast,:] = u_TOT Fd_vec_fast[idx_fast,:] = 0.0 ref_phi_vec_fast[idx_fast,:] = ref_phi ## Update to step i+1 k_PID.update(error_phi) # Controller simulation step at rate Ts_MPC if run_MPC_controller: M_cl.update(ref_phi) if not EMERGENCY_STOP: x_cl = np.array([x_step[0], x_step[1], x_step[2]]) Xref_MPC = Xref_MPC_all[idx_MPC:idx_MPC + Np + 1] xref_MPC = Xref_MPC_all[idx_MPC] kMPC.update(x_cl, phi_ref_MPC, xref=xref_MPC) # update with measurement and reference # System simulation step at rate Ts_fast time_integrate_start = time.perf_counter() system_dyn.set_f_params(u_TOT) system_dyn.integrate(t_step + Ts_PID) x_step = system_dyn.y t_int_vec_fast[idx_fast,:] = time.perf_counter() - time_integrate_start # Time update t_step += Ts_PID simout = {'t': t_vec, 'x': x_vec, 'u': u_vec, 'y': y_vec, 'y_meas': y_meas_vec, 'x_ref': x_ref_vec, 'status': status_vec, 'Fd_fast': Fd_vec_fast, 't_fast': t_vec_fast, 'x_fast': x_vec_fast, 'x_ref_fast': x_ref_vec_fast, 'u_fast': u_vec_fast, 'y_meas_fast': y_meas_vec_fast, 'emergency_fast': emergency_vec_fast, 'PID_tf': PID_tf, 'Ts_MPC': Ts_MPC, 'ref_phi_fast': ref_phi_vec_fast, 'x_model': x_model_vec, 't_int_fast': t_int_vec_fast } return simout