Skip to content

Instantly share code, notes, and snippets.

@idbrii
Created June 9, 2022 17:45
Show Gist options
  • Save idbrii/b79a1e6e3c3f88b62c134db211986610 to your computer and use it in GitHub Desktop.
Save idbrii/b79a1e6e3c3f88b62c134db211986610 to your computer and use it in GitHub Desktop.
A pid controller (proportional–integral–derivative controller) in lua
-- Public domain/CC0
-- A pid controller (proportional–integral–derivative controller).
-- Useful to track an entity without overshooting (like a docking spaceship).
-- Could be expanded to control orientation, speed, or other values.
-- Requires tuning values:
-- tuning = {
-- gain = {
-- -- all in [0,1]
-- proportional = 0.1,
-- derivative = 0.9,
-- integral = 0.1,
-- },
-- -- in output distance units
-- max_accumulated_error = 50,
-- }
local Vec2 = require "cpml.modules.vec2"
local class = require "gabe.class"
local mathx = require "batteries.mathx"
local Pid = class('pid')
-- min/max are numbers
local function clamp_vec(v, min, max)
local n, len = v:normalize()
len = mathx.clamp(len, min, max)
return n * len
end
function Pid:init(tuning)
self.tuning = tuning
self.value_fns = {}
end
function Pid:getCurrent()
return self.current
end
function Pid:trackEntityPosition(target)
self.target = target
self.current = target.transform.pos:clone()
self:resetState()
end
function Pid:resetState()
self.accumulated_error = Vec2.zero
self.prev_error = Vec2.zero
-- Start with a state of zero error to prevent derivative kick.
self.prev_error = self:_calcError()
end
function Pid:_calcError()
return self.target.transform.pos - self.current
end
function Pid:update(dt)
if not self.target then
return
end
local error_delta = self:_calcError()
local error_velocity = error_delta - self.prev_error
self.prev_error = error_delta
local limit = self.tuning.max_accumulated_error
self.accumulated_error = self.accumulated_error + (error_delta * dt)
self.accumulated_error = clamp_vec(self.accumulated_error, -limit, limit)
local gain = self.tuning.gain
local proportional = error_delta * gain.proportional
local derivative = error_velocity * gain.derivative
local integral = self.accumulated_error * gain.integral
local pid = proportional + derivative + integral
self.current = self.current + pid
local limits = self.tuning.output_limit
if limits then
self.current = clamp_vec(self.current, limits.min, limits.max)
end
if self.tuning.max_total_error then
error_delta = self:_calcError()
error_delta = clamp_vec(error_delta, 0, self.tuning.max_total_error)
self.current = self.target.transform.pos + error_delta
end
end
return Pid
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment