t = np.array(df_X[COL_T], dtype=np.float32) x = np.array(df_X[COL_X], dtype=np.float32) y = np.array(df_X[COL_Y], dtype=np.float32) y = np.copy(x[:, [0, 2]]) u = np.array(df_X[COL_U], dtype=np.float32) Ts = t[1] - t[0] n_x = x.shape[-1] x0_torch = torch.from_numpy(x[0, :]) # x_noise = np.copy(x) + np.random.randn(*x.shape)*std_noise # x_noise = x_noise.astype(np.float32) y_noise = np.copy(y) + np.random.randn(*y.shape) * std_noise y_noise = y_noise.astype(np.float32) # In[Load model] ss_model = CartPoleStateSpaceModel(Ts) nn_solution = NeuralStateSpaceSimulator(ss_model) #model_name = "model_OE_minibatch_100.pkl" model_name = "model_ARX_FE_nonoise.pkl" nn_solution.ss_model.load_state_dict( torch.load(os.path.join("models", model_name))) # In[Simulation plot] x_torch = torch.tensor(x) x0_torch = torch.tensor(x[0, :]) u_torch = torch.tensor(u) t_torch = torch.tensor(t) with torch.no_grad(): x_sim_torch = nn_solution.f_sim(x0_torch, u_torch) loss = torch.mean(torch.abs(x_sim_torch - x_torch))
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
x_est[:, 2] = y_meas[:, 1] x_est[:, 1] = np.convolve(x_est[:, 0], taps, 'same') x_est[:, 3] = np.convolve(x_est[:, 2], taps, 'same') # Get fit data n_fit = int(len_fit // Ts) time_fit = t[0:n_fit] u_fit = u[0:n_fit] x_est_fit = x_est[0:n_fit] y_meas_fit = y[0:n_fit] batch_size = n_fit // seq_len x_hidden_fit = torch.tensor( x_est_fit, requires_grad=True) # this is an optimization variable! # Setup neural model structure ss_model = CartPoleStateSpaceModel(Ts, init_small=True) nn_solution = NeuralStateSpaceSimulator(ss_model) # Setup optimizer params = list(nn_solution.ss_model.parameters()) + [x_hidden_fit] optimizer = optim.Adam(params, lr=lr) # Batch extraction funtion def get_sequential_batch(seq_len): # Select batch indexes num_train_samples = x_est_fit.shape[0] batch_size = num_train_samples // seq_len - 1 batch_start = np.arange(0, batch_size, dtype=np.int64) * seq_len batch_idx = batch_start[:, np.newaxis] + np.arange( seq_len) # batch all indices
COL_Y = ['p_meas', 'theta_meas'] COL_X = ['p', 'v', 'theta', 'omega'] COL_U = ['u'] COL_R = ['r'] df_X = pd.read_csv(os.path.join("data", "pendulum_data_MPC_ref.csv")) t = np.array(df_X[COL_T], dtype=np.float32) y = np.array(df_X[COL_Y], dtype=np.float32) x = np.array(df_X[COL_X], dtype=np.float32) #u = np.array(df_X[COL_U],dtype=np.float32) u = np.array(df_X[COL_U], dtype=np.float32) Ts = t[1] - t[0] x_noise = x # In[Model] ss_model = CartPoleStateSpaceModel(Ts) model_name = "model_ARX_FE_nonoise.pkl" ss_model.load_state_dict(torch.load(os.path.join("models", model_name))) ss_model_residual = NeuralStateSpaceModel(n_x=4, n_u=1, n_feat=64) nn_solution = NeuralSumODE([ss_model, ss_model_residual]) # In[Setup optimization problem] len_fit = 40 n_fit = int(len_fit // Ts) u_fit = u[0:n_fit] x_fit = x_noise[0:n_fit] t_fit = t[0:n_fit] u_fit_torch = torch.from_numpy(u_fit) x_meas_fit_torch = torch.from_numpy(x_fit)
COL_Y = ['p_meas', 'theta_meas'] COL_X = ['p', 'v', 'theta', 'omega'] COL_U = ['u'] COL_R = ['r'] df_X = pd.read_csv(os.path.join("data", "pendulum_data_MPC_ref.csv")) t = np.array(df_X[COL_T], dtype=np.float32) y = np.array(df_X[COL_Y],dtype=np.float32) x = np.array(df_X[COL_X],dtype=np.float32) #u = np.array(df_X[COL_U],dtype=np.float32) u = np.array(df_X[COL_R],dtype=np.float32) Ts = t[1] - t[0] x_noise = x # In[Model] ss_model = CartPoleStateSpaceModel(Ts)#n_x=4, n_u=1, n_feat=64) nn_solution = NeuralStateSpaceSimulator(ss_model) # In[Setup optimization problem] len_fit = 40 n_fit = int(len_fit//Ts) u_fit = u[0:n_fit] x_fit = x_noise[0:n_fit] t_fit = t[0:n_fit] u_fit_torch = torch.from_numpy(u_fit) x_meas_fit_torch = torch.from_numpy(x_fit) t_fit_torch = torch.from_numpy(t_fit) num_iter = 10000 test_freq = 1
'xref_fun': xref_fun_def, 'uref': np.array([0.0]), # N 'std_npos': 0 * 0.001, # m 'std_nphi': 0 * 0.00005, # rad 'std_dF': 0.1, # N 'w_F': 5, # rad 'len_sim': 40, #s 'Ac': Ac_def, 'Bc': Bc_def, 'Ts_slower_loop': Ts_slower, 'Q_kal': np.diag([0.1, 10, 0.1, 10]), 'R_kal': 1 * np.eye(2), 'seed_val': 42 } ss_model = CartPoleStateSpaceModel(Ts=Ts_slower) nn_solution = NeuralStateSpaceSimulator(ss_model, Ts=Ts_PID) model_name = "model_SS_1step_nonoise.pkl" nn_solution.ss_model.load_state_dict( torch.load(os.path.join("models", model_name))) f_ODE_wrapped = nn_solution.f_ODE def get_parameter(sim_options, par_name): return sim_options.get(par_name, DEFAULTS_PENDULUM_MPC[par_name]) def get_default_parameters(sim_options): """ Which parameters are left to default ??""" default_keys = [ key for key in DEFAULTS_PENDULUM_MPC if key not in sim_options