Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrected PID calculation #187

Merged
merged 10 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| [Log](https://sleitnick.github.io/RbxUtil/api/Log) | `Log = "sleitnick/[email protected]"` | Log class for logging to PlayFab |
| [Net](https://sleitnick.github.io/RbxUtil/api/Net) | `Net = "sleitnick/[email protected]"` | Static networking module |
| [Option](https://sleitnick.github.io/RbxUtil/api/Option) | `Option = "sleitnick/[email protected]"` | Represent optional values in Lua |
| [PID](https://sleitnick.github.io/RbxUtil/api/PID) | `PID = "sleitnick/pid@1.2.1"` | PID Controller class |
| [PID](https://sleitnick.github.io/RbxUtil/api/PID) | `PID = "sleitnick/pid@2.0.0"` | PID Controller class |
| [Quaternion](https://sleitnick.github.io/RbxUtil/api/Quaternion) | `Quaternion = "sleitnick/[email protected]"` | Quaternion class |
| [Sequent](https://sleitnick.github.io/RbxUtil/api/Sequent) | `Sequent = "sleitnick/[email protected]"` | Sequent class |
| [Ser](https://sleitnick.github.io/RbxUtil/api/Ser) | `Ser = "sleitnick/[email protected]"` | Ser class for serialization and deserialization |
Expand Down
17 changes: 2 additions & 15 deletions modules/pid/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,15 @@ declare namespace PID {
}

interface PID {
/**
* POnE stands for "Proportional on Error".
*
* Set to `true` by default.
*
* - `true`: The PID applies the proportional calculation on the _error_.
* - `false`: The PID applies the proportional calculation on the _measurement_.
*
* Setting this value to `false` may help the PID move smoother and help
* eliminate overshoot.
*/
POnE: boolean;

/**
* Calculates the new output based on the setpoint and input.
*
* @param setpoint The goal for the PID.
* @param input The current input.
* @param processVariable The measured value of the system to compare against the setpoint.
* @param deltaTime Delta time.
* @returns The updated output.
*/
Calculate(setpoint: number, input: number, deltaTime: number): number;
Calculate(setpoint: number, processVariable: number, deltaTime: number): number;

/**
* Resets the PID.
Expand Down
86 changes: 28 additions & 58 deletions modules/pid/init.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
--!native

export type PID = {
POnE: boolean,
Reset: (self: PID) -> (),
Calculate: (self: PID, setpoint: number, input: number, deltaTime: number) -> number,
Debug: (self: PID, name: string, parent: Instance?) -> (),
Expand All @@ -14,42 +13,20 @@ export type PID = {
for _proportional, integral, derivative_. PIDs are input feedback loops that try to reach a specific
goal by measuring the difference between the input and the desired value, and then returning a new
desired input.

A common example is a car's cruise control, which would give a PID the current speed
and the desired speed, and the PID controller would return the desired throttle input to reach the
desired speed.

Original code based upon the [Arduino PID Library](https://github.com/br3ttb/Arduino-PID-Library).
]=]
local PID = {}
PID.__index = PID

--[=[
@within PID
@prop POnE boolean

POnE stands for "Proportional on Error".

Set to `true` by default.

- `true`: The PID applies the proportional calculation on the _error_.
- `false`: The PID applies the proportional calculation on the _measurement_.

Setting this value to `false` may help the PID move smoother and help
eliminate overshoot.

```lua
local pid = PID.new(...)
pid.POnE = true|false
```
]=]

--[=[
@param min number -- Minimum value the PID can output
@param max number -- Maximum value the PID can output
@param kp number -- Proportional coefficient
@param ki number -- Integral coefficient
@param kd number -- Derivative coefficient
@param kp number -- Proportional coefficient (P)
@param ki number -- Integral coefficient (I)
@param kd number -- Derivative coefficient (D)
@return PID

Constructs a new PID.
Expand All @@ -60,19 +37,13 @@ PID.__index = PID
]=]
function PID.new(min: number, max: number, kp: number, ki: number, kd: number): PID
local self = setmetatable({}, PID)

self._min = min
self._max = max

self._kp = kp
self._ki = ki
self._kd = kd

self._lastInput = 0
self._outputSum = 0

self.POnE = true

self._lastError = 0 -- Store the last error for derivative calculation
self._integralSum = 0 -- Store the sum Σ of errors for integral calculation
return self
end

Expand All @@ -81,13 +52,13 @@ end
]=]
function PID:Reset()
self._lastInput = 0
self._outputSum = 0
self._integralSum = 0
end

--[=[
@param setpoint number -- The desired point to reach
@param input number -- The current inputted value
@param deltaTime number -- Delta time
@param setpoint number -- The desired point to reach
@param processVariable number -- The measured value of the system to compare against the setpoint
@param deltaTime number -- Delta time. This is the time between each PID calculation
@return output: number

Calculates the new output based on the setpoint and input. For example,
Expand All @@ -98,36 +69,35 @@ end
local cruisePID = PID.new(0, 1, ...)
local desiredSpeed = 50

RunService.Heartbeat:Connect(function()
local throttle = cruisePID:Calculate(desiredSpeed, car.CurrentSpeed)
RunService.Heartbeat:Connect(function(dt)
local throttle = cruisePID:Calculate(desiredSpeed, car.CurrentSpeed, dt)
car:SetThrottle(throttle)
end)
```
]=]
function PID:Calculate(setpoint: number, input: number, deltaTime: number)
local ki = self._ki * deltaTime
local kd = self._kd * deltaTime
function PID:Calculate(setpoint: number, processVariable: number, deltaTime: number)
-- Calculate the error e(t) = SP - PV(t)
local err = setpoint - processVariable

local err = setpoint - input
local dInput = input - self._lastInput
self._outputSum += ki * err
-- Proportional term
local pOut = self._kp * err

if not self.POnE then
self._outputSum -= self._kp * dInput
end
-- Integral term
self._integralSum = self._integralSum + err * deltaTime
local iOut = self._ki * self._integralSum

self._outputSum = math.clamp(self._outputSum, self._min, self._max)
-- Derivative term
local derivative = (err - self._lastError) / deltaTime
local dOut = self._kd * derivative

local output = 0
if self.POnE then
output = self._kp * err
end
-- Combine terms
local output = pOut + iOut + dOut

output += self._outputSum - kd * dInput
-- Clamp output to min/max
output = math.clamp(output, self._min, self._max)

self._lastInput = input

-- Save the current error for the next derivative calculation
self._lastError = err
return output
end

Expand Down
2 changes: 1 addition & 1 deletion modules/pid/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rbxutil/pid",
"version": "1.2.1",
"version": "2.0.0",
"main": "init.lua",
"repository": "github:Sleitnick/RbxUtil",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion modules/pid/wally.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sleitnick/pid"
description = "PID Controller class"
version = "1.2.1"
version = "2.0.0"
license = "MIT"
authors = ["Stephen Leitnick"]
registry = "https://github.com/UpliftGames/wally-index"
Expand Down
Loading