Control

class pathsim.blocks.ctrl.PT1(K=1.0, T=1.0)[source]

Bases: StateSpace

First-order lag element (PT1).

The transfer function is defined as

\[H(s) = \frac{K}{1 + T s}\]

where K is the static gain and T is the time constant.

Example

The block is initialized like this:

pt1 = PT1(K=2.0, T=0.5)
Parameters:
  • K (float) – static gain

  • T (float) – time constant in seconds (must be > 0)

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.PT2(K=1.0, T=1.0, d=1.0)[source]

Bases: StateSpace

Second-order lag element (PT2).

The transfer function is defined as

\[H(s) = \frac{K}{1 + 2 d T s + T^2 s^2}\]

where K is the static gain, T is the time constant (related to the natural frequency by \(\omega_n = 1/T\)) and d is the damping ratio.

The damping ratio d controls the transient behavior:

  • \(d < 1\): underdamped (oscillatory)

  • \(d = 1\): critically damped

  • \(d > 1\): overdamped

Example

The block is initialized like this:

#underdamped second-order system
pt2 = PT2(K=1.0, T=0.1, d=0.3)
Parameters:
  • K (float) – static gain

  • T (float) – time constant in seconds (must be > 0)

  • d (float) – damping ratio (must be >= 0)

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.LeadLag(K=1.0, T1=1.0, T2=1.0)[source]

Bases: StateSpace

Lead-Lag compensator.

The transfer function is defined as

\[H(s) = K \frac{T_1 s + 1}{T_2 s + 1}\]

where K is the static gain, T1 is the lead time constant and T2 is the lag time constant.

  • \(T_1 > T_2\): lead compensator (phase advance)

  • \(T_1 < T_2\): lag compensator (phase lag)

  • \(T_1 = T_2\): pure gain

Example

The block is initialized like this:

#lead compensator
ll = LeadLag(K=1.0, T1=0.5, T2=0.1)
Parameters:
  • K (float) – static gain

  • T1 (float) – lead (numerator) time constant in seconds

  • T2 (float) – lag (denominator) time constant in seconds (must be > 0)

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.PID(Kp=0, Ki=0, Kd=0, f_max=100)[source]

Bases: StateSpace

Proportional-Integral-Differentiation (PID) controller.

The transfer function is defined as

\[H(s) = K_p + K_i \frac{1}{s} + K_d \frac{s}{1 + s / f_\mathrm{max}}\]

where the differentiation is approximated by a high pass filter that holds for signals up to a frequency of approximately f_max.

Internally realized as a linear state space model with two states (differentiator filter state and integrator state).

Note

Depending on f_max, the resulting system might become stiff or ill conditioned! As a practical choice set f_max to 3x the highest expected signal frequency. Since this block uses an approximation of real differentiation, the approximation will not hold if there are high frequency components present in the signal. For example if you have discontinuities such as steps or square waves.

Example

The block is initialized like this:

#cutoff at 1kHz
pid = PID(Kp=2, Ki=0.5, Kd=0.1, f_max=1e3)
Parameters:
  • Kp (float) – proportional controller coefficient

  • Ki (float) – integral controller coefficient

  • Kd (float) – differentiator controller coefficient

  • f_max (float) – highest expected signal frequency

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.AntiWindupPID(Kp=0, Ki=0, Kd=0, f_max=100, Ks=10, limits=[-10, 10])[source]

Bases: PID

Proportional-Integral-Differentiation (PID) controller with anti-windup mechanism (back-calculation).

Anti-windup mechanisms are needed when the magnitude of the control signal from the PID controller is limited by some real world saturation. In these cases, the integrator will continue to accumulate the control error and “wind itself up”. Once the setpoint is reached, this can result in significant overshoots. This implementation adds a conditional feedback term to the internal integrator that “unwinds” it when the PID output crosses some limits. This is pretty much a deadzone feedback element for the integrator.

Mathematically, this block implements the following set of ODEs

\[\begin{split}\begin{align} \dot{x}_1 &= f_\mathrm{max} (u - x_1) \\ \dot{x}_2 &= u - w \end{align}\end{split}\]

with the anti-windup feedback (depending on the pid output)

\[w = K_s (y - \min(\max(y, y_\mathrm{min}), y_\mathrm{max}))\]

and the output itself

\[y = K_p u + K_d f_\mathrm{max} (u - x_1) + K_i x_2\]

Note

Depending on f_max, the resulting system might become stiff or ill conditioned! As a practical choice set f_max to 3x the highest expected signal frequency. Since this block uses an approximation of real differentiation, the approximation will not hold if there are high frequency components present in the signal. For example if you have discontinuities such as steps or square waves.

Example

The block is initialized like this:

#cutoff at 1kHz, windup limits at [-5, 5]
pid = AntiWindupPID(Kp=2, Ki=0.5, Kd=0.1, f_max=1e3, limits=[-5, 5])
Parameters:
  • Kp (float) – proportional controller coefficient

  • Ki (float) – integral controller coefficient

  • Kd (float) – differentiator controller coefficient

  • f_max (float) – highest expected signal frequency

  • Ks (float) – feedback term for back calculation for anti-windup control of integrator

  • limits (array_like[float]) – lower and upper limit for PID output that triggers anti-windup of integrator

class pathsim.blocks.ctrl.RateLimiter(rate=1.0, f_max=100)[source]

Bases: DynamicalSystem

Rate limiter block that limits the rate of change of a signal.

Implements a continuous-time rate limiter as a first-order tracking system with clipped rate of change:

\[\dot{x} = \mathrm{clip}\left(f_\mathrm{max} (u - x),\; -r,\; r\right)\]

where r is the maximum allowed rate and f_max controls the tracking bandwidth when the signal is not rate-limited. The output is the state \(y = x\).

Note

The parameter f_max should be set high enough that the output tracks the input without lag when the rate is within limits.

Example

The block is initialized like this:

#max rate of 10 units/s
rl = RateLimiter(rate=10.0, f_max=1e3)
Parameters:
  • rate (float) – maximum rate of change (positive value)

  • f_max (float) – tracking bandwidth parameter

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.Backlash(width=1.0, f_max=100)[source]

Bases: DynamicalSystem

Backlash (mechanical play) element.

Models the hysteresis-like behavior of mechanical backlash in gears, couplings and other systems with play. The output only tracks the input after the input has moved through the full backlash width.

\[\dot{x} = f_\mathrm{max} \left((u - x) - \mathrm{clip}(u - x,\; -w/2,\; w/2)\right)\]

where w is the total backlash width. Inside the dead zone \(|u - x| \leq w/2\) the output does not move. Once the input pushes past the edge, the output tracks with bandwidth f_max.

Example

The block is initialized like this:

#backlash with 0.5 units of total play
bl = Backlash(width=0.5, f_max=1e3)
Parameters:
  • width (float) – total backlash width (play)

  • f_max (float) – tracking bandwidth parameter when engaged

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}
class pathsim.blocks.ctrl.Deadband(lower=-1.0, upper=1.0)[source]

Bases: Block

Deadband (dead zone) element.

Outputs zero when the input is within the dead zone, and passes the signal shifted by the zone boundary otherwise:

\[\begin{split}y = \begin{cases} u - u_\mathrm{upper} & \text{if } u > u_\mathrm{upper} \\ 0 & \text{if } u_\mathrm{lower} \leq u \leq u_\mathrm{upper} \\ u - u_\mathrm{lower} & \text{if } u < u_\mathrm{lower} \end{cases}\end{split}\]

or equivalently \(y = u - \mathrm{clip}(u,\; u_\mathrm{lower},\; u_\mathrm{upper})\).

Example

The block is initialized like this:

#symmetric dead zone of width 0.2
db = Deadband(lower=-0.1, upper=0.1)
Parameters:
  • lower (float) – lower bound of the dead zone

  • upper (float) – upper bound of the dead zone

input_port_labels = {'in': 0}
output_port_labels = {'out': 0}