Model-based design in Julia: Composing components with ModelingToolkit.jl
Published:
The ModelingToolkit.jl is a way to easily build models of physical systems and then simulate those models. It comes with a standard library : ModelingToolkit.jl which contains various components to model physical systems such as electrical circuits and mechanics.
In this blog article I will explore how to build up new ModelingToolkit components from existing components.
I will take the DC motor example from the sciml.ai website and turn the DC motor into a component that can be used in simulations.
First we’ll define the DC motor component:
@component function DcMotor(; name, R=0.5,L=4.5e-3,k=0.5,J=0.02,f=0.01)
@named p = Pin()
@named n = Pin()
@named flange = Flange()
@named support = Support()
@named R1 = Resistor(R = R)
@named L1 = Inductor(L = L)
@named emf = EMF(k = k)
@named inertia = Inertia(J = J)
@named friction = Damper(d = f)
connections = [
connect(support, emf.support, friction.flange_b)
connect(emf.flange, friction.flange_a, inertia.flange_a)
connect(inertia.flange_b, flange)
connect(p, R1.p)
connect(R1.n, L1.p)
connect(L1.n, emf.p)
connect(emf.n, n)
]
systems = [
p,
n,
flange,
support,
R1,
L1,
emf,
inertia,
friction,
]
ODESystem(connections, t, [], [], systems = systems, name = name)
end
We can then use this component in a simulation like this:
using ModelingToolkit
using ModelingToolkitStandardLibrary.Electrical
using ModelingToolkitStandardLibrary.Mechanical.Rotational
using ModelingToolkitStandardLibrary.ElectroMechanical.Rotational
using ModelingToolkitStandardLibrary.Blocks
using OrdinaryDiffEq
using Plots
@parameters t
R = 0.5 # [Ohm] armature resistance
L = 4.5e-3 # [H] armature inductance
k = 0.5 # [N.m/A] motor constant
J = 0.02 # [kg.m²] inertia
f = 0.01 # [N.m.s/rad] friction factor
tau_L_step = -0.3 # [N.m] amplitude of the load torque step
@named ground = Ground()
@named source = Voltage()
@named ref = Blocks.Step(height=1, start_time=0)
@named pi_controller = Blocks.LimPI(k=1.1, T=0.035, u_max=10, Ta=0.035)
@named feedback = Blocks.Feedback()
@named fixed = Fixed()
@named load = Torque(use_support=false)
@named load_step = Blocks.Step(height=tau_L_step, start_time=3)
@named speed_sensor = SpeedSensor()
@named motor = DcMotor()
connections = [connect(fixed.flange, motor.support)
connect(motor.flange, load.flange)
connect(motor.flange, speed_sensor.flange)
connect(load_step.output, load.tau)
connect(ref.output, feedback.input1)
connect(speed_sensor.w, :y, feedback.input2)
connect(feedback.output, pi_controller.err_input)
connect(pi_controller.ctr_output, :u, source.V)
connect(source.p, motor.p)
connect(motor.n, source.n, ground.g)]
@named model = ODESystem(connections, t,
systems=[
motor,
ground,
ref,
pi_controller,
feedback,
source,
fixed,
load,
load_step,
speed_sensor,
])
sys = structural_simplify(model)
prob = ODEProblem(sys, [], (0, 6.0))
sol = solve(prob, Rodas4())
plot(sol)