Search code examples
pythonnumpyscipypython-control

Python-control - step system


When I create a system using the python-control package:

import control
H = control.tf([1], [1])

And then want to iteratively simulate that system, how do I do it?

I know I can do this:

T = np.arange(0, 10, 0.01)
u = np.sin(T)
y, t, x = control.lsim(H, u, T)

But what I want to do is this:

Tstart = get_current_time()   # returns a scalar
T = get_current_time()
x = None
while T - Tstart < 100:
    u = get_next_input()      # returns a scalar
    T = get_current_time()
    y, x = control.step_system(H, u, T, x)
    do_something_with_output(y)

Is there some way I can do this? How else are you supposed to use a system developed with the control package to, you know, control something?


Solution

  • This is a great question. I am interested in this myself and asked a similar question on the Mathworks forum a while ago and it's not currently possible in MATLAB.

    The good news is, you can now do it in Python Control using the iosys module and the input_output_response function.

    For a linear system as in your example you use the LinearIOSystem class

    Here is my simulation example:

    import time
    import numpy as np
    import matplotlib.pyplot as plt
    import control
    from control import input_output_response
    from control.iosys import LinearIOSystem
    
    # Define system
    # Continuous-time transfer function
    G = control.tf([1], [2, 1])
    
    # Convert to state-space representation
    Gss = control.ss(G)
    
    # Construct IO system
    sys = LinearIOSystem(Gss, inputs='u', outputs='y')
    
    def get_next_input(u, avg_time=0.5):
        """Function to simulate data acquisition"""
        t0 = time.time()
        wait_time = avg_time*(0.5 + np.random.rand())
        while time.time() - t0 < wait_time:
            pass
        if np.random.rand() > 0.8:
            u = u + np.random.randn()
        return u
    
    # Simulate system in response to irregular inputs
    t0 = time.time()
    t = 0
    y0 = 0
    u = 0
    x = np.zeros(sys.nstates)
    np.random.seed(1)
    sim_results = [[0, u, y0]]
    print(sim_results[-1])
    while t < 10:
        u_new, t_new  = get_next_input(u), time.time() - t0
        # Simulation of system up to current time
        T_sim = [t, t_new]
        T_sim, Y_sim, X_sim = input_output_response(sys, T_sim, u, X0=x,
                                                    return_x=True)
        sim_results.append([T_sim[-1], u_new, Y_sim[-1]])
        print(sim_results[-1])
        # Set current state and outputs to end of simulation period
        x = X_sim[0, -1]
        u = u_new
        t = t_new
    
    sim_results = np.array(sim_results)
    t = sim_results[:, 0]
    u = sim_results[:, 1]
    y = sim_results[:, 2]
    
    # Plot inputs and outputs
    plt.subplot(2, 1, 1)
    plt.plot(t, y, 'o-')
    plt.xlabel('t')
    plt.ylabel('y(t)')
    plt.grid()
    plt.subplot(2, 1, 2)
    plt.step(t, u, where='post')
    plt.xlabel('t')
    plt.ylabel('u(t)')
    plt.grid()
    plt.show()
    

    input-output time response of simulated system

    In answer to your final question:

    How else are you supposed to use a system developed with the control package to, you know, control something?"

    I think tools like the MATLAB control module and python-control are intended to be used for the analysis, design and simulation of control systems, not necessarily for their implementation. Depending on your application, usually the final control system implementation is done on specialized hardware and/or software or might be hand-coded in a low-level language like C for example. High-level languages like MATLAB and Python are arguably too unreliable and difficult to maintain/upgrade for them to be attractive solutions in any serious process control or real-world robotics application. But for hobbyists and laboratory experiments they are ideal and so I agree this kind of functionality is useful.