Skip to content
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
42 changes: 17 additions & 25 deletions addons/pid/fnc_create.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,17 @@ Description:
Creates a PID controller.

Parameters:
_pGain - gain for the immediate error <NUMBER>
(Default: 0)
_iGain - gain for the persistent error over time <NUMBER>
(Default: 0)
_dGain - gain for the error rate over time <NUMBER>
(Default: 0)
_setpoint - initial setpoint for the controller <NUMBER>
(Default: 0)
_min - the minimum value the controller can return <NUMBER>
(Default: -1e30)
_max - the maximum value the controller can return <NUMBER>
(Default: 1e30)
_historyLength - how many past errors are stored and used to calculate the derivative/integral <NUMBER>
(Default: 10)
_errorFunction - the function which calculates the error which the controller operators <CODE>
(Default: <CBA_pid_fnc_error_linear>)
_pGain - gain for the immediate error <NUMBER> (Default: 0)
_iGain - gain for the persistent error over time <NUMBER> (Default: 0)
_dGain - gain for the error rate over time <NUMBER> (Default: 0)
_setpoint - initial setpoint for the controller <NUMBER> (Default: 0)
_min - the minimum value the controller can return <NUMBER> (Default: -1e30)
_max - the maximum value the controller can return <NUMBER> (Default: 1e30)
_historyLength - how many past errors are stored and used to calculate the derivative/integral <NUMBER> (Default: 10)
_errorFunction - the function which calculates the error which the controller operators <CODE> (Default: <CBA_pid_fnc_error_linear>)

Returns:
_pid - a PID controller <LOCATION>
_pid - a PID controller <HASHMAP>

Examples:
(begin example)
Expand All @@ -46,12 +38,12 @@ params [
["_errorFunction", FUNC(error_linear), [{}]]
];

private _pid = call CBA_fnc_createNamespace;
_pid setVariable [QGVAR(gains), [_pGain, _iGain, _dGain]];
_pid setVariable [QGVAR(bounds), [_min, _max]];
_pid setVariable [QGVAR(history), []];
_pid setVariable [QGVAR(historyLength), _historyLength];
_pid setVariable [QGVAR(setpoint), _setpoint];
_pid setVariable [QGVAR(errorFunction), _errorFunction];

private _pid = createHashMapFromArray [
[QGVAR(gains), [_pGain, _iGain, _dGain]],
[QGVAR(bounds), [_min, _max]],
[QGVAR(history), []],
[QGVAR(historyLength), _historyLength],
[QGVAR(setpoint), _setpoint],
[QGVAR(errorFunction), _errorFunction]
];
_pid
8 changes: 3 additions & 5 deletions addons/pid/fnc_reset.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Description:
Reset a PID controller's history.

Parameters:
_pid - the controller <LOCATION>
_pid - the controller <HASHMAP>

Returns:
Nothing
Expand All @@ -21,9 +21,7 @@ Author:
---------------------------------------------------------------------------- */
SCRIPT(reset);
params [
["_pid", locationNull, [locationNull]]
["_pid", createHashMap, [createHashMap]]
];

if (isNull _pid) exitWith {};

_pid setVariable [QGVAR(history), []];
_pid set [QGVAR(history), []];
10 changes: 4 additions & 6 deletions addons/pid/fnc_setGains.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Description:
Set new gains for a PID controller.

Parameters:
_pid - a pid controller <LOCATION>
_pid - a pid controller <HASHMAP>
_pGain - the new proportional gain <NUMBER><NIL> (Default: `nil`)
OR
`nil`, to keep the current gain
Expand All @@ -29,15 +29,13 @@ Author:
---------------------------------------------------------------------------- */
SCRIPT(setGains);
params [
["_pid", locationNull, [locationNull]],
["_pid", createHashMap, [createHashMap]],
["_pGain", nil, [0, nil]],
["_iGain", nil, [0, nil]],
["_dGain", nil, [0, nil]]
];

if (isNull _pid) exitWith {};

(_pid getVariable [QGVAR(gains), [0, 0, 0]]) params ["_currentPGain", "_currentIGain", "_currentDGain"];
(_pid getOrDefault [QGVAR(gains), [0, 0, 0]]) params ["_currentPGain", "_currentIGain", "_currentDGain"];
if !(isNil "_pGain") then {
_currentPGain = _pGain;
};
Expand All @@ -47,4 +45,4 @@ if !(isNil "_iGain") then {
if !(isNil "_dGain") then {
_currentDGain = _dGain;
};
_pid setVariable [QGVAR(gains), [_currentPGain, _currentIGain, _currentDGain]];
_pid set [QGVAR(gains), [_currentPGain, _currentIGain, _currentDGain]];
10 changes: 4 additions & 6 deletions addons/pid/fnc_setpoint.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Description:
Set a new setpoint for a PID controller.

Parameters:
_pid - a pid controller <LOCATION>
_pid - a pid controller <HASHMAP>
_setpoint - the new target setpoint for the controller <NUMBER>
_reset - if we want to reset the PID for the new setpoint <BOOL> (Default: true)

Expand All @@ -23,14 +23,12 @@ Author:
---------------------------------------------------------------------------- */
SCRIPT(setpoint);
params [
["_pid", locationNull, [locationNull]],
["_pid", createHashMap, [createHashMap]],
["_setpoint", 0, [0]],
["_reset", true, [true]]
];

if (isNull _pid) exitWith {};

_pid setVariable [QGVAR(setpoint), _setpoint];
_pid set [QGVAR(setpoint), _setpoint];
if (_reset) then {
_pid setVariable [QGVAR(history), []];
_pid set [QGVAR(history), []];
};
20 changes: 9 additions & 11 deletions addons/pid/fnc_update.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ Description:
Updates a PID controller and returns the controller output.

Parameters:
_pid - a pid controller <LOCATION>
_pid - a pid controller <HASHMAP>
_value - the measured value for this update <NUMBER>
_time - the time for which the value was measured <NUMBER>
(Default: CBA_missionTime)
_time - the time for which the value was measured <NUMBER> (Default: CBA_missionTime)

Returns:
The output of the controller <NUMBER>
Expand All @@ -24,15 +23,14 @@ Author:
---------------------------------------------------------------------------- */
SCRIPT(update);
params [
["_pid", locationNull, [locationNull]],
["_pid", createHashMap, [createHashMap]],
["_value", 0, [0]],
["_time", CBA_missionTime, [0]]
];

if (isNull _pid) exitWith { 0 };
private _error = [_value, _pid getVariable [QGVAR(setpoint), 0]] call (_pid getVariable [QGVAR(errorFunction), FUNC(error_linear)]);
private _history = _pid getVariable [QGVAR(history), []];
private _maxHistoryLength = _pid getVariable [QGVAR(historyLength), DEFAULT_HISTORY_LENGTH];
private _error = [_value, _pid getOrDefault [QGVAR(setpoint), 0]] call (_pid getOrDefault [QGVAR(errorFunction), FUNC(error_linear)]);
private _history = _pid getOrDefault [QGVAR(history), []];
private _maxHistoryLength = _pid getOrDefault [QGVAR(historyLength), DEFAULT_HISTORY_LENGTH];

if (_history isNotEqualTo []) then {
// We need a continuous function, so if two measurements are for the same measured time we ignore the new measurements
Expand All @@ -49,7 +47,7 @@ if (_history isNotEqualTo []) then {
if (count _history > _maxHistoryLength) then {
_history deleteAt 0;
};
_pid setVariable [QGVAR(history), _history];
_pid set [QGVAR(history), _history];

private _derivative = 0;
switch (true) do {
Expand Down Expand Up @@ -94,10 +92,10 @@ private _tn_prev = 0;
_error_prev = _error;
} forEach _history;

(_pid getVariable [QGVAR(gains), [0, 0, 0]]) params ["_pGain", "_iGain", "_dGain"];
(_pid getOrDefault [QGVAR(gains), [0, 0, 0]]) params ["_pGain", "_iGain", "_dGain"];

private _delta = _error * _pGain + _integral * _iGain + _derivative * _dGain;

(_pid getVariable [QGVAR(bounds), [-LARGE_NUMBER, LARGE_NUMBER]]) params ["_min", "_max"];
(_pid getOrDefault [QGVAR(bounds), [-LARGE_NUMBER, LARGE_NUMBER]]) params ["_min", "_max"];

_max min (_delta max _min)
44 changes: 44 additions & 0 deletions addons/pid/test_pid.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "script_component.hpp"
SCRIPT(test_pid);

// execVM "\x\cba\addons\pid\test_pid.sqf";

private _fnc_arrayAproxEq = {
params ["_a", "_b"];
if (count _a != count _b) exitWith { false };
{
if (abs (_x - (_b # _forEachIndex)) > 0.001) exitWith { false };
true
} forEach _a;
};


////////////////////////////////////////////////////////////////////////////////////////////////////

private _pid = [1, 0.05, 0.2, 5, -100, 100, 5] call CBA_pid_fnc_create;

private _deltas = [];
_deltas pushBack ([_pid, 5, 0] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 4, 1] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 3, 2] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 3] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 4] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 5] call CBA_pid_fnc_update);

private _expected = [0,1.225,2.3,-0.275,-1.825,-1];
private _test = [_deltas,_expected] call _fnc_arrayAproxEq;
TEST_TRUE(_test,"bad PID controller results");

_pid call CBA_pid_fnc_reset;
[_pid, 0.5, nil, 0.8] call CBA_pid_fnc_setGains;
_deltas = [];
_deltas pushBack ([_pid, 5, 0] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 4, 1] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 3, 2] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 3] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 4] call CBA_pid_fnc_update);
_deltas pushBack ([_pid, 6, 5] call CBA_pid_fnc_update);

private _expected = [0,1.325,1.9,2.025,-4.025,-0.5];
private _test = [_deltas,_expected] call _fnc_arrayAproxEq;
TEST_TRUE(_test,"bad PID controller results");
Loading