Resurgence (PY2022)
Codebase for the Husky Robotics 2021-2022 rover Resurgence
Loading...
Searching...
No Matches
PIDControl.h
1#pragma once
2
3#include "../utils/time.h"
4#include "../world_interface/data.h"
5
6#include <chrono>
7#include <functional>
8#include <optional>
9#include <utility>
10
11#include <Eigen/Core>
12
13namespace control {
14
19struct PIDGains {
20 double kP, kI, kD, kF;
21 std::optional<double> iZone;
22};
23
30template <int dim>
32public:
33 template <int d>
34 using Arrayd = Eigen::Array<double, d, 1>;
35
42 reset();
43 setGains(gains);
44 }
45
52 for (int i = 0; i < dim; i++) {
53 kP(i) = gains[i].kP;
54 kI(i) = gains[i].kI;
55 kD(i) = gains[i].kD;
56 kF(i) = gains[i].kF;
57 iZone(i) = gains[i].iZone.value_or(std::numeric_limits<double>::infinity());
58 }
59 }
60
66 bool hasTarget() {
67 return setPoint.has_value();
68 }
69
75 void setTarget(const Arrayd<dim>& setPoint) {
76 this->setPoint = setPoint;
77 }
78
86 Arrayd<dim> getOutput(robot::types::datatime_t currTime, const Arrayd<dim>& currValue) {
87 if (!setPoint.has_value()) {
88 return Arrayd<dim>::Zero();
89 }
90
91 // see wikipedia link above for in-depth explanation
92
93 Arrayd<dim> err = setPoint.value() - currValue;
94 Arrayd<dim> pTerm = err * kP;
95 Arrayd<dim> fTerm = setPoint.value() * kF;
96
97 Arrayd<dim> iTerm = Arrayd<dim>::Zero();
98 Arrayd<dim> dTerm = Arrayd<dim>::Zero();
99
100 if (lastData.has_value()) {
101 // TODO: instead of numerically differentiating we can use a model (if we have one)
102 auto [lastTime, lastValue] = lastData.value();
103 double dtSec = util::durationToSec(currTime - lastTime);
104
105 // use the backwards finite difference for approximating the derivative
106 Arrayd<dim> derivative = (currValue - lastValue) / dtSec;
107 dTerm = derivative * kD;
108
109 // use the trapezoid rule for approximating the integral
110 Arrayd<dim> area = (lastValue + currValue) * dtSec / 2.0;
111 iAccum += area;
112 iTerm = (err.abs() <= iZone).select(iAccum * kI, Arrayd<dim>::Zero());
113 }
114
115 lastData = {currTime, currValue};
116 Arrayd<dim> output = pTerm + iTerm + dTerm + fTerm;
117
118 return output;
119 }
120
124 void reset() {
125 setPoint.reset();
126 lastData.reset();
127 iAccum.setZero();
128 }
129
130private:
131 Arrayd<dim> kP, kI, kD, kF;
132 Arrayd<dim> iZone; // +inf indicates no i zone
133 Arrayd<dim> iAccum;
134 std::optional<Arrayd<dim>> setPoint;
135 std::optional<std::pair<robot::types::datatime_t, Arrayd<dim>>> lastData;
136};
137} // namespace control
static constexpr _Tp infinity() noexcept
bool hasTarget()
Check if a target is currently set.
Definition PIDControl.h:66
void reset()
Reset the controller.
Definition PIDControl.h:124
void setTarget(const Arrayd< dim > &setPoint)
Set the target of the controller.
Definition PIDControl.h:75
Arrayd< dim > getOutput(robot::types::datatime_t currTime, const Arrayd< dim > &currValue)
Get the output from the controller.
Definition PIDControl.h:86
PIDController(const std::array< PIDGains, dim > &gains)
Construct a new PIDController.
Definition PIDControl.h:41
void setGains(const std::array< PIDGains, dim > &gains)
Set the gains of the PID controller.
Definition PIDControl.h:51
std::chrono::time_point< dataclock > datatime_t
A point in time as measured by dataclock.
Definition data.h:31
double durationToSec(std::chrono::duration< Rep, Period > dur)
Convert a duration to seconds, as a double.
Definition time.h:18
A set of PID gains.
Definition PIDControl.h:19