Skip to content

Instantly share code, notes, and snippets.

@akfish
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akfish/6939eb5da2cb848996dc to your computer and use it in GitHub Desktop.
Save akfish/6939eb5da2cb848996dc to your computer and use it in GitHub Desktop.
PID Controllor for Hypnotizd's Reactor Controller
----------------------------------------------------------
-- 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