Discrete-time control of a nonlinear continuous-time system #677
Replies: 4 comments 1 reply
-
I have been working for awhile on how to best do this in a general way in python-control. The challenge is that the controller outputs a zero-order-hold “staircase” function that is incompatible with the way variable-step numerical integrators work. (They jump back and forth). I have not yet figured out a way to force any of scipy’s integrators to pick only the time steps you specify, but I am not sure I have exhausted all options yet. In the meantime one way to simulate such a system is to repeatedly simulate your dynamical system (eg tanks2) over short intervals of This is cumbersome and slow though. What I have been working on is a an alternative that entails simple fixed-step Euler integration, at a short dt for accuracy, of the continuous-time plant. This entails creating a discrete-time model of the plant for a short dt, and a function that can create a I’ll post here if I get something working. |
Beta Was this translation helpful? Give feedback.
-
FWIW, here's one approach, in which one calls This is fairly raw; I haven't thought at all about how to apply this to the IOSystem classes. import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# non-linear DE
def de(t, y, u):
return -y - y**3 + u
# controller; discrete or continuous time
def prop_controller(r, y, k):
return k * (r - y)
# desired final time
tf_des = 0.3
# sample period
dt = 1/10
# number of samples
nsamples = max(5, int(np.ceil(tf_des/dt)))
k = 5 # gain
r = 5 # setpoint
y0 = -1 # initial state
# discrete-time state (=output)
yd = np.empty(nsamples)
# discrete-time input
ud = np.empty(nsamples)
# initialize
yd[0] = y0
ud[0] = prop_controller(r, yd[0], k)
for nsample in range(1, nsamples):
# construct new DE function for each sample interval
f = lambda t, y: de(t, y, ud[nsample-1])
sol = solve_ivp(f, [nsample*dt, (nsample+1)*dt], [yd[nsample-1]])
yd[nsample] = sol.y[0,-1]
ud[nsample] = prop_controller(r, yd[nsample], k)
# continuous-time controlled system
def control_de(t, y, r, k):
return de(t, y, prop_controller(r, y, k))
# continuous-time solution
cont_sol = solve_ivp(lambda t, y: control_de(t, y, r, k),
[0, nsamples*dt], [y0])
# compare continous-time and discrete-time
plt.clf()
plt.stairs(yd, np.arange(nsamples+1)*dt, baseline=None)
plt.plot(cont_sol.t, cont_sol.y[0]) |
Beta Was this translation helpful? Give feedback.
-
At the end, I modified the solution by @roryyorke: created a helper function that correctly integrates within sampling time and a wrapper for nlsys that evaluates the model in discrete-time. This will give correct results at sampling times and can be used without further modifications in existing code. If a more detailed solution is needed between sampling times, it is easy to integrate the system using the resulting piece-wise constant closed-loop input and solve_ivp with more data points.
|
Beta Was this translation helpful? Give feedback.
-
Yes, I studied the example in the docs and played originally with it as well. Discrete-time delays can indeed be modeled using state-space formulation. As you mention, the integration precision was less accurate and I wanted the exact results as I had in Simulink. In addition, this proposed solution requires only one small additional function and the whole code can remain the same - it integrates very well with the rest of the python-control package. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I am trying to apply state-feedback control using discrete-time SS controller to a system of two tanks described by differential equations. The process is thus modelled as NonlinearIOSystem without sampling time:
tanks2i_io = control.NonlinearIOSystem(
tanks2i_rhs, None, inputs=('q01','q02'), outputs=('h1', 'h2'),
states=('h1', 'h2'), name='tanks2i')
The controller is discrete-time SS:
fbff_ss_controller = control.NonlinearIOSystem(
None,
lambda t, x, u, params: np.clip(-K @ (u[1:] - hs) + Kw * (u[0] - hs[1]) + q01s, 0.05, 2.0),
inputs=('w', 'h1', 'h2'), outputs=('q01'), name='control', dt = ts)
The resulting closed-loop system (interconnect) does end with compile error:
ValueError: Systems have incompatible timebases
Is there any working example for such a setup or is it not implemented yet? In the latter case, is these any possible workaround?
I attach the full example file.
example.txt
Beta Was this translation helpful? Give feedback.
All reactions