shih-hao-tseng / SLSpy

SLSpy provides a Python-based framework to design and simulate model-based control systems, especially for system level synthesis (SLS) methods.
Other
18 stars 1 forks source link

How to find the optimize controller gain's vector? #4

Closed roya-correct-ai closed 4 years ago

roya-correct-ai commented 4 years ago

Hi there

I am wondering how to find the final controller gain (the controller gain after solving the optimization problem) in the SLSpy library, for example in the simple state-space example, is it possible to print the controller gain in the output? When I print the SLS_controller, it gives me the following result, I do not know how to extract the controller gain from that.

<slspy.sls.controller_models.SLS_StateFeedback_FIR_Controller object at 0x000002124B267F98>

I am sorry my question maybe is trivial but I would appreciate it if you guide me. Many thanks in advance

shih-hao-tseng commented 4 years ago

To allow direct application to real systems, SLSpy aims to provide time-domain controller implementations instead of the frequency-domain controller transfer functions, and hence one would need to compute the frequency-domain representation manually when needed. In principle, since the Phi_x and Phi_u are stored as arrays in the controller, one can compute the gain K by Phi_u * Phi_x^{-1}. More specifically, the formula is:

(\sum\limits{tau = 0}^{T} Phi_u[tau] z^{-\tau})(\sum\limits{tau = 0}^{T} Phi_x[tau] z^{-\tau})^{-1}

where T is the FIR horizon of the controller.

roya-correct-ai commented 4 years ago

Thank you for your response. When I print the Phi_u and Phi_x after synthesizer.synthesizeControllerModel (), it gives me all Phi_u and Phi_x arrays (more than one) that can stabilize the closed-loop system. But I need to know the Phi_u and Phi_x array that the SLSpy selects to simulate the result. I mean, the optimum Phi_x and Phi_u array.

shih-hao-tseng commented 4 years ago

There should only be only one Phi_x and Phi_u (the attributes _Phi_x and _Phi_u of the controller, each as an array of numpy ndarrays representing the spectral components). If possible, could you paste the results you see here?

roya-correct-ai commented 4 years ago

Here is the result of Phi_u for FIR_horizon=5 and sim_horizon=5:

[array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])] [array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])] [array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])] [array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])] [array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])] [array([[0., 0.]]), array([[-14.67722575, 6.48169046]]), array([[-7.81209198, 2.87905107]]), array([[-1.38092264, -0.58846473]]), array([[ 5.04673416, -4.15054371]]), array([[11.90122946, -8.04421957]])]

In the code, I print SLS_controller._Phi_u after the following line: SLS_controller = synthesizer.synthesizeControllerModel ()

Could you please guide me on why we have a different array for each sample time? should not be the same? And how I can find the Phi_u from the above result to construct the K=Phi_u * Phi_x^{-1}? Many thanks in advance

shih-hao-tseng commented 4 years ago

Thank you. Could you also show me your system setting or the whole code? In theory, we should only get one copy of _Phi_u, and it seems that here you get multiple copies.

roya-correct-ai commented 4 years ago

Thank you for your response. I found an extra print in the code, that makes the Phi_u is printed in the output at each sample time. So my mistake, but my problem still remains: Could you please guide me, do we have a constant controller's gain K = Phi_u * Phi_x {{- 1} for all sample times? or the controller's gain will be computed at each sample time, so we have different values ​​of K at each sample time? Because when I print the Phi_u the result consists of 5 (based on FIR T) array: [array ([[0., 0.]]), array ([[- 14.67722575, 6.48169046]]), array ([[- 7.81209198, 2.87905107]]), array ([[- 1.38092264, -0.58846473]]) , array ([[5.04673416, -4.15054371]]), array ([[11.90122946, -8.04421957]])] Does it mean that at each sample time, the SLS optimization problem will be computed and we have a new gain K?

shih-hao-tseng commented 4 years ago

Controller synthesis and simulation are two separate phases. I think the "sample time" here might refer to the time step in the simulation? In that case, SLS only runs once, before the simulation, and hence does not repeat itself for each time step.

There are two different horizons in the program, the FIR horizon for the synthesis algorithm and the simulation horizon for simulation. The simulation horizon refers to the maximum time steps, i.e., how long we should run the feedbacked system. On the other hand, the FIR horizon is used in the synthesis algorithm to regulate the length of the finite impulse response of Phi_x and Phi_u.

Back to our current case, the result

[array ([[0., 0.]]), array ([[- 14.67722575, 6.48169046]]), array ([[- 7.81209198, 2.87905107]]), array ([[- 1.38092264, -0.58846473]]) , array ([[5.04673416, -4.15054371]]), array ([[11.90122946, -8.04421957]])]

represents the Phi_u with the z-transform:

[[0., 0.]] + [[- 14.67722575, 6.48169046]] z^{-1} + [[- 7.81209198, 2.87905107]] z^{-2} + [[- 1.38092264, -0.58846473]] z^{-3} + [[5.04673416, -4.15054371]] z^{-4} + [[11.90122946, -8.04421957]] z^{-5}

In SLS, we actually build the controller using the component zPhi_u, which has a finite impulse response of length 5, as required. Similarly, we can obtain Phi_x accordingly.

Together, the controller gain, or the frequency-domain transfer function, K can be computed by Phi_u * Phi_x^{- 1}, which remains the same once synthesized.

When running the simulation, we plug the controller into the system and simulate the feedbacked system dynamics, i.e., the time-domain results of the following frequency-domain equations: zx = Ax + Bu + w u = Kx where x (state), u (control), and w (noise) are signals.

roya-correct-ai commented 4 years ago

Thank you so much. Now it is totally clear for me. Much appreciated.

sorry for bothering you, I have another question. Suppose that I use the state-space-example.py and find a controller for a specific system. Now if I want to change only the system, to see whether the previous controller can stabilize the new system matrices, could you please let me know how should I change the code to use the previous controller for the new system? Should I save the Phi_u and Phi_x, then run the code with new matrices by using the previous Phi_u and Phi_x?

shih-hao-tseng commented 4 years ago

Great question. SLSpy is intentionally designed to separate synthesis and simulation phases so that the users do not need to handle Phi_x and Phi_u themselves to evaluate different synthesis methods. We can simply create another system and let the simulator take the new system by simulator.setSystem(system=\<new system>) before simulator.run()

More specifically, let SLS_controller be the controller given by synthesizer.synthesizeControllerModel () and new_system be the new system of interest, do

simulator.setSystem(system=new_system) simulator.setController(controller=SLS_controller) # this line can be omitted if the controller has been set before x_history, y_history, z_history, u_history, w_history = simulator.run()

roya-correct-ai commented 4 years ago

Thank you so much, great solution.

Another question, can I change my plant during the simulation process? For example for the time between 0 and 10 seconds, the code simulates the plant_1 closed-loop, and then after time 10, the plant is switched to plant_2 with the same controller. Could you please guide me on how should I change the code or library to achieve this scenario?

roya-correct-ai commented 4 years ago

Dear Shih

In my case, Nx=2, Nu=1, and Nw=2. Could you please guide me on why the dimension of Phi_u arrays are 2-by-1 and the dimension of Phi_x arrays are 2-by-2? Shouldn't Phi_u and Phi_x have the same dimension? Many thanks in advance

shih-hao-tseng commented 4 years ago

Absolutely, let's analyze that simulation task from the high-level perspective: We want to run the simulation with the original system (plant_1) and the controller (say, SLS_controller) for 10 time units (presumably in seconds), and then we switch to the plant_2. Without initializing the controller, we continue the simulation with an initialized plant_2 and run for N time units. Therefore, the code would be:

simulator.setSystem(system=plant_1) simulator.setController(controller=SLS_controller) simulator.setHorizon(horizon=10) # the results for the first 10 time units x_history, y_history, z_history, u_history, w_history = simulator.run()

plant_2.initialize() # initialize the plant in case the plant was used before simulator.setSystem(system=plant_2) simulator.setHorizon(horizon=N) # the results for the following N time units x_history, y_history, z_history, u_history, w_history = simulator.run(initialize=False)

For the dimensions, in SLS, the system response maps noise to state/control. Therefore, Phi_x is Nx by Nw (2-by-2) and Phi_u is Nu by Nw (1-by-2).

roya-correct-ai commented 4 years ago

Dear Shih, thank you so much for your valuable guidance.