WPILibC++ 2027.0.0-alpha-4
Loading...
Searching...
No Matches
Models.hpp
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <stdexcept>
8
9#include <gcem.hpp>
10
13#include "wpi/units/acceleration.hpp"
14#include "wpi/units/angular_acceleration.hpp"
15#include "wpi/units/angular_velocity.hpp"
16#include "wpi/units/length.hpp"
17#include "wpi/units/moment_of_inertia.hpp"
18#include "wpi/units/velocity.hpp"
19#include "wpi/units/voltage.hpp"
21
22namespace wpi::math {
23/**
24 * Linear system factories.
25 */
27 public:
28 template <typename Distance>
29 using Velocity_t = wpi::units::unit_t<wpi::units::compound_unit<
30 Distance, wpi::units::inverse<wpi::units::seconds>>>;
31
32 template <typename Distance>
33 using Acceleration_t = wpi::units::unit_t<wpi::units::compound_unit<
34 wpi::units::compound_unit<Distance,
35 wpi::units::inverse<wpi::units::seconds>>,
36 wpi::units::inverse<wpi::units::seconds>>>;
37
38 /**
39 * Creates a flywheel state-space model from physical constants.
40 *
41 * The states are [angular velocity], the inputs are [voltage], and the
42 * outputs are [angular velocity].
43 *
44 * @param motor The motor (or gearbox) attached to the flywheel.
45 * @param J The moment of inertia J of the flywheel.
46 * @param gearing Gear ratio from motor to flywheel (greater than 1 is a
47 * reduction).
48 * @throws std::domain_error if J <= 0 or gearing <= 0.
49 */
51 DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
52 if (J <= 0_kg_sq_m) {
53 throw std::domain_error("J must be greater than zero.");
54 }
55 if (gearing <= 0.0) {
56 throw std::domain_error("gearing must be greater than zero.");
57 }
58
60 {(-gcem::pow(gearing, 2) * motor.Kt / (motor.Kv * motor.R * J))
61 .value()}};
62 Matrixd<1, 1> B{{(gearing * motor.Kt / (motor.R * J)).value()}};
63 Matrixd<1, 1> C{{1.0}};
64 Matrixd<1, 1> D{{0.0}};
65
66 return LinearSystem<1, 1, 1>(A, B, C, D);
67 }
68
69 /**
70 * Creates a flywheel state-space model from SysId constants kᵥ (V/(rad/s))
71 * and kₐ (V/(rad/s²)) from the feedforward model u = kᵥv + kₐa.
72 *
73 * The states are [velocity], the inputs are [voltage], and the outputs are
74 * [velocity].
75 *
76 * @param kV The velocity gain, in V/(rad/s).
77 * @param kA The acceleration gain, in V/(rad/s²).
78 * @throws std::domain_error if kV < 0 or kA <= 0.
79 * @see <a
80 * href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
81 */
83 decltype(1_V / 1_rad_per_s) kV, decltype(1_V / 1_rad_per_s_sq) kA) {
84 if (kV < decltype(kV){0}) {
85 throw std::domain_error("Kv must be greater than or equal to zero.");
86 }
87 if (kA <= decltype(kA){0}) {
88 throw std::domain_error("Ka must be greater than zero.");
89 }
90
91 Matrixd<1, 1> A{{-kV.value() / kA.value()}};
92 Matrixd<1, 1> B{{1.0 / kA.value()}};
93 Matrixd<1, 1> C{{1.0}};
94 Matrixd<1, 1> D{{0.0}};
95
96 return LinearSystem<1, 1, 1>(A, B, C, D);
97 }
98
99 /**
100 * Creates an elevator state-space model from physical constants.
101 *
102 * The states are [position, velocity], the inputs are [voltage], and the
103 * outputs are [position, velocity].
104 *
105 * @param motor The motor (or gearbox) attached to the carriage.
106 * @param mass The mass of the elevator carriage, in kilograms.
107 * @param radius The radius of the elevator's driving drum, in meters.
108 * @param gearing Gear ratio from motor to carriage (greater than 1 is a
109 * reduction).
110 * @throws std::domain_error if mass <= 0, radius <= 0, or gearing <= 0.
111 */
113 DCMotor motor, wpi::units::kilogram_t mass, wpi::units::meter_t radius,
114 double gearing) {
115 if (mass <= 0_kg) {
116 throw std::domain_error("mass must be greater than zero.");
117 }
118 if (radius <= 0_m) {
119 throw std::domain_error("radius must be greater than zero.");
120 }
121 if (gearing <= 0.0) {
122 throw std::domain_error("gearing must be greater than zero.");
123 }
124
126 {0.0, 1.0},
127 {0.0, (-gcem::pow(gearing, 2) * motor.Kt /
128 (motor.R * wpi::units::math::pow<2>(radius) * mass * motor.Kv))
129 .value()}};
130 Matrixd<2, 1> B{{0.0},
131 {(gearing * motor.Kt / (motor.R * radius * mass)).value()}};
132 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
133 Matrixd<2, 1> D{{0.0}, {0.0}};
134
135 return LinearSystem<2, 1, 2>(A, B, C, D);
136 }
137
138 /**
139 * Creates an elevator state-space model from SysId constants kᵥ (V/(m/s)) and
140 * kₐ (V/(m/s²)) from the feedforward model u = kᵥv + kₐa.
141 *
142 * The states are [position, velocity], the inputs are [voltage], and the
143 * outputs are [position, velocity].
144 *
145 * @param kV The velocity gain, in V/(m/s).
146 * @param kA The acceleration gain, in V/(m/s²).
147 *
148 * @throws std::domain_error if kV < 0 or kA <= 0.
149 * @see <a
150 * href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
151 */
153 decltype(1_V / 1_mps) kV, decltype(1_V / 1_mps_sq) kA) {
154 if (kV < decltype(kV){0}) {
155 throw std::domain_error("Kv must be greater than or equal to zero.");
156 }
157 if (kA <= decltype(kA){0}) {
158 throw std::domain_error("Ka must be greater than zero.");
159 }
160
161 Matrixd<2, 2> A{{0.0, 1.0}, {0.0, -kV.value() / kA.value()}};
162 Matrixd<2, 1> B{{0.0}, {1.0 / kA.value()}};
163 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
164 Matrixd<2, 1> D{{0.0}, {0.0}};
165
166 return LinearSystem<2, 1, 2>(A, B, C, D);
167 }
168
169 /**
170 * Create a single-jointed arm state-space model from physical constants.
171 *
172 * The states are [angle, angular velocity], the inputs are [voltage], and the
173 * outputs are [angle, angular velocity].
174 *
175 * @param motor The motor (or gearbox) attached to the arm.
176 * @param J The moment of inertia J of the arm.
177 * @param gearing Gear ratio from motor to arm (greater than 1 is a
178 * reduction).
179 * @throws std::domain_error if J <= 0 or gearing <= 0.
180 */
182 DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing) {
183 if (J <= 0_kg_sq_m) {
184 throw std::domain_error("J must be greater than zero.");
185 }
186 if (gearing <= 0.0) {
187 throw std::domain_error("gearing must be greater than zero.");
188 }
189
191 {0.0, 1.0},
192 {0.0, (-gcem::pow(gearing, 2) * motor.Kt / (motor.Kv * motor.R * J))
193 .value()}};
194 Matrixd<2, 1> B{{0.0}, {(gearing * motor.Kt / (motor.R * J)).value()}};
195 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
196 Matrixd<2, 1> D{{0.0}, {0.0}};
197
198 return LinearSystem<2, 1, 2>(A, B, C, D);
199 }
200
201 /**
202 * Creates a single-jointed arm state-space model from SysId constants kᵥ
203 * (V/(rad/s)) and kₐ (V/(rad/s²)) from the feedforward model u = kᵥv + kₐa.
204 *
205 * The states are [position, velocity], the inputs are [voltage], and the
206 * outputs are [position, velocity].
207 *
208 * @param kV The velocity gain, in volts/(unit/sec).
209 * @param kA The acceleration gain, in volts/(unit/sec²).
210 *
211 * @throws std::domain_error if kV < 0 or kA <= 0.
212 * @see <a
213 * href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
214 */
216 decltype(1_V / 1_rad_per_s) kV, decltype(1_V / 1_rad_per_s_sq) kA) {
217 if (kV < decltype(kV){0}) {
218 throw std::domain_error("Kv must be greater than or equal to zero.");
219 }
220 if (kA <= decltype(kA){0}) {
221 throw std::domain_error("Ka must be greater than zero.");
222 }
223
224 Matrixd<2, 2> A{{0.0, 1.0}, {0.0, -kV.value() / kA.value()}};
225 Matrixd<2, 1> B{{0.0}, {1.0 / kA.value()}};
226 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
227 Matrixd<2, 1> D{{0.0}, {0.0}};
228
229 return LinearSystem<2, 1, 2>(A, B, C, D);
230 }
231
232 /**
233 * Creates a differential drive state-space model from physical constants.
234 *
235 * The states are [left velocity, right velocity], the inputs are [left
236 * voltage, right voltage], and the outputs are [left velocity, right
237 * velocity].
238 *
239 * @param motor The motor (or gearbox) driving the drivetrain.
240 * @param mass The mass of the robot in kilograms.
241 * @param r The radius of the wheels in meters.
242 * @param rb The radius of the base (half of the trackwidth), in meters.
243 * @param J The moment of inertia of the robot.
244 * @param gearing Gear ratio from motor to wheel (greater than 1 is a
245 * reduction).
246 * @throws std::domain_error if mass <= 0, r <= 0, rb <= 0, J <= 0, or
247 * gearing <= 0.
248 */
250 const DCMotor& motor, wpi::units::kilogram_t mass, wpi::units::meter_t r,
251 wpi::units::meter_t rb, wpi::units::kilogram_square_meter_t J,
252 double gearing) {
253 if (mass <= 0_kg) {
254 throw std::domain_error("mass must be greater than zero.");
255 }
256 if (r <= 0_m) {
257 throw std::domain_error("r must be greater than zero.");
258 }
259 if (rb <= 0_m) {
260 throw std::domain_error("rb must be greater than zero.");
261 }
262 if (J <= 0_kg_sq_m) {
263 throw std::domain_error("J must be greater than zero.");
264 }
265 if (gearing <= 0.0) {
266 throw std::domain_error("gearing must be greater than zero.");
267 }
268
269 auto C1 = -gcem::pow(gearing, 2) * motor.Kt /
270 (motor.Kv * motor.R * wpi::units::math::pow<2>(r));
271 auto C2 = gearing * motor.Kt / (motor.R * r);
272
274 {((1 / mass + wpi::units::math::pow<2>(rb) / J) * C1).value(),
275 ((1 / mass - wpi::units::math::pow<2>(rb) / J) * C1).value()},
276 {((1 / mass - wpi::units::math::pow<2>(rb) / J) * C1).value(),
277 ((1 / mass + wpi::units::math::pow<2>(rb) / J) * C1).value()}};
279 {((1 / mass + wpi::units::math::pow<2>(rb) / J) * C2).value(),
280 ((1 / mass - wpi::units::math::pow<2>(rb) / J) * C2).value()},
281 {((1 / mass - wpi::units::math::pow<2>(rb) / J) * C2).value(),
282 ((1 / mass + wpi::units::math::pow<2>(rb) / J) * C2).value()}};
283 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
284 Matrixd<2, 2> D{{0.0, 0.0}, {0.0, 0.0}};
285
286 return LinearSystem<2, 2, 2>(A, B, C, D);
287 }
288
289 /**
290 * Creates a differential drive state-space model from SysId constants kᵥ and
291 * kₐ in both linear {(V/(m/s), (V/(m/s²))} and angular {(V/(rad/s),
292 * (V/(rad/s²))} cases.
293 *
294 * The states are [left velocity, right velocity], the inputs are [left
295 * voltage, right voltage], and the outputs are [left velocity, right
296 * velocity].
297 *
298 * @param kVLinear The linear velocity gain in volts per (meters per second).
299 * @param kALinear The linear acceleration gain in volts per (meters per
300 * second squared).
301 * @param kVAngular The angular velocity gain in volts per (meters per
302 * second).
303 * @param kAAngular The angular acceleration gain in volts per (meters per
304 * second squared).
305 * @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
306 * or kAAngular <= 0.
307 * @see <a
308 * href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
309 */
311 decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
312 decltype(1_V / 1_mps) kVAngular, decltype(1_V / 1_mps_sq) kAAngular) {
313 if (kVLinear <= decltype(kVLinear){0}) {
314 throw std::domain_error("Kv,linear must be greater than zero.");
315 }
316 if (kALinear <= decltype(kALinear){0}) {
317 throw std::domain_error("Ka,linear must be greater than zero.");
318 }
319 if (kVAngular <= decltype(kVAngular){0}) {
320 throw std::domain_error("Kv,angular must be greater than zero.");
321 }
322 if (kAAngular <= decltype(kAAngular){0}) {
323 throw std::domain_error("Ka,angular must be greater than zero.");
324 }
325
326 double A1 = -0.5 * (kVLinear.value() / kALinear.value() +
327 kVAngular.value() / kAAngular.value());
328 double A2 = -0.5 * (kVLinear.value() / kALinear.value() -
329 kVAngular.value() / kAAngular.value());
330 double B1 = 0.5 / kALinear.value() + 0.5 / kAAngular.value();
331 double B2 = 0.5 / kALinear.value() - 0.5 / kAAngular.value();
332
333 Matrixd<2, 2> A{{A1, A2}, {A2, A1}};
334 Matrixd<2, 2> B{{B1, B2}, {B2, B1}};
335 Matrixd<2, 2> C{{1.0, 0.0}, {0.0, 1.0}};
336 Matrixd<2, 2> D{{0.0, 0.0}, {0.0, 0.0}};
337
338 return LinearSystem<2, 2, 2>(A, B, C, D);
339 }
340
341 /**
342 * Creates a differential drive state-space model from SysId constants kᵥ and
343 * kₐ in both linear {(V/(m/s), (V/(m/s²))} and angular {(V/(rad/s),
344 * (V/(rad/s²))} cases.
345 *
346 * The states are [left velocity, right velocity], the inputs are [left
347 * voltage, right voltage], and the outputs are [left velocity, right
348 * velocity].
349 *
350 * @param kVLinear The linear velocity gain in volts per (meters per
351 * second).
352 * @param kALinear The linear acceleration gain in volts per (meters per
353 * second squared).
354 * @param kVAngular The angular velocity gain in volts per (radians per
355 * second).
356 * @param kAAngular The angular acceleration gain in volts per (radians per
357 * second squared).
358 * @param trackwidth The distance between the differential drive's left and
359 * right wheels, in meters.
360 * @throws domain_error if kVLinear <= 0, kALinear <= 0, kVAngular <= 0,
361 * kAAngular <= 0, or trackwidth <= 0.
362 * @see <a
363 * href="https://github.com/wpilibsuite/allwpilib/tree/main/sysid">https://github.com/wpilibsuite/allwpilib/tree/main/sysid</a>
364 */
366 decltype(1_V / 1_mps) kVLinear, decltype(1_V / 1_mps_sq) kALinear,
367 decltype(1_V / 1_rad_per_s) kVAngular,
368 decltype(1_V / 1_rad_per_s_sq) kAAngular,
369 wpi::units::meter_t trackwidth) {
370 if (kVLinear <= decltype(kVLinear){0}) {
371 throw std::domain_error("Kv,linear must be greater than zero.");
372 }
373 if (kALinear <= decltype(kALinear){0}) {
374 throw std::domain_error("Ka,linear must be greater than zero.");
375 }
376 if (kVAngular <= decltype(kVAngular){0}) {
377 throw std::domain_error("Kv,angular must be greater than zero.");
378 }
379 if (kAAngular <= decltype(kAAngular){0}) {
380 throw std::domain_error("Ka,angular must be greater than zero.");
381 }
382 if (trackwidth <= 0_m) {
383 throw std::domain_error("r must be greater than zero.");
384 }
385
386 // We want to find a factor to include in Kv,angular that will convert
387 // `u = Kv,angular omega` to `u = Kv,angular v`.
388 //
389 // v = omega r
390 // omega = v/r
391 // omega = 1/r v
392 // omega = 1/(trackwidth/2) v
393 // omega = 2/trackwidth v
394 //
395 // So multiplying by 2/trackwidth converts the angular gains from V/(rad/s)
396 // to V/(m/s).
397 return DifferentialDriveFromSysId(kVLinear, kALinear,
398 kVAngular * 2.0 / trackwidth * 1_rad,
399 kAAngular * 2.0 / trackwidth * 1_rad);
400 }
401};
402
403} // namespace wpi::math
#define WPILIB_DLLEXPORT
Definition SymbolExports.hpp:36
Holds the constants for a DC motor.
Definition DCMotor.hpp:19
radians_per_second_per_volt_t Kv
Motor velocity constant.
Definition DCMotor.hpp:47
newton_meters_per_ampere_t Kt
Motor torque constant.
Definition DCMotor.hpp:50
wpi::units::ohm_t R
Motor internal resistance.
Definition DCMotor.hpp:44
A plant defined using state-space notation.
Definition LinearSystem.hpp:35
Linear system factories.
Definition Models.hpp:26
wpi::units::unit_t< wpi::units::compound_unit< Distance, wpi::units::inverse< wpi::units::seconds > > > Velocity_t
Definition Models.hpp:29
static constexpr LinearSystem< 2, 2, 2 > DifferentialDriveFromSysId(decltype(1_V/1_mps) kVLinear, decltype(1_V/1_mps_sq) kALinear, decltype(1_V/1_rad_per_s) kVAngular, decltype(1_V/1_rad_per_s_sq) kAAngular, wpi::units::meter_t trackwidth)
Creates a differential drive state-space model from SysId constants kᵥ and kₐ in both linear {(V/(m/s...
Definition Models.hpp:365
wpi::units::unit_t< wpi::units::compound_unit< wpi::units::compound_unit< Distance, wpi::units::inverse< wpi::units::seconds > >, wpi::units::inverse< wpi::units::seconds > > > Acceleration_t
Definition Models.hpp:33
static constexpr LinearSystem< 2, 2, 2 > DifferentialDriveFromPhysicalConstants(const DCMotor &motor, wpi::units::kilogram_t mass, wpi::units::meter_t r, wpi::units::meter_t rb, wpi::units::kilogram_square_meter_t J, double gearing)
Creates a differential drive state-space model from physical constants.
Definition Models.hpp:249
static constexpr LinearSystem< 2, 1, 2 > ElevatorFromPhysicalConstants(DCMotor motor, wpi::units::kilogram_t mass, wpi::units::meter_t radius, double gearing)
Creates an elevator state-space model from physical constants.
Definition Models.hpp:112
static constexpr LinearSystem< 2, 2, 2 > DifferentialDriveFromSysId(decltype(1_V/1_mps) kVLinear, decltype(1_V/1_mps_sq) kALinear, decltype(1_V/1_mps) kVAngular, decltype(1_V/1_mps_sq) kAAngular)
Creates a differential drive state-space model from SysId constants kᵥ and kₐ in both linear {(V/(m/s...
Definition Models.hpp:310
static constexpr LinearSystem< 2, 1, 2 > SingleJointedArmFromSysId(decltype(1_V/1_rad_per_s) kV, decltype(1_V/1_rad_per_s_sq) kA)
Creates a single-jointed arm state-space model from SysId constants kᵥ (V/(rad/s)) and kₐ (V/(rad/s²)...
Definition Models.hpp:215
static constexpr LinearSystem< 1, 1, 1 > FlywheelFromPhysicalConstants(DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing)
Creates a flywheel state-space model from physical constants.
Definition Models.hpp:50
static constexpr LinearSystem< 2, 1, 2 > SingleJointedArmFromPhysicalConstants(DCMotor motor, wpi::units::kilogram_square_meter_t J, double gearing)
Create a single-jointed arm state-space model from physical constants.
Definition Models.hpp:181
static constexpr LinearSystem< 2, 1, 2 > ElevatorFromSysId(decltype(1_V/1_mps) kV, decltype(1_V/1_mps_sq) kA)
Creates an elevator state-space model from SysId constants kᵥ (V/(m/s)) and kₐ (V/(m/s²)) from the fe...
Definition Models.hpp:152
static constexpr LinearSystem< 1, 1, 1 > FlywheelFromSysId(decltype(1_V/1_rad_per_s) kV, decltype(1_V/1_rad_per_s_sq) kA)
Creates a flywheel state-space model from SysId constants kᵥ (V/(rad/s)) and kₐ (V/(rad/s²)) from the...
Definition Models.hpp:82
constexpr common_t< T1, T2 > pow(const T1 base, const T2 exp_term) noexcept
Compile-time power function.
Definition pow.hpp:82
Definition LinearSystem.hpp:20
Eigen::Matrix< double, Rows, Cols, Options, MaxRows, MaxCols > Matrixd
Definition EigenCore.hpp:21