# Making a PD Controller Component in ModelingToolkit.jl

**URL:** <https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509>\
**Category:** Modelling & Simulations\
**Created:** [April 3, 2021, 4:51pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509 "2021-04-03T16:51:39Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 3, 2021, 4:51pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/1 "2021-04-03T16:51:39Z")

</div>

If I were to make a PD controller component, I’d probably do something like this:

```julia
using ModelingToolkit
function PD_controller(Pgain, Dgain, num_inputs; name=:pd)
    @parameters t
    D = Differential(t)
    @variables u[1:num_inputs](t)
    @variables o[1:num_inputs](t)

    eqs = o .~ Pgain.*(u) + Dgain.* D.(u)
    return ODESystem(eqs, t, vcat(u,o), []; name = name)
end

```

When I hook up an actual control system involving a PD controller, I cant `structural_simplify` the system. (It’s fine without the derivative component). The stacktrace is

> … Internal error in Tearing.jl: vs[1] = 0.

I guess this is because the component needs an explicit equation for D(u), which it doesn’t have. Now in my case, `D(u)` is defined, since `u ~ another_component.x` , where `another_component.x` has its dynamics defined. But it’s defined in the equations for a different component. Is there a way for `structural_simplify` to be extended to deal with such cases, or is there a particular coding pattern that allows for it without requiring the PD controller to receive `D(u)` as a separate input?

Thanks in advance! It really feels like ModelingToolkit.jl could become a more flexible, fully differentiable version of Simulink in the near future.

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 3, 2021, 6:24pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/2 "2021-04-03T18:24:35Z")

</div>

Normally, PD controllers are implemented with a filter to make them proper. Does that help? In other words:

u(s) = K\_\mathrm{p}\frac{1+T\_\mathrm{d}s}{1+\gamma T\_\mathrm{d}s}\cdot e(s)

with typical value \gamma = 0.1, instead of the “pure” PD controller:

u(s) = K\_\mathrm{p}(1+T\_\mathrm{d}s)\cdot e(s)

---

<div class="post-metadata">

**Author:** ![zdenek\_hurak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zdenek_hurak/32/53118_2.png) [@zdenek\_hurak](https://discourse.julialang.org/u/zdenek_hurak)\
**Post date:** [April 3, 2021, 7:04pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/3 "2021-04-03T19:04:51Z")

</div>

Indeed.

I will also add that while the filtering part makes the controller proper, which is plausible from a mathematical/fundamental viewpoint, it also does the very practical job of _filtering_, which is certainly plausible from an engineering viewpoint.

And while discussing these practical aspects, it is perhaps worth mentioning that in practical implementations it is often wise to apply the (filtered) derivative part of the controller to the measured plant outputs `y` only. The proportional (and possibly the integration part, if present) of the controller are applied to the regulation error `e = r-y` (sometimes just the integral part is applied to the regulation error). The reason is that if there are abrupt changes in the reference signal `r` (Heaviside steps), the derivative part gets immediately saturated. Some discussion of this is in chapter 11.5 of [http://www.cds.caltech.edu/~murray/books/AM08/pdf/fbs-public\_24Jul2020.pdf](http://www.cds.caltech.edu/~murray/books/AM08/pdf/fbs-public_24Jul2020.pdf) (paragraph on Setpoint weighting) or elsewhere.

---

<div class="post-metadata">

**Author:** ![zdenek\_hurak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zdenek_hurak/32/53118_2.png) [@zdenek\_hurak](https://discourse.julialang.org/u/zdenek_hurak)\
**Post date:** [April 3, 2021, 7:14pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/4 "2021-04-03T19:14:57Z")

</div>

> [@Dhruva2](#):
>
> It really feels like ModelingToolkit.jl could become a more flexible, fully differentiable version of Simulink in the near future.

That would be great. Let’s not forget, however, what the key reason for the popularity of Simulink in the control systems community is (and I really mean not just the academic community but also at least some industries, certainly the automotive one). It is certainly not (just) the convenience and/or performance of its numerical solvers for this and that. There are other software tools – commercial and FOSS – that offer competitive features and performance for the mere control design related computations. But what matters is that from a controller designed in Matlab and implemented in Simulink an engineer can generate a code optimized for running in real time on a wide selection of target platforms.

---

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 4, 2021, 8:21am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/5 "2021-04-04T08:21:13Z")

</div>

Thanks, yes, that is a good point! I guess the PD controller I made was just a minimal example highlighting the issue I was asking about:

Component A has input u. The dynamics of component A depend upon du/dt.

du/dt is defined in the equations for Component X, but not component A. One could explicitly feed du/dt as an extra input to component X if necessary. But it would be nice if one could just take the derivative of u inside component A, and when connecting the systems and using `structural_simplify`, the simplification algorithm realises that du/dt is defined elsewhere, and hence the differential equations are complete and can compile.

---

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 4, 2021, 8:21am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/6 "2021-04-04T08:21:52Z")

</div>

Thanks for the insight and the references

---

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 4, 2021, 8:23am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/7 "2021-04-04T08:23:00Z")

</div>

Yes definitely, I’m speaking with the bias of somebody who isn’t implementing code on a target platform in the wild!

---

<div class="post-metadata">

**Author:** ![runjaj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/runjaj/32/23628_2.png) [@runjaj](https://discourse.julialang.org/u/runjaj)\
**Post date:** [April 4, 2021, 9:30am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/8 "2021-04-04T09:30:50Z")

</div>

This is my naive version of a classic PID:

```julia
function controlPID(;name)
    @parameters Kc τI τD
    @variables ym(t) ε(t) c(t) x(t)
    eqs = [
        D(ym) ~ expand_derivatives(D(sp)) - (c - Kc*ε - Kc/τI*x)/(Kc*τD),
        D(x) ~ ε
    ]
    ODESystem(eqs, name=name)
end

```

The error is defined as \varepsilon = y\_{sp} -y\_m. To avoid calculating \frac{\mathrm{d} \varepsilon}{\mathrm{d}t}, I differentiated the error \frac{\mathrm{d} \varepsilon}{\mathrm{d}t} = \frac{\mathrm{d} y\_{sp}}{\mathrm{d}t} -\frac{\mathrm{d} y\_m}{\mathrm{d}t}.

c is the output of the controller.

I let ModelingToolkit to differentiate y\_{sp}.

I hope that everything is cleat, if not I can share a more complete example.

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 4, 2021, 9:52am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/9 "2021-04-04T09:52:44Z")

</div>

I haven’t started looking too much into the ModelingToolkit yet, so I’m curious as to how you connect it to a simple systems in a feedback configuration?

- Where is the setpoint (`sp`) provided?
- Where is the control error (`ε`) defined?

A simple example with, say, controlling a second order system, would be very useful for me and others to try to set aside time to absorb the possibilities of ModelingToolkit :-).

---

<div class="post-metadata">

**Author:** ![runjaj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/runjaj/32/23628_2.png) [@runjaj](https://discourse.julialang.org/u/runjaj)\
**Post date:** [April 4, 2021, 4:40pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/10 "2021-04-04T16:40:55Z")

</div>

This is a long step-by-step tutorial to simulate a PID loop. Keep in mind that my knowledge of Julia and ModelingToolkit is very limited. I’m pretty sure that there are a lot of things to be improved.

You can use Pluto to run this tutorial.

First, we will load the libraries:

```julia
using ModelingToolkit, DifferentialEquations, Plots

```

Then, we will define time t, ~~as a parameter of the simulation and a couple of functions that we will need:~~ error \varepsilon(t) and setpoint y\_{sp}(t) as variables of the simulation:

```julia
## Removed to define t as a variable
## @parameters t
@variables t, ε(t), ysp(t)
D = Differential(t)

```

As set point we can use any function, like Heaviside, sin, or something more complex:

```julia
# Set-point function:

# sp = t*(1-0.5*(1+tanh((t-5)/.1)))

sp = 1

```

The control loop will have the following elements:

- Summing point, to calculate the error: \varepsilon = y\_{sp} - y\_m.
- PID controller: K\_c = 10. \tau\_I = 2., and \tau\_D = 0.1
- Plant: First order (K\_p = 2 and \tau\_p = 10)
- Sensor: First order (K\_m = 1 and \tau\_m = 1)

We set up all these parameters:

```julia
# Simulation parameters

begin
	Kp = 2
	τp = 10
	Km = 1
	τm = 1
	Kc = 10.
	τI = 2.
	τD = 0.1

	tend = 20.
end

```

Now, we will define a generic function of a first order system:

```julia
# First order system: G(s) = y(t)/f(s) = K/(τ*s +1)
# τ dy(t)/dt +y(t) = K f(t)

function firstOrder(;name)
    @parameters K τ
    @variables f(t) y(t)
    eqs = [
        D(y) ~ (K*f - y)/τ
        ]
    ODESystem(eqs, name=name)
end

```

And we will define the PID controller:

```julia
# PID controller: Gc(s) = c(s)/ε(s) = Kc*(1 + 1/(τI*s) + τD*s)
# c(t) = Kc*(ε(t) + 1/τI*∫ε(t)dt + τD*dε(t)/dt
# x(t) = ∫ε(t)dt
# ε(t) = ysp(t) - ym(t)

function controlPID(;name)
    @parameters Kc τI τD
    @variables ym(t) ε(t) c(t) x(t)
    eqs = [
        D(ym) ~ expand_derivatives(D(sp)) - (c - Kc*ε - Kc/τI*x)/(Kc*τD),
        D(x) ~ ε
    ]
    ODESystem(eqs, name=name)
end

```

The next step is setting up the plant, sensor and controller:

```julia
# Process (Plant): First order system
# Input: c(t)
# Output: y(t)

@named proc = firstOrder()

```

```julia
# Sensor: First order system
# Input: y(t)
# Output: ym(t) 

@named sensor = firstOrder()

```

```julia
# PID controller:
# Inputs: ysp(t), ym(t)
# Output: c(t)

@named control_PID = controlPID()

```

After defining all the elements of our control loop, we will define the loop itself:

```julia
@named loopPID = ODESystem([
        sensor.f ~ proc.y,
        proc.f ~ control_PID.c,
        control_PID.ε ~ ε,
        control_PID.ym ~ sensor.y,
        ε ~ ysp - sensor.y,
        ysp ~ sp
        ], t, systems=[proc, sensor, control_PID]);

```

I think that the name of the variables is clear. If not, I can prepare a drawing to make them clearer.

Now we have defined our system, but is a DAE and it has more equations than necessary. We can use ModelingToolkit to make all the simplifications:

```julia
sys = structural_simplify(dae_index_lowering(loopPID))

```

We have almost finished. We need to define the initial conditions and set up the parameters of the loop:

```julia
# Initial conditions

u0_PID = [
    proc.y => 0.0,
    proc.f => .0,
    sensor.y => 0.0,
    sensor.f => 0.0,
    control_PID.c => 0.0,
    control_PID.ε => 0,
    control_PID.x => 0,
    control_PID.ym => 0,
    ε => 0
    ]

```

```julia
p_PID = [
    proc.τ => τp,
    proc.K => Kp,
    sensor.τ => τm,
    sensor.K => Km,
    control_PID.Kc => Kc,
    control_PID.τI => τI,
    control_PID.τD => τD
    ]

```

We have everything set, so we can ask ModelingToolkit to create the problem:

```julia
probPID = ODEProblem(sys, u0_PID, (0.0, tend), p_PID)

```

Finally, we solve the problem:

```julia
solPID = solve(probPID, ImplicitEuler())

```

Done!

We can plot the solution easily:

```julia
plot(solPID)

```

If we want to choose which variable to be plotted:

```julia
plot(solPID, vars=[proc.y,sensor.y])

```

We can test if the solution is right. To do it, we will use ControlSystems.jl:

```julia
using ControlSystems
s = tf("s")
Gp = Kp/(τp*s+1)
Gm = Km/(τm*s+1)
Gc = Kc*(1 + 1/(τI*s) + τD*s)
G = Gc*Gp/(1+Gc*Gp*Gm)

begin
	stepplot(G, tend)
	plot!(solPID, vars=[proc.y])
end

```

Please let me know which points are not clear or which ones could be improved and I will edit this post.

---

<div class="post-metadata">

**Author:** ![zdenek\_hurak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zdenek_hurak/32/53118_2.png) [@zdenek\_hurak](https://discourse.julialang.org/u/zdenek_hurak)\
**Post date:** [April 4, 2021, 5:11pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/11 "2021-04-04T17:11:27Z")

</div>

> [@runjaj](#):
>
> Then, we will define time _t_ as a parameter of the simulation… :
> 
> ```julia
> @parameters t
> @variables ε(t), ysp(t)
> 
> ```

I find it confusing to regard (and declare) the time as a _parameter_. The parameters are constant throughout the simulation, aren’t they? Shouldn’t the time _t_ be better viewed as a _variable_? The example at [https://mtk.sciml.ai/stable/tutorials/ode\_modeling/](https://mtk.sciml.ai/stable/tutorials/ode_modeling/) suggests so. Note also that the issue has been filed [Comment on terminology: parameter vs. (independent) variable t · Issue #564 · SciML/ModelingToolkit.jl · GitHub](https://github.com/SciML/ModelingToolkit.jl/issues/564#issuecomment-796651810).

---

<div class="post-metadata">

**Author:** ![runjaj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/runjaj/32/23628_2.png) [@runjaj](https://discourse.julialang.org/u/runjaj)\
**Post date:** [April 4, 2021, 6:27pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/12 "2021-04-04T18:27:40Z")

</div>

I think you’re right. Probably I learnt that in an old version of ModelingToolkit documentation. I’m going to edit my post to reflect your suggestion .

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 4, 2021, 6:46pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/13 "2021-04-04T18:46:19Z")

</div>

The meaning of `parameter` varies among scientific fields. In Modelica, there are two types of constants: `constant` and `parameter`, where the former is a physics/mathematics constant such as \pi, Planck’s constant, etc., while the latter are constant that vary from one simulation to the next (e.g., geometric quantities, etc.). Thus, in Modelica (and some mathematics fields), a “parameter” is a constant.

In some physics fields, “parameter” means a different thing. As an example, in “distributed parameter system”.

I haven’t really seen a formal definition of `parameter` in ModelingToolkit, but the word seems to include constants as well as independent variables (including time). Possibly also _functions_ of independent variables, but I’m not sure. (That is, according to some older documentation of ModelingToolkit.jl. It is possible that the use of concepts has changed over time.)

The concept of `parameter` doesn’t exists in `Symbolics.jl` (as far as I know), and is an extension of `variables` from `Symbolics.jl` — I think Chris R. mentioned that `parameters` are `variables` with some added meta information, but this is not entirely clear to me.

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 5, 2021, 8:12am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/14 "2021-04-05T08:12:47Z")

</div>

> [@runjaj](#):
>
> The next step is setting up the plant, sensor and controller:
> 
> ```julia
> # Process (Plant): First order system
> # Input: c(t)
> # Output: y(t)
> 
> @named proc = firstOrder() here
> 
> ```

Is there a typo in this sequence? (Should there be a keyword `here` at the end of the last line?)

When I remove the word `here`, your code runs fine in `IJulia`.

---

<div class="post-metadata">

**Author:** ![runjaj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/runjaj/32/23628_2.png) [@runjaj](https://discourse.julialang.org/u/runjaj)\
**Post date:** [April 5, 2021, 9:34am UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/15 "2021-04-05T09:34:12Z")

</div>

Thank you for noticing that mistake. I have removed it.

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 5, 2021, 2:18pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/16 "2021-04-05T14:18:30Z")

</div>

One question: when you create models such as `firstOrder`…

```julia
# First order system: G(s) = y(t)/f(s) = K/(τ*s +1)
# τ dy(t)/dt +y(t) = K f(t)

function firstOrder(;name)
    @parameters K τ
    @variables f(t) y(t)
    eqs = [
        D(y) ~ (K*f - y)/τ
        ]
    ODESystem(eqs, name=name)
end

```

… have you found a way to give default values to your parameters `K` and `τ` that kick in if you don’t specify the value in your `proc`/`sensor` parameter specification…

```julia
p_PID = [
    proc.τ => τp,
    proc.K => Kp,
    sensor.τ => τm,
    sensor.K => Km,
    control_PID.Kc => Kc,
    control_PID.τI => τI,
    control_PID.τD => τD
    ]

```

?

---

<div class="post-metadata">

**Author:** ![runjaj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/runjaj/32/23628_2.png) [@runjaj](https://discourse.julialang.org/u/runjaj)\
**Post date:** [April 5, 2021, 3:44pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/17 "2021-04-05T15:44:01Z")

</div>

_Just after Saving my answer, I noticed that I was answering a different question… Sorry! Next time I’ll read the question more carefully._

I have found a way, I’m not sure if it’s the best way but it works.

You can define the parameters for each block of the simulation. First, the plant (process):

```julia
p_PROC = [proc.K => Kp, proc.τ => τp]

```

Then, the sensor:

```julia
p_SENSOR = [sensor.K => Km, sensor.τ => τm]

```

And, finally, the controller:

```julia
p_PID = [
    control_PID.Kc => Kc,
    control_PID.τI => τI,
    control_PID.τD => τD
    ]

```

To define the simulation problem, we need to put them together:

```julia
p_LOOP = union(p_PROC, p_SENSOR, p_PID)

```

So, now the problem is:

```julia
probPID = ODEProblem(sys, u0_PID, (0.0, tend), p_LOOP)

```

We could do the same for the initial conditions.

---

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 5, 2021, 3:52pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/18 "2021-04-05T15:52:22Z")

</div>

I’ll reply more fully too all the comments on this thread a bit later.

For now, what I would say for defaults is:

for each component ODESystem, when you generate the ODESystem, you can add a keyword argument: `defaults = Dict( tau => 1., K => 2.)`, and so on. The entries in this dict comprise either/both initial conditions and parameters. When you connect up the system, the default values are inherited

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [April 5, 2021, 4:02pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/19 "2021-04-05T16:02:35Z")

</div>

Does this mean:

```julia
# First order system: G(s) = y(t)/f(s) = K/(τ*s +1)
# τ dy(t)/dt +y(t) = K f(t)

function firstOrder(;name)
    @parameters K τ
    @variables f(t) y(t)
    eqs = [
        D(y) ~ (K*f - y)/τ
        ]
    ODESystem(eqs, name=name; defaults = Dict(tau => 1., K => 2.))
end

```

?

---

<div class="post-metadata">

**Author:** ![Dhruva2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dhruva2/32/18475_2.png) [@Dhruva2](https://discourse.julialang.org/u/Dhruva2)\
**Post date:** [April 5, 2021, 8:37pm UTC](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509/20 "2021-04-05T20:37:00Z")

</div>

Yes, see ‘Defaults’ section here: [Composing Ordinary Differential Equations · ModelingToolkit.jl](https://mtk.sciml.ai/stable/tutorials/ode_modeling/)

[Next page](https://discourse.julialang.org/t/making-a-pd-controller-component-in-modelingtoolkit-jl/58509.md?page=2)
