1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include <wpi/SymbolExports.h>
#include "frc/EigenCore.h"
#include "frc/controller/LinearPlantInversionFeedforward.h"
#include "frc/controller/LinearQuadraticRegulator.h"
#include "frc/estimator/KalmanFilter.h"
#include "frc/system/LinearSystem.h"
#include "units/time.h"
#include "units/voltage.h"
namespace frc {
/**
* Combines a controller, feedforward, and observer for controlling a mechanism
* with full state feedback.
*
* For everything in this file, "inputs" and "outputs" are defined from the
* perspective of the plant. This means U is an input and Y is an output
* (because you give the plant U (powers) and it gives you back a Y (sensor
* values). This is the opposite of what they mean from the perspective of the
* controller (U is an output because that's what goes to the motors and Y is an
* input because that's what comes back from the sensors).
*
* For more on the underlying math, read
* https://file.tavsys.net/control/controls-engineering-in-frc.pdf.
*
* @tparam States Number of states.
* @tparam Inputs Number of inputs.
* @tparam Outputs Number of outputs.
*/
template <int States, int Inputs, int Outputs>
class LinearSystemLoop {
public:
using StateVector = Vectord<States>;
using InputVector = Vectord<Inputs>;
using OutputVector = Vectord<Outputs>;
/**
* Constructs a state-space loop with the given plant, controller, and
* observer. By default, the initial reference is all zeros. Users should
* call reset with the initial system state before enabling the loop. This
* constructor assumes that the input(s) to this system are voltage.
*
* @param plant State-space plant.
* @param controller State-space controller.
* @param observer State-space observer.
* @param maxVoltage The maximum voltage that can be applied. Commonly 12.
* @param dt The nominal timestep.
*/
LinearSystemLoop(LinearSystem<States, Inputs, Outputs>& plant,
LinearQuadraticRegulator<States, Inputs>& controller,
KalmanFilter<States, Inputs, Outputs>& observer,
units::volt_t maxVoltage, units::second_t dt)
: LinearSystemLoop(
plant, controller, observer,
[=](const InputVector& u) {
return frc::DesaturateInputVector<Inputs>(u, maxVoltage.value());
},
dt) {}
/**
* Constructs a state-space loop with the given plant, controller, and
* observer. By default, the initial reference is all zeros. Users should
* call reset with the initial system state before enabling the loop. This
* constructor assumes that the input(s) to this system are voltage.
*
* @param plant State-space plant.
* @param controller State-space controller.
* @param observer State-space observer.
* @param clampFunction The function used to clamp the input vector.
* @param dt The nominal timestep.
*/
LinearSystemLoop(LinearSystem<States, Inputs, Outputs>& plant,
LinearQuadraticRegulator<States, Inputs>& controller,
KalmanFilter<States, Inputs, Outputs>& observer,
std::function<InputVector(const InputVector&)> clampFunction,
units::second_t dt)
: LinearSystemLoop(
controller,
LinearPlantInversionFeedforward<States, Inputs>{plant, dt},
observer, clampFunction) {}
/**
* Constructs a state-space loop with the given controller, feedforward and
* observer. By default, the initial reference is all zeros. Users should
* call reset with the initial system state.
*
* @param controller State-space controller.
* @param feedforward Plant inversion feedforward.
* @param observer State-space observer.
* @param maxVoltage The maximum voltage that can be applied. Assumes
* that the inputs are voltages.
*/
LinearSystemLoop(
LinearQuadraticRegulator<States, Inputs>& controller,
const LinearPlantInversionFeedforward<States, Inputs>& feedforward,
KalmanFilter<States, Inputs, Outputs>& observer, units::volt_t maxVoltage)
: LinearSystemLoop(
controller, feedforward, observer, [=](const InputVector& u) {
return frc::DesaturateInputVector<Inputs>(u, maxVoltage.value());
}) {}
/**
* Constructs a state-space loop with the given controller, feedforward,
* observer and clamp function. By default, the initial reference is all
* zeros. Users should call reset with the initial system state.
*
* @param controller State-space controller.
* @param feedforward Plant-inversion feedforward.
* @param observer State-space observer.
* @param clampFunction The function used to clamp the input vector.
*/
LinearSystemLoop(
LinearQuadraticRegulator<States, Inputs>& controller,
const LinearPlantInversionFeedforward<States, Inputs>& feedforward,
KalmanFilter<States, Inputs, Outputs>& observer,
std::function<InputVector(const InputVector&)> clampFunction)
: m_controller(&controller),
m_feedforward(feedforward),
m_observer(&observer),
m_clampFunc(clampFunction) {
m_nextR.setZero();
Reset(m_nextR);
wpi::math::MathSharedStore::ReportUsage(
wpi::math::MathUsageId::kSystem_LinearSystemLoop, 1);
}
LinearSystemLoop(LinearSystemLoop&&) = default;
LinearSystemLoop& operator=(LinearSystemLoop&&) = default;
/**
* Returns the observer's state estimate x-hat.
*/
const StateVector& Xhat() const { return m_observer->Xhat(); }
/**
* Returns an element of the observer's state estimate x-hat.
*
* @param i Row of x-hat.
*/
double Xhat(int i) const { return m_observer->Xhat(i); }
/**
* Returns the controller's next reference r.
*/
const StateVector& NextR() const { return m_nextR; }
/**
* Returns an element of the controller's next reference r.
*
* @param i Row of r.
*/
double NextR(int i) const { return NextR()(i); }
/**
* Returns the controller's calculated control input u.
*/
InputVector U() const {
return ClampInput(m_controller->U() + m_feedforward.Uff());
}
/**
* Returns an element of the controller's calculated control input u.
*
* @param i Row of u.
*/
double U(int i) const { return U()(i); }
/**
* Set the initial state estimate x-hat.
*
* @param xHat The initial state estimate x-hat.
*/
void SetXhat(const StateVector& xHat) { m_observer->SetXhat(xHat); }
/**
* Set an element of the initial state estimate x-hat.
*
* @param i Row of x-hat.
* @param value Value for element of x-hat.
*/
void SetXhat(int i, double value) { m_observer->SetXhat(i, value); }
/**
* Set the next reference r.
*
* @param nextR Next reference.
*/
void SetNextR(const StateVector& nextR) { m_nextR = nextR; }
/**
* Return the controller used internally.
*/
const LinearQuadraticRegulator<States, Inputs>& Controller() const {
return *m_controller;
}
/**
* Return the feedforward used internally.
*
* @return the feedforward used internally.
*/
const LinearPlantInversionFeedforward<States, Inputs> Feedforward() const {
return m_feedforward;
}
/**
* Return the observer used internally.
*/
const KalmanFilter<States, Inputs, Outputs>& Observer() const {
return *m_observer;
}
/**
* Zeroes reference r and controller output u. The previous reference
* of the PlantInversionFeedforward and the initial state estimate of
* the KalmanFilter are set to the initial state provided.
*
* @param initialState The initial state.
*/
void Reset(const StateVector& initialState) {
m_nextR.setZero();
m_controller->Reset();
m_feedforward.Reset(initialState);
m_observer->SetXhat(initialState);
}
/**
* Returns difference between reference r and current state x-hat.
*/
StateVector Error() const { return m_controller->R() - m_observer->Xhat(); }
/**
* Correct the state estimate x-hat using the measurements in y.
*
* @param y Measurement vector.
*/
void Correct(const OutputVector& y) { m_observer->Correct(U(), y); }
/**
* Sets new controller output, projects model forward, and runs observer
* prediction.
*
* After calling this, the user should send the elements of u to the
* actuators.
*
* @param dt Timestep for model update.
*/
void Predict(units::second_t dt) {
InputVector u =
ClampInput(m_controller->Calculate(m_observer->Xhat(), m_nextR) +
m_feedforward.Calculate(m_nextR));
m_observer->Predict(u, dt);
}
/**
* Clamps input vector between system's minimum and maximum allowable input.
*
* @param u Input vector to clamp.
* @return Clamped input vector.
*/
InputVector ClampInput(const InputVector& u) const { return m_clampFunc(u); }
protected:
LinearQuadraticRegulator<States, Inputs>* m_controller;
LinearPlantInversionFeedforward<States, Inputs> m_feedforward;
KalmanFilter<States, Inputs, Outputs>* m_observer;
/**
* Clamping function.
*/
std::function<InputVector(const InputVector&)> m_clampFunc;
// Reference to go to in the next cycle (used by feedforward controller).
StateVector m_nextR;
// These are accessible from non-templated subclasses.
static constexpr int kStates = States;
static constexpr int kInputs = Inputs;
static constexpr int kOutputs = Outputs;
};
extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
LinearSystemLoop<1, 1, 1>;
extern template class EXPORT_TEMPLATE_DECLARE(WPILIB_DLLEXPORT)
LinearSystemLoop<2, 1, 1>;
} // namespace frc