Last active
August 29, 2015 14:17
-
-
Save akfish/6939eb5da2cb848996dc to your computer and use it in GitHub Desktop.
PID Controllor for Hypnotizd's Reactor Controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---------------------------------------------------------- | |
-- Active Cooling Big Reactor Monitor -- | |
-- -- | |
-- Minecraft HermitCraft FTB Infinity Episode 28 -- | |
-- https://www.youtube.com/watch?v=cIPxwZJWOiE -- | |
-- -- | |
-- YouTube Channel http://youtube.com/hypnotizd -- | |
-- -- | |
-- Add PID Algorithm By AKFish -- | |
-- https://github.com/akfish -- | |
-- https://catx.me -- | |
---------------------------------------------------------- | |
local mon = {} | |
local hasMon = false | |
local hasRMon = false | |
local rMonName = "" | |
local cPort = {} | |
local hasCPort = false | |
local hasRCPort = false | |
local rCPortName = "" | |
local directions = {"top", "bottom", "front", "back", "left", "right"} | |
local minX = 0 | |
local minY = 0 | |
local minZ = 0 | |
local maxX = 0 | |
local maxY = 0 | |
local maxZ = 0 | |
local crPrevCoreTemp = 0 | |
local crCoreTemp = 0 | |
local crCoreChangeWait = 0 | |
-- PID Controllor Factory | |
-- @author AKFish | |
-- @reference http://en.wikipedia.org/wiki/PID_controller | |
-- @param target - Desired process variable (i.e. reactor temprature) value | |
-- @param k_p - Proportional gain | |
-- @param k_i - Integral gain | |
-- @param k_d - Derivative gain | |
-- @return a function that calculates controllor output (delta for the manipulated variable, i.e. rod position) | |
local function PID(target, k_p, k_i, k_d) | |
local previouseError = 0 | |
local integral = 0 | |
local dt = 1 | |
return function (measured) | |
local error = target - measured | |
previouseError = error | |
integral = integral + error * dt | |
local derivative = (error - previouseError) / dt | |
return k_p * error + k_i * integral + k_d * derivative | |
end | |
end | |
local targetCoreTemperature = 10000000 -- Or maybe something not that hot | |
-- The following parameters need to be fine tuned for optimal results | |
-- See http://en.wikipedia.org/wiki/PID_controller#Manual_tuning | |
local k_p = 0.1 | |
local k_i = 0.1 | |
local k_d = 0.1 | |
-- The controllor instance | |
-- Note: this instance stores internal states of the controllor and | |
-- should be recreated when any of the parameters | |
-- or initial conditions are changed | |
local pid = PID(targetCoreTemperature, k_p, k_i, k_d) | |
local function findPeripherals() | |
for k, v in pairs(peripheral.getNames()) do | |
if string.find(v, "Reactor") then | |
hasCPort = true | |
hasRCPort = true | |
rCPortName = v | |
print("INFO: Found remote "..v) | |
os.sleep(1) | |
elseif string.find(v, "monitor") then | |
hasMon = true | |
hasRMon = true | |
rMonName = v | |
print("INFO: Found remote "..v) | |
os.sleep(1) | |
end | |
end | |
end | |
local function findCPort() | |
if hasRCPort then return end | |
for k, v in pairs(directions) do | |
local p = peripheral.wrap(v) | |
if p ~= nil then | |
if string.find(peripheral.getType(v), "Reactor") then | |
cPort = p | |
hasCPort = true | |
return | |
end | |
end | |
end | |
print("ERROR: No computer port detected!") | |
end | |
local function findMon() | |
if hasRMon then return end | |
for k, v in pairs(directions) do | |
local p = peripheral.wrap(v) | |
if p ~= nil then | |
if string.find(peripheral.getType(v), "monitor") then | |
mon = p | |
hasMon = true | |
return | |
end | |
end | |
end | |
print("WARNING: No monitor detected!") | |
os.sleep(5) | |
end | |
local function monClear() | |
if hasMon then | |
if hasRMon then | |
peripheral.call(rMonName, "setCursorPos", 1, 1) | |
peripheral.call(rMonName, "clear") | |
else | |
mon.setCursorPos(1, 1) | |
mon.clear() | |
end | |
else | |
term.clear() | |
term.setCursorPos(1, 1) | |
end | |
end | |
local function monWrite(x, y, text) | |
if hasMon then | |
if hasRMon then | |
peripheral.call(rMonName, "setCursorPos", x, y) | |
peripheral.call(rMonName, "write", text) | |
else | |
mon.setCursorPos(x, y) | |
mon.write(text) | |
end | |
else | |
term.setCursorPos(x, y) | |
term.write(text) | |
end | |
end | |
local function getSize() | |
if hasRCPort then | |
minX, minY, minZ = peripheral.call(rCPortName, "getMinimumCoordinate") | |
maxX, maxY, maxZ = peripheral.call(rCPortName, "getMaximumCoordinate") | |
else | |
minX, minY, minZ = cPort.getMinimumCoordinate() | |
maxX, maxY, maxZ = cPort.getMaximumCoordinate() | |
end | |
end | |
local function setAllControlRodLevels(x) | |
if hasRCPort then | |
peripheral.call(rCPortName, "setAllControlRodLevels", x) | |
else | |
cPort.setAllControlRodLevels(x) | |
end | |
end | |
local function getActive() | |
local ret = false | |
if hasRCPort then | |
ret = peripheral.call(rCPortName, "getActive") | |
else | |
ret = cPort.getActive() | |
end | |
return ret | |
end | |
local function getControlRodLevel() | |
local ret = 0 | |
if hasRCPort then | |
ret = peripheral.call(rCPortName, "getControlRodLevel", 0) | |
else | |
ret = cPort.getControlRodLevel(0) | |
end | |
return ret | |
end | |
local function getFuelTemperature() | |
local ret | |
if hasRCPort then | |
ret = peripheral.call(rCPortName, "getFuelTemperature") | |
else | |
ret = cPort.getFuelTemperature() | |
end | |
return ret | |
end | |
local function setControlRods() | |
if getActive() then | |
crCoreTemp = getFuelTemperature() | |
local delta = pid(crCoreTemp) | |
local ctrlRodLevel = getControlRodLevel() | |
-- Note: Higher than desired temperature yeilds negative delta and vice versa | |
-- Higher rod level cools it down more/faster, use negative sign | |
-- (If higher rod level cools it down less/slower, use plus sign) | |
local newRodLevel = ctrlRodLevel - delta | |
-- TODO: clamp the newRodLevel value into the correct range | |
setAllControlRodLevels(newRodLevel) | |
end | |
end | |
local function init() | |
term.clear() | |
term.setCursorPos(1, 1) | |
findPeripherals() | |
findCPort() | |
findMon() | |
getSize() | |
monClear() | |
end | |
local function displayInfo() | |
local rods = 0 | |
local casingTemp = 0 | |
local coreTemp = 0 | |
local rodLevel = 0 | |
local fuelReactivity = 0 | |
local fuelConsumed = 0 | |
local steamProduced = 0 | |
if hasRCPort then | |
rods = peripheral.call(rCPortName, "getNumberOfControlRods") | |
casingTemp = peripheral.call(rCPortName, "getCasingTemperature") | |
coreTemp = peripheral.call(rCPortName, "getFuelTemperature") | |
rodLevel = peripheral.call(rCPortName, "getControlRodLevel", 0) | |
fuelReactivity = peripheral.call(rCPortName, "getFuelReactivity") | |
fuelConsumed = peripheral.call(rCPortName, "getFuelConsumedLastTick") | |
steamProduced = peripheral.call(rCPortName, "getHotFluidProducedLastTick") | |
else | |
rods = cPort.getNumberOfControlRods() | |
casingTemp = cPort.getCasingTemperature() | |
coreTemp = cPort.getFuelTemperature() | |
rodLevel = cPort.getControlRodLevel(0) | |
fuelReactivity = cPort.getFuelReactivity() | |
fuelConsumed = cPort.getFuelConsumedLastTick() | |
steamProduced = cPort.getHotFluidProducedLastTick() | |
end | |
local active = "Inactive" | |
if getActive() then | |
active = "Active" | |
end | |
monClear() | |
monWrite(1, 1, " Reactor Status: "..active) | |
monWrite(1, 2, " Reactor Size: "..1+maxX-minX.."x"..1+maxY-minY.."x"..1+maxZ-minZ) | |
monWrite(1, 3, " Num Control Rods: "..rods) | |
monWrite(1, 4, " Casing Temp.: "..casingTemp) | |
monWrite(1, 5, " Core Temp.: "..coreTemp) | |
monWrite(1, 6, "Control Rod Level: "..rodLevel) | |
monWrite(1, 7, " Fuel Reactivity: "..fuelReactivity) | |
monWrite(1, 8, " Fuel Usage/tick: "..fuelConsumed) | |
monWrite(1, 9, " Steam Produced: "..steamProduced) | |
end | |
init() | |
if hasCPort == false then return end | |
while true do | |
setControlRods() | |
displayInfo() | |
os.sleep(3) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment