FurutaMPCMultirateHardware cannot be compiled for the run_ode! route -- the one that steps a
program's clocked partition with an ODE solver against the device so that the MPC's predicted
trajectories are recorded for MPCComponents.mpc_gui. The single-rate FurutaMPCHardware compiles
for it (see the second half of test/hardware_mpc.jl); the multirate model does not:
spec = program_spec(FurutaMPCMultirateHardware)
model = FurutaMPCMultirateHardware(; name = spec.name, log_file = "run_mpc_gui.csv", spec.ode_kwargs...)
ssys = mtkcompile(model; additional_passes = [SynchToolkit.compile_lustre])
ERROR: ArgumentError: Found clock partition with multiple associated clocks. Involved variables:
[control_system₊estimator_elbow₊rate(t), ..., (control_system₊mpc₊u(t))[1], ...,
ModelingToolkitBase.SampleTime(nothing)(), ...]
The sensing partition and the MPC's have been merged into one. realtime = true is the whole
cause -- output_trajectories has nothing to do with it, and with realtime = false the model
compiles:
(realtime = false, output_trajectories = true) -> compiles
(realtime = false, output_trajectories = false) -> compiles
(realtime = true, output_trajectories = false) -> Found clock partition with multiple associated clocks
The cause is upstream, in ModelingToolkit's clock inference:
JuliaComputing/StateSelection.jl#159. SampleTime() is a single nullary symbolic term, and while
its substitution by a period is per equation and correct, inference treats the term as a variable
and therefore as a graph node shared by every equation that mentions it. Two equations on two
clocks that both mention it become one partition with two clocks. A model may contain at most one
SampleTime() if it has more than one clock.
This model has exactly two: HardwareMeasurement's SampleTimeSource, on the sensing clock (it is
what keeps hw_measure in the equations rather than evaluated while the model is built), and
HardwareDiagnostics', used only in the realtime branch, on the MPC's clock. Both are in
dyad/hardware_loop.dyad.
A local workaround exists and is deliberately not applied: giving HardwareDiagnostics the period
it paces to as an ordinary Ts parameter -- defaulting to SampleTime() so that every single-rate
program stays as it is, and passed as a number by FurutaMPCMultirateHardware -- removes the second
mention, and the dep input still anchors the pacing call in the equations, so the
SampleTimeSource subcomponent can go. Measured on the route it unblocks, a 0.5 s run against
bound callbacks gives 100 MPC solves and 500 encoder reads and estimator updates, x_pred of
nx × (Np + 1) per solve, late recorded on the MPC's clock only, and elapsed reaching 0.497 s,
so the pacing is on the wall clock and mpc_gui has what it reads. Whether to carry that workaround
or wait for the upstream fix is what this issue is for.
Until then: the multirate controller runs on the rig through the compiled program
(compile_program → ProgramRuntime → run_inprocess!), which is unaffected -- run_inprocess!
keeps time in its own loop and needs no realtime pacing inside the model. What is unavailable is
the mpc_gui inspection route for the multirate controller.
Two caveats on that route regardless of this issue, both documented in FurutaMPCMultirateHardware:
realtime = true paces the MPC's clock only, so the five fast reads bunch just before each MPC slot
instead of spreading, which makes it a route for inspecting predictions rather than a substitute for
a rig run; and the GUI itself has not been opened on a multirate solution, only the data it consumes
checked.
FurutaMPCMultirateHardwarecannot be compiled for therun_ode!route -- the one that steps aprogram's clocked partition with an ODE solver against the device so that the MPC's predicted
trajectories are recorded for
MPCComponents.mpc_gui. The single-rateFurutaMPCHardwarecompilesfor it (see the second half of
test/hardware_mpc.jl); the multirate model does not:The sensing partition and the MPC's have been merged into one.
realtime = trueis the wholecause --
output_trajectorieshas nothing to do with it, and withrealtime = falsethe modelcompiles:
The cause is upstream, in ModelingToolkit's clock inference:
JuliaComputing/StateSelection.jl#159.
SampleTime()is a single nullary symbolic term, and whileits substitution by a period is per equation and correct, inference treats the term as a variable
and therefore as a graph node shared by every equation that mentions it. Two equations on two
clocks that both mention it become one partition with two clocks. A model may contain at most one
SampleTime()if it has more than one clock.This model has exactly two:
HardwareMeasurement'sSampleTimeSource, on the sensing clock (it iswhat keeps
hw_measurein the equations rather than evaluated while the model is built), andHardwareDiagnostics', used only in therealtimebranch, on the MPC's clock. Both are indyad/hardware_loop.dyad.A local workaround exists and is deliberately not applied: giving
HardwareDiagnosticsthe periodit paces to as an ordinary
Tsparameter -- defaulting toSampleTime()so that every single-rateprogram stays as it is, and passed as a number by
FurutaMPCMultirateHardware-- removes the secondmention, and the
depinput still anchors the pacing call in the equations, so theSampleTimeSourcesubcomponent can go. Measured on the route it unblocks, a 0.5 s run againstbound callbacks gives 100 MPC solves and 500 encoder reads and estimator updates,
x_predofnx × (Np + 1)per solve,laterecorded on the MPC's clock only, andelapsedreaching 0.497 s,so the pacing is on the wall clock and
mpc_guihas what it reads. Whether to carry that workaroundor wait for the upstream fix is what this issue is for.
Until then: the multirate controller runs on the rig through the compiled program
(
compile_program→ProgramRuntime→run_inprocess!), which is unaffected --run_inprocess!keeps time in its own loop and needs no
realtimepacing inside the model. What is unavailable isthe
mpc_guiinspection route for the multirate controller.Two caveats on that route regardless of this issue, both documented in
FurutaMPCMultirateHardware:realtime = truepaces the MPC's clock only, so the five fast reads bunch just before each MPC slotinstead of spreading, which makes it a route for inspecting predictions rather than a substitute for
a rig run; and the GUI itself has not been opened on a multirate solution, only the data it consumes
checked.