WPILibC++ 2026.1.1
Loading...
Searching...
No Matches
TrapezoidProfile.h
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 <type_traits>
8
9#include "units/math.h"
10#include "units/time.h"
11#include "wpimath/MathShared.h"
12
13namespace frc {
14
15/**
16 * A trapezoid-shaped velocity profile.
17 *
18 * While this class can be used for a profiled movement from start to finish,
19 * the intended usage is to filter a reference's dynamics based on trapezoidal
20 * velocity constraints. To compute the reference obeying this constraint, do
21 * the following.
22 *
23 * Initialization:
24 * @code{.cpp}
25 * TrapezoidProfile::Constraints constraints{kMaxV, kMaxA};
26 * double previousProfiledReference = initialReference;
27 * TrapezoidProfile profile{constraints};
28 * @endcode
29 *
30 * Run on update:
31 * @code{.cpp}
32 * previousProfiledReference = profile.Calculate(timeSincePreviousUpdate,
33 * previousProfiledReference,
34 * unprofiledReference);
35 * @endcode
36 *
37 * where `unprofiledReference` is free to change between calls. Note that when
38 * the unprofiled reference is within the constraints, `Calculate()` returns the
39 * unprofiled reference unchanged.
40 *
41 * Otherwise, a timer can be started to provide monotonic values for
42 * `Calculate()` and to determine when the profile has completed via
43 * `IsFinished()`.
44 */
45template <class Distance>
47 public:
48 using Distance_t = units::unit_t<Distance>;
49 using Velocity =
50 units::compound_unit<Distance, units::inverse<units::seconds>>;
51 using Velocity_t = units::unit_t<Velocity>;
53 units::compound_unit<Velocity, units::inverse<units::seconds>>;
54 using Acceleration_t = units::unit_t<Acceleration>;
55
56 /**
57 * Profile constraints.
58 */
60 public:
61 /// Maximum velocity.
63
64 /// Maximum acceleration.
66
67 /**
68 * Default constructor.
69 */
70 constexpr Constraints() {
71 if (!std::is_constant_evaluated()) {
74 }
75 }
76
77 /**
78 * Constructs constraints for a Trapezoid Profile.
79 *
80 * @param maxVelocity Maximum velocity, must be non-negative.
81 * @param maxAcceleration Maximum acceleration, must be non-negative.
82 */
86 if (!std::is_constant_evaluated()) {
89 }
90
92 throw std::domain_error("Constraints must be non-negative");
93 }
94 }
95 };
96
97 /**
98 * Profile state.
99 */
100 class State {
101 public:
102 /// The position at this state.
104
105 /// The velocity at this state.
107
108 constexpr bool operator==(const State&) const = default;
109 };
110
111 /**
112 * Constructs a TrapezoidProfile.
113 *
114 * @param constraints The constraints on the profile, like maximum velocity.
115 */
116 constexpr TrapezoidProfile(Constraints constraints) // NOLINT
117 : m_constraints(constraints) {}
118
119 constexpr TrapezoidProfile(const TrapezoidProfile&) = default;
120 constexpr TrapezoidProfile& operator=(const TrapezoidProfile&) = default;
121 constexpr TrapezoidProfile(TrapezoidProfile&&) = default;
123
124 /**
125 * Calculates the position and velocity for the profile at a time t where the
126 * current state is at time t = 0.
127 *
128 * @param t How long to advance from the current state toward the desired
129 * state.
130 * @param current The current state.
131 * @param goal The desired state when the profile is complete.
132 * @return The position and velocity of the profile at time t.
133 */
134 constexpr State Calculate(units::second_t t, State current, State goal) {
135 m_direction = ShouldFlipAcceleration(current, goal) ? -1 : 1;
136 m_current = Direct(current);
137 goal = Direct(goal);
138 if (units::math::abs(m_current.velocity) > m_constraints.maxVelocity) {
139 m_current.velocity =
140 units::math::copysign(m_constraints.maxVelocity, m_current.velocity);
141 }
142
143 // Deal with a possibly truncated motion profile (with nonzero initial or
144 // final velocity) by calculating the parameters as if the profile began and
145 // ended at zero velocity
146 units::second_t cutoffBegin =
147 m_current.velocity / m_constraints.maxAcceleration;
148 Distance_t cutoffDistBegin =
149 cutoffBegin * cutoffBegin * m_constraints.maxAcceleration / 2.0;
150
151 units::second_t cutoffEnd = goal.velocity / m_constraints.maxAcceleration;
152 Distance_t cutoffDistEnd =
153 cutoffEnd * cutoffEnd * m_constraints.maxAcceleration / 2.0;
154
155 // Now we can calculate the parameters as if it was a full trapezoid instead
156 // of a truncated one
157
158 Distance_t fullTrapezoidDist =
159 cutoffDistBegin + (goal.position - m_current.position) + cutoffDistEnd;
160 units::second_t accelerationTime =
161 m_constraints.maxVelocity / m_constraints.maxAcceleration;
162
163 Distance_t fullSpeedDist =
164 fullTrapezoidDist -
165 accelerationTime * accelerationTime * m_constraints.maxAcceleration;
166
167 // Handle the case where the profile never reaches full speed
168 if (fullSpeedDist < Distance_t{0}) {
169 accelerationTime =
170 units::math::sqrt(fullTrapezoidDist / m_constraints.maxAcceleration);
171 fullSpeedDist = Distance_t{0};
172 }
173
174 m_endAccel = accelerationTime - cutoffBegin;
175 m_endFullSpeed = m_endAccel + fullSpeedDist / m_constraints.maxVelocity;
176 m_endDecel = m_endFullSpeed + accelerationTime - cutoffEnd;
177 State result = m_current;
178
179 if (t < m_endAccel) {
180 result.velocity += t * m_constraints.maxAcceleration;
181 result.position +=
182 (m_current.velocity + t * m_constraints.maxAcceleration / 2.0) * t;
183 } else if (t < m_endFullSpeed) {
184 result.velocity = m_constraints.maxVelocity;
185 result.position += (m_current.velocity +
186 m_endAccel * m_constraints.maxAcceleration / 2.0) *
187 m_endAccel +
188 m_constraints.maxVelocity * (t - m_endAccel);
189 } else if (t <= m_endDecel) {
190 result.velocity =
191 goal.velocity + (m_endDecel - t) * m_constraints.maxAcceleration;
192 units::second_t timeLeft = m_endDecel - t;
193 result.position =
194 goal.position -
195 (goal.velocity + timeLeft * m_constraints.maxAcceleration / 2.0) *
196 timeLeft;
197 } else {
198 result = goal;
199 }
200
201 return Direct(result);
202 }
203
204 /**
205 * Returns the time left until a target distance in the profile is reached.
206 *
207 * @param target The target distance.
208 * @return The time left until a target distance in the profile is reached, or
209 * zero if no goal was set.
210 */
211 constexpr units::second_t TimeLeftUntil(Distance_t target) const {
212 Distance_t position = m_current.position * m_direction;
213 Velocity_t velocity = m_current.velocity * m_direction;
214
215 units::second_t endAccel = m_endAccel * m_direction;
216 units::second_t endFullSpeed = m_endFullSpeed * m_direction - endAccel;
217
218 if (target < position) {
219 endAccel *= -1.0;
220 endFullSpeed *= -1.0;
221 velocity *= -1.0;
222 }
223
224 endAccel = units::math::max(endAccel, 0_s);
225 endFullSpeed = units::math::max(endFullSpeed, 0_s);
226
227 const Acceleration_t acceleration = m_constraints.maxAcceleration;
228 const Acceleration_t deceleration = -m_constraints.maxAcceleration;
229
230 Distance_t distToTarget = units::math::abs(target - position);
231
232 if (distToTarget < Distance_t{1e-6}) {
233 return 0_s;
234 }
235
236 Distance_t accelDist =
237 velocity * endAccel + 0.5 * acceleration * endAccel * endAccel;
238
239 Velocity_t decelVelocity;
240 if (endAccel > 0_s) {
241 decelVelocity = units::math::sqrt(
242 units::math::abs(velocity * velocity + 2 * acceleration * accelDist));
243 } else {
244 decelVelocity = velocity;
245 }
246
247 Distance_t fullSpeedDist = m_constraints.maxVelocity * endFullSpeed;
248 Distance_t decelDist;
249
250 if (accelDist > distToTarget) {
251 accelDist = distToTarget;
252 fullSpeedDist = Distance_t{0};
253 decelDist = Distance_t{0};
254 } else if (accelDist + fullSpeedDist > distToTarget) {
255 fullSpeedDist = distToTarget - accelDist;
256 decelDist = Distance_t{0};
257 } else {
258 decelDist = distToTarget - fullSpeedDist - accelDist;
259 }
260
261 units::second_t accelTime =
262 (-velocity + units::math::sqrt(units::math::abs(
263 velocity * velocity + 2 * acceleration * accelDist))) /
264 acceleration;
265
266 units::second_t decelTime =
267 (-decelVelocity +
268 units::math::sqrt(units::math::abs(decelVelocity * decelVelocity +
269 2 * deceleration * decelDist))) /
270 deceleration;
271
272 units::second_t fullSpeedTime = fullSpeedDist / m_constraints.maxVelocity;
273
274 return accelTime + fullSpeedTime + decelTime;
275 }
276
277 /**
278 * Returns the total time the profile takes to reach the goal.
279 *
280 * @return The total time the profile takes to reach the goal, or zero if no
281 * goal was set.
282 */
283 constexpr units::second_t TotalTime() const { return m_endDecel; }
284
285 /**
286 * Returns true if the profile has reached the goal.
287 *
288 * The profile has reached the goal if the time since the profile started has
289 * exceeded the profile's total time.
290 *
291 * @param t The time since the beginning of the profile.
292 * @return True if the profile has reached the goal.
293 */
294 constexpr bool IsFinished(units::second_t t) const {
295 return t >= TotalTime();
296 }
297
298 private:
299 /**
300 * Returns true if the profile inverted.
301 *
302 * The profile is inverted if goal position is less than the initial position.
303 *
304 * @param initial The initial state (usually the current state).
305 * @param goal The desired state when the profile is complete.
306 */
307 static constexpr bool ShouldFlipAcceleration(const State& initial,
308 const State& goal) {
309 return initial.position > goal.position;
310 }
311
312 // Flip the sign of the velocity and position if the profile is inverted
313 constexpr State Direct(const State& in) const {
314 State result = in;
315 result.position *= m_direction;
316 result.velocity *= m_direction;
317 return result;
318 }
319
320 // The direction of the profile, either 1 for forwards or -1 for inverted
321 int m_direction = 1;
322
323 Constraints m_constraints;
324 State m_current;
325
326 units::second_t m_endAccel = 0_s;
327 units::second_t m_endFullSpeed = 0_s;
328 units::second_t m_endDecel = 0_s;
329};
330
331} // namespace frc
Profile constraints.
Definition TrapezoidProfile.h:59
constexpr Constraints(Velocity_t maxVelocity, Acceleration_t maxAcceleration)
Constructs constraints for a Trapezoid Profile.
Definition TrapezoidProfile.h:83
Velocity_t maxVelocity
Maximum velocity.
Definition TrapezoidProfile.h:62
constexpr Constraints()
Default constructor.
Definition TrapezoidProfile.h:70
Acceleration_t maxAcceleration
Maximum acceleration.
Definition TrapezoidProfile.h:65
Profile state.
Definition TrapezoidProfile.h:100
constexpr bool operator==(const State &) const =default
Velocity_t velocity
The velocity at this state.
Definition TrapezoidProfile.h:106
Distance_t position
The position at this state.
Definition TrapezoidProfile.h:103
A trapezoid-shaped velocity profile.
Definition TrapezoidProfile.h:46
constexpr TrapezoidProfile & operator=(const TrapezoidProfile &)=default
constexpr TrapezoidProfile(Constraints constraints)
Constructs a TrapezoidProfile.
Definition TrapezoidProfile.h:116
constexpr TrapezoidProfile & operator=(TrapezoidProfile &&)=default
constexpr TrapezoidProfile(const TrapezoidProfile &)=default
constexpr units::second_t TotalTime() const
Returns the total time the profile takes to reach the goal.
Definition TrapezoidProfile.h:283
units::compound_unit< Distance, units::inverse< units::seconds > > Velocity
Definition TrapezoidProfile.h:49
constexpr bool IsFinished(units::second_t t) const
Returns true if the profile has reached the goal.
Definition TrapezoidProfile.h:294
constexpr units::second_t TimeLeftUntil(Distance_t target) const
Returns the time left until a target distance in the profile is reached.
Definition TrapezoidProfile.h:211
constexpr TrapezoidProfile(TrapezoidProfile &&)=default
units::unit_t< Distance > Distance_t
Definition TrapezoidProfile.h:48
constexpr State Calculate(units::second_t t, State current, State goal)
Calculates the position and velocity for the profile at a time t where the current state is at time t...
Definition TrapezoidProfile.h:134
units::unit_t< Acceleration > Acceleration_t
Definition TrapezoidProfile.h:54
units::compound_unit< Velocity, units::inverse< units::seconds > > Acceleration
Definition TrapezoidProfile.h:52
units::unit_t< Velocity > Velocity_t
Definition TrapezoidProfile.h:51
static void ReportUsage(MathUsageId id, int count)
Definition MathShared.h:80
State
Possible state of a SysId routine.
Definition SysIdRoutineLog.h:25
Definition CAN.h:11