def test_get_state_matrices(self): """Tests the get_state_matrices function.""" # Define constants K_list = [1, 3, 10] tau_list = [0.1, 0.3, 1] delay_list = [0.01, 0.1, 0.3, 1] param_list = set(itertools.chain(*[zip(K_list, x, y) for x in itertools.permutations(tau_list, len(K_list)) for y in itertools.permutations(delay_list, len(K_list))])) for K, tau, td in param_list: den = tau * td ** 2 A_exp = np.array([[0, 1, 0], [0, 0, 1], [-12 / den, -(6 * td + 12 * tau) / den, -(6 * tau + td) / tau / td]]) B_exp = np.array([[0], [0], [12 / den]]) C_exp = np.array([[K, -K * td / 2, K * td ** 2 / 12]]) A, B, C = sim_functions.get_state_matrices(K, tau, td) self.assertListEqual(A.tolist(), A_exp.tolist(), "A matrix does not match expected.") self.assertListEqual(B.tolist(), B_exp.tolist(), "B matrix does not match expected.") self.assertListEqual(C.tolist(), C_exp.tolist(), "C matrix does not match expected") def test_get_second_ord_matrices(self): """Tests the sim_functions.get_second_ord_matrices method.""" pass
def setUp(self): """Run repeated setup code once.""" # Define class and local parameters unittest.TestCase.setUp(self) self.K_list = [3, 10, 99.21] self.tau_list = [0.3, 1] self.delay_list = [0.03, 0.1, 0.3] self.t_step = 0.01 self.stop_time = 20.0 self.input_val = 1.0 self.t_step_ctrl = 0.1 self.zeta = 0.7054 # air MFC damping ratio self.wn = 1.3501 # air MFC natural frequency, rad/s self.td = 1.8 # air MFC time delay, sec self.y0_1 = [0] self.y0_2 = [0] * 3 # for first-order model self.y0_3 = [0] * 4 # for second-order model A, B, self.C = sim_functions.get_state_matrices( self.K_list[1], self.tau_list[-1], self.delay_list[0]) A2, B2, self.C2 = sim_functions.get_second_ord_matrices( self.K_list[-1], self.zeta, self.wn, self.td) self.Kp_list = [4.0, 5.0] self.mass_flow_des = [4.0, 2.0] self.std = 1.0 self.Ks = 2.0 # filter static gain (?) Q = 1e-5 * np.identity(A2.shape[0]) # process noise covariance R = self.std # measurement noise covariance self.P = Q # initial estimate of error covariance self.offset = 0.0 mean = 0.0 # Define objects self.test_mfc1 = lab_hardware.MFC( lambda t, y, u: test_ode(t, y, u, self.K_list[1], self.tau_list[-1] ), lambda x: x[0], self.y0_1) self.test_mfc2 = lab_hardware.MFC( lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, self.C), self.y0_2) self.test_mfc3 = lab_hardware.MFC( lambda t, y, u: sim_functions.system_state_update(t, y, u, A2, B2), lambda y: sim_functions.system_output(y, self.C2), self.y0_3) self.mfc_list = [ lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, K, tau), lambda x: x[0], self.y0_1) for K, tau in zip(self.K_list, self.tau_list) ] self.test_KF = lab_hardware.KalmanFilter(A2, B2, self.Ks * self.C2, Q, R, self.P) self.test_sensor = lab_hardware.StaticSensor( lambda y: sim_functions.static_model(y, self.Ks, self.offset, mean, self.std), 1.0)
def run_simulation(): """Run a simulation of the controller.""" # Define system parameters p_atm = 14.0 # psi t_step = 0.01 # simulation time step, seconds t_step_ctrl = 0.1 # controller update rate, seconds K = 0.1 # static pressure sensor gain, V/psi (?) offset = 1.0 # static pressure sensor offset, V (?) K_mfcs = [99.21, 7.968, 8.007] # MFC gains: [air, pilot, outer], SLPM/V zeta_mfcs = [0.7054, 0.6, 0.6] # MFC damping ratios: [air, pilot, outer] wn_mfcs = [1.3501, 1, 1] # MFC natural frequencies: [air, pilot, outer], rad/sec td_mfcs = [1.8, 0.139, 0.306] # MFC time delays: [air, pilot, outer], sec K_mid = 7.964 # middle fuel MFC static gain, SLPM/V tau_mid = 0.516 # middle fuel MFC time constant, sec (first-order) td_mid = 0.194 # middle fuel MFC time delay, sec y0 = [0.0, 0.0, 0.0, 0.0] # initial value, 2nd order sys with 3,2 order Pade approximation of time delay mfc_list = [] control_law_list = [] Q = np.ndarray([[100, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0] [0, 0, 0, 1]]) R = np.ndarray([[1]]) sensor_locations = [43.81, 76.2, 236.728, 609.6] # dynamic P sensor locations from base, mm # Initialize sensor list sensor_list = [lab_hardware.StaticSensor(model=lambda y: sim_functions.static_model(y, K, offset), location=0.0)] for loc in sensor_locations: sensor_list.append(lab_hardware.DynamicSensor(model=tf1, location=loc)) # TODO figure out sensor transfer functions or models! # Initialize mass flow controller (MFC) list and control law list for K, zeta, wn, td in zip(K_mfcs, zeta_mfcs, wn_mfcs, td_mfcs): A, B, C = sim_functions.get_second_ord_matrices(K, zeta, wn, td) K_lqr = sim_functions.make_lqr_law(A, B, Q, R) mfc_list.append(lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, C), y0)) control_law_list.append(lambda e:-K_lqr.dot(e)) # Insert our first-order-modeled middle fuel MFC A, B, C = sim_functions.get_state_matrices(K_mid, tau_mid, td_mid) K_lqr = sim_functions.make_lqr_law(A, B, Q[:2, :2], R) # smaller Q matrix, only 3 states mfc_list.insert(2, lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, C), y0[:2])) # shorter y0, only 3 states control_law_list.insert(2, lambda e:-K_lqr.dot(e)) # mfc and control law list should be in order: [air, pilot, middle, outer] # Initialize the combustor object combustor = lab_hardware.Combustor(p_atm, t_step, t_step_ctrl, sensor_list, mfc_list, control_law_list)
def setUp(self): """Run repeated setup code once.""" # Define class and local parameters unittest.TestCase.setUp(self) self.K_list = [3, 10, 99.21] self.tau_list = [0.3, 1] self.delay_list = [0.03, 0.1, 0.3] self.t_step = 0.01 self.stop_time = 20.0 self.input_val = 1.0 self.t_step_ctrl = 0.1 self.zeta = 0.7054 # air MFC damping ratio self.wn = 1.3501 # air MFC natural frequency, rad/s self.td = 1.8 # air MFC time delay, sec self.y0_1 = [0] self.y0_2 = [0] * 3 # for first-order model self.y0_3 = [0] * 4 # for second-order model A, B, self.C = sim_functions.get_state_matrices(self.K_list[1], self.tau_list[-1], self.delay_list[0]) A2, B2, self.C2 = sim_functions.get_second_ord_matrices(self.K_list[-1], self.zeta, self.wn, self.td) self.Kp_list = [4.0, 5.0] self.mass_flow_des = [4.0, 2.0] self.std = 1.0 self.Ks = 2.0 # filter static gain (?) Q = 1e-5 * np.identity(A2.shape[0]) # process noise covariance R = self.std # measurement noise covariance self.P = Q # initial estimate of error covariance self.offset = 0.0 mean = 0.0 # Define objects self.test_mfc1 = lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, self.K_list[1], self.tau_list[-1]), lambda x: x[0], self.y0_1) self.test_mfc2 = lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A, B), lambda y: sim_functions.system_output(y, self.C), self.y0_2) self.test_mfc3 = lab_hardware.MFC(lambda t, y, u: sim_functions.system_state_update(t, y, u, A2, B2), lambda y: sim_functions.system_output(y, self.C2), self.y0_3) self.mfc_list = [lab_hardware.MFC(lambda t, y, u: test_ode(t, y, u, K, tau), lambda x: x[0], self.y0_1) for K, tau in zip(self.K_list, self.tau_list)] self.test_KF = lab_hardware.KalmanFilter(A2, B2, self.Ks * self.C2, Q, R, self.P) self.test_sensor = lab_hardware.StaticSensor(lambda y: sim_functions.static_model(y, self.Ks, self.offset, mean, self.std), 1.0)