Question about the Ramp Tune-In example, suitable for simple PID control?
toastonrye opened this issue Β· 10 comments
I'm referencing the example from the docs;
The value of R is slowly ramped up or down until it matches the input value Y.
TICKRATE = IF(R!=Y, 1, 4)
T = CLOCK() % 2
R = IF(R<Y AND T.RE, R+1, R)
R = IF(R>Y AND T.RE, R-1, R)
By default the RLC runs every 4 ticks, but if R doesn't equal Y then the controller is sped up for faster calculation updates?
And the whole point of T is just a rising edge, but there is only a rising edge if the tickrate is an odd number?
I'm trying to figure out a simple PID control, and above looks to be exactly that.
My question, what is the best way I can try to slow it down. My loop is reacting too fast, and overshooting.
Also, it doesn't seem possible to write my own simple functions? Thanks,
Hi, you make a good point there, I actually should sweep-and-update the documentation.
Before answering your questions: In the timing and quantization range that the game allows, PID might not behave ideal as expected from RW applications. The controller will likely also jitter when tuned in (between error==1 or 0).
I quickly made a quick setup for checking this. It consists of 2 RLCs. The one is a PID filter, and the other a simple low pass, and added tracks and levers for set-point and noise. Principally a PID is a nice tool - it depends that your plant at the output looks like ;)
PID
# PID config, values scaled
Kp = 20 # Proportional coeff
Ki = 2 # Integral coeff
Kd = 0 # Differential coeff
Isat = 200 # Integrator saturation
Ts = 4 # Sample period
e = Y-G # error (setpoint-feedback)
s = TIV1(Ts) # sample trigger
o = (e < 0) & (R==0) # overrun
# Calculate PID at sample points
yp = IF(s, e*Kp , yp) # P output
yi = IF(s & !o, yi + e*Ki , yi ) # I output
yi = LIM(yi, -Isat, Isat) # I saturated
yd = IF(s, (e-e1)*Kd , yd) # D output
e1 = IF(s, e , e1) # Save e(k-1)
YY = yp + yi + yd # Summary output
# Output to R, scale down
R = YY/10
Low pass
#
# s-T1 ( 1 / 1+Z ) filter
#
# The higher Tc, the higher the filter
# time constant.
# s-T1 filter config, Tc = 1 to 99
Tc = 75
T2 = 100-Tc
Ts = 4 # Sample time
s = TIV1(Ts)
# G is input, scale up, Yellow only
# as direct noise test
x = (G+Y) * 100
yk = IF(!s, yk, (yk*Tc + x*T2) / 100)
# Output is red, scale down, round up
# at 0.1
R = (yk+10) / 100
Answers
-
Yes, the RLC has 4 ticks, but re-schedules if an input changes (OFF->ON or vice versa), so that it
reacts fast to logic whilst keeping the CPU consumption low.T
is really just an edge generator,
as you already presumed.clock()%2
yields0
or1
, where a transition from0
to>0
is detected
as a rising edge. Since a few Mod versions, I added theTIVx()
timers, which are more accurate
for triggering. -
As a PID example, the code above could be used.
-
Slowing down your controller can be now done with increasing the sample period, e.g.
s = TIVx(10)
instead ofs = TIVx(4)
as in the code above. -
Yes, the code window is kept tight and limited to one screen, so that not too much code is entered -
sounds strange I know, but this is to restrict the performance that the RLCs need. In Single-Player
we normally do not care, but the mod is also used on servers (mostly not ultra high spec machines),
and mods should take these use cases into account.
Do you make something like a temperature control? I mean where you put energy into the system, but not
take it out? (like a heater control?).
This is really cool, thanks for the detailed reply! Gives me some homework to try and understand both code snippets.
Yea, I'm definitely interested in any documentation updates/examples to try.
- I'm going to have to experiment more with ticks, vanilla Redstone I think is 2 ticks, the RLC is 4 ticks.. I haven't played with TIVx() yet. I wish that carpet mod for Fabric was on Forge, so I could freeze/ step through ticks for learning/debugging.
- I copy and pasted your example, but I think I am misunderstanding how to interface with it. I made a short 4 minute video to try and concisely explain my setup and what I am trying to do.
https://www.youtube.com/watch?v=egkZK_4Pmf4 - I'll need to test this tomorrow, to see how it works.
- Yes that makes sense, I'm just use to ComputerCraft, writing functions for when I need to call code. I'm trying to wrap my mind around how to do that with RLCs. It seems from your examples, using the AND (&) with an enable bit in a IF(X & ENABLE, true, false) is what I need to be using.
Yes, temperature control! I think it would be really cool to demonstrate your RLC PID with PneumaticCraft's heat system.
Ok, reading through your PID code more carefully, I definitely interfaced incorrectly to it.
My thermostat feedback should be on the PID controller's green, and my output to the regulator should have been on the PID controller's red.
I'm still unsure about that low pass filter controller, do I even need it? Or was that made for simulating?
Hey man, alright, I could take a look at the video, so let's quickly walk through ;)
As you deduced already correctly you only need the RLC with the PID code:
- Your feedback input is the thermostat at
G
, and it should rise from 0 to 15 like the temperature rises. - Your target temperature is set on
Y
with the analog lever, and the 0-to-15 scaled signal represents the same temperature values as the thermostat curve. - Your controller output is
R
to the Regulator Tube Module.
Digital filters like PID normally run with a constant sampling rate. To do this with the RLC, we need make the sample trigger s=TIV1(Ts)
- if the sampling period Ts
is 20tck
, then s
will be ON for one tick every second. It's a bit "meh" that we need to use the signal = IF(s, ...., signal)
thing to skip calculating if we are not in that sample tick, but well;
What the PID does is trying to bring G
to the same value as Y
by changing R
as needed.
-
First we calculate how far we are away, that's called the error
e
:e = Y-G
. -
The proportional part directly reacts with a scaling to that error
yp = Kp * e
-
The integral part reacts over time by adding
Ki * e
every sample tick to its output. -
The differential part reacts to how fast
G
vsY
changes with the scalingKd * de/dt
, where the deltade
is simply calculated by subtracting the last error value from the current one. And the the time change isTs
because this is the difference between the last and the current sampling point. -
The saturation is only a fence guard that is often built into PI controllers, so that the integrator does not run away and needs ages to get down again.
-
I additionally added the overrun
o
to my setup, this can help avoiding that the integrator runs away downward, because redstone does not have negative values, so we can only push energy into the system, but cannot actively take it out. If our outputR
is already zero, the integrator can be set on hold, so that it does not fall far below zero while the boiler slowly cools down itself. -
To increase the resolution of the coefficients, they are scaled x10
Kp=2.0 -> 20
, and we finally divide the output by 10 again.
That's basically it. The rest should be tuning. Likely, do will not need the differential part, with that resolution and noise it normally causes more trouble than help. The resulting PI controller code is then:
# PI config, values scaled x10
Ts = 4 # Sample period
Kp = 20 # Proportional coeff
Ki = 2 # Integral coeff
Isat = 200 # I saturation
e = Y-G # error
s = TIV1(Ts) # sample trigger
o = (e < 0) & (R==0) # overrun
# Calculate PI at sample points
yp = if(s, e*Kp , yp) # P output
yi = if(s & !o, yi + e*Ki , yi ) # I output
yi = LIM(yi, -Isat, Isat) # I saturated
# Output to R, scale down
R = (yp + yi + yd) / 10
- If you get overshoots, play with the sampling time first, the time quantization is likely better than the redstone value resolution.
Alternatively, the hysteresis controller in the docs may also be a simple alternative in case the tuning does not work out. Cheer's;;
Oky, small update, to ease the pain with the sampling rate, I modified TICKRATE=...
to ignore signal interrupts, so the whole TIV1
and IF...
will be obsolete then. What MC version/loader are you on, so that I update this earlier?
I am currently playing a modpack E9E (v1.20.1) for Minecraft 1.19.2.
But I'll update myself.
Redstone Pen 1.2.20
Forge 1.19.2-43.2.14
I had another question I was meaning to ask, I haven't been in game yet to check.
Is it possible to do something like a tickrate = 0, so it stops? Like a debugging where you could step through each calculation?
Uh, ok. mc1.19 is a while ago. I'll need to port back stepwise, but should be feasible.
I have no debug stepping implemented - the whole code is executed every tick top-down, and the world around cannot be stopped normally. What I have is a display of the current values of variables by hovering the RLC symbol in the UI:
Hmm, something like a oscilloscope like tracking could help, but this may be a mayor pain to implement with the means that MC provides. I'll give it another thought some day ;)
What you have shown me is working great so far, I wouldn't bother back-porting it to 1.19 if you are already onto the next version. I'm actually not sure what you mean by modifying the tickrate to ignore interrupts, I don't see tickrate in your example code?
I am not sure how that carpet mod freezes the tickrate, I guess he is doing it to the whole world which can be weird.
It's actually really interesting you mention the oscilloscope, I was thinking that would be really cool to have a historian like MineFactory Reloaded did. I've been playing that old version recently, it would be cool to have some sort of trend of your 6 possible colours.
If you don't remember it, I have a short 10 second example of that MFR historian here https://youtu.be/pUNqm1xlFQM?t=900 (link should take you to the 15:00 mark where it is).
Anyways, you more than answered my original question. Your PID script/ explanation is great, thanks so much!
Hi, aye something similar crossed my mind. It would have to support multi-channel somehow, and still fit into a vanillarish look & feel, or be an item with a monitor. Let's see, I have already a connector for Arduino and other stuff in the mod, so a scope UI should be a feasible challenge ;) Cheer's;-