WPILibC++ 2027.0.0-alpha-2
Loading...
Searching...
No Matches
MathUtil.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 <numbers>
8#include <type_traits>
9
10#include <gcem.hpp>
11#include <wpi/SymbolExports.h>
12
15#include "units/angle.h"
16#include "units/base.h"
17#include "units/length.h"
18#include "units/math.h"
19#include "units/time.h"
20#include "units/velocity.h"
21#include "wpimath/MathShared.h"
22
23namespace frc {
24
25/**
26 * Returns 0.0 if the given value is within the specified range around zero. The
27 * remaining range between the deadband and the maximum magnitude is scaled from
28 * 0.0 to the maximum magnitude.
29 *
30 * @param value Value to clip.
31 * @param deadband Range around zero.
32 * @param maxMagnitude The maximum magnitude of the input (defaults to 1). Can
33 * be infinite.
34 * @return The value after the deadband is applied.
35 */
36template <typename T>
37 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
38constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
39 T magnitude;
40 if constexpr (std::is_arithmetic_v<T>) {
41 magnitude = gcem::abs(value);
42 } else {
43 magnitude = units::math::abs(value);
44 }
45
46 if (magnitude < deadband) {
47 return T{0.0};
48 }
49
50 if (value > T{0.0}) {
51 // Map deadband to 0 and map max to max with a linear relationship.
52 //
53 // y - y₁ = m(x - x₁)
54 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
55 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
56 //
57 // (x₁, y₁) = (deadband, 0) and (x₂, y₂) = (max, max).
58 //
59 // x₁ = deadband
60 // y₁ = 0
61 // x₂ = max
62 // y₂ = max
63 // y = (max - 0)/(max - deadband) (x - deadband) + 0
64 // y = max/(max - deadband) (x - deadband)
65 //
66 // To handle high values of max, rewrite so that max only appears on the
67 // denominator.
68 //
69 // y = ((max - deadband) + deadband)/(max - deadband) (x - deadband)
70 // y = (1 + deadband/(max - deadband)) (x - deadband)
71 return (1.0 + deadband / (maxMagnitude - deadband)) * (value - deadband);
72 } else {
73 // Map -deadband to 0 and map -max to -max with a linear relationship.
74 //
75 // y - y₁ = m(x - x₁)
76 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
77 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
78 //
79 // (x₁, y₁) = (-deadband, 0) and (x₂, y₂) = (-max, -max).
80 //
81 // x₁ = -deadband
82 // y₁ = 0
83 // x₂ = -max
84 // y₂ = -max
85 // y = (-max - 0)/(-max + deadband) (x + deadband) + 0
86 // y = max/(max - deadband) (x + deadband)
87 //
88 // To handle high values of max, rewrite so that max only appears on the
89 // denominator.
90 //
91 // y = ((max - deadband) + deadband)/(max - deadband) (x + deadband)
92 // y = (1 + deadband/(max - deadband)) (x + deadband)
93 return (1.0 + deadband / (maxMagnitude - deadband)) * (value + deadband);
94 }
95}
96
97/**
98 * Raises the input to the power of the given exponent while preserving its
99 * sign.
100 *
101 * The function normalizes the input value to the range [0, 1] based on the
102 * maximum magnitude, raises it to the power of the exponent, then scales the
103 * result back to the original range and copying the sign. This keeps the value
104 * in the original range and gives consistent curve behavior regardless of the
105 * input value's scale.
106 *
107 * This is useful for applying smoother or more aggressive control response
108 * curves (e.g. joystick input shaping).
109 *
110 * @param value The input value to transform.
111 * @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared
112 * curve). Must be positive.
113 * @param maxMagnitude The maximum expected absolute value of input. Must be
114 * positive.
115 * @return The transformed value with the same sign and scaled to the input
116 * range.
117 */
118template <typename T>
119 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
120constexpr T CopySignPow(T value, double exponent, T maxMagnitude = T{1.0}) {
121 if constexpr (std::is_arithmetic_v<T>) {
122 return gcem::copysign(
123 gcem::pow(gcem::abs(value) / maxMagnitude, exponent) * maxMagnitude,
124 value);
125 } else {
127 gcem::pow((units::math::abs(value) / maxMagnitude).value(), exponent) *
128 maxMagnitude,
129 value);
130 }
131}
132
133/**
134 * Returns modulus of input.
135 *
136 * @param input Input value to wrap.
137 * @param minimumInput The minimum value expected from the input.
138 * @param maximumInput The maximum value expected from the input.
139 */
140template <typename T>
141constexpr T InputModulus(T input, T minimumInput, T maximumInput) {
142 T modulus = maximumInput - minimumInput;
143
144 // Wrap input if it's above the maximum input
145 int numMax = (input - minimumInput) / modulus;
146 input -= numMax * modulus;
147
148 // Wrap input if it's below the minimum input
149 int numMin = (input - maximumInput) / modulus;
150 input -= numMin * modulus;
151
152 return input;
153}
154
155/**
156 * Checks if the given value matches an expected value within a certain
157 * tolerance.
158 *
159 * @param expected The expected value
160 * @param actual The actual value
161 * @param tolerance The allowed difference between the actual and the expected
162 * value
163 * @return Whether or not the actual value is within the allowed tolerance
164 */
165template <typename T>
166 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
167constexpr bool IsNear(T expected, T actual, T tolerance) {
168 if constexpr (std::is_arithmetic_v<T>) {
169 return std::abs(expected - actual) < tolerance;
170 } else {
171 return units::math::abs(expected - actual) < tolerance;
172 }
173}
174
175/**
176 * Checks if the given value matches an expected value within a certain
177 * tolerance. Supports continuous input for cases like absolute encoders.
178 *
179 * Continuous input means that the min and max value are considered to be the
180 * same point, and tolerances can be checked across them. A common example
181 * would be for absolute encoders: calling isNear(2, 359, 5, 0, 360) returns
182 * true because 359 is 1 away from 360 (which is treated as the same as 0) and
183 * 2 is 2 away from 0, adding up to an error of 3 degrees, which is within the
184 * given tolerance of 5.
185 *
186 * @param expected The expected value
187 * @param actual The actual value
188 * @param tolerance The allowed difference between the actual and the expected
189 * value
190 * @param min Smallest value before wrapping around to the largest value
191 * @param max Largest value before wrapping around to the smallest value
192 * @return Whether or not the actual value is within the allowed tolerance
193 */
194template <typename T>
195 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
196constexpr bool IsNear(T expected, T actual, T tolerance, T min, T max) {
197 T errorBound = (max - min) / 2.0;
198 T error = frc::InputModulus<T>(expected - actual, -errorBound, errorBound);
199
200 if constexpr (std::is_arithmetic_v<T>) {
201 return std::abs(error) < tolerance;
202 } else {
203 return units::math::abs(error) < tolerance;
204 }
205}
206
207/**
208 * Wraps an angle to the range -π to π radians (-180 to 180 degrees).
209 *
210 * @param angle Angle to wrap.
211 */
213constexpr units::radian_t AngleModulus(units::radian_t angle) {
215 units::radian_t{-std::numbers::pi},
216 units::radian_t{std::numbers::pi});
217}
218
219// floorDiv and floorMod algorithms taken from Java
220
221/**
222 * Returns the largest (closest to positive infinity)
223 * {@code int} value that is less than or equal to the algebraic quotient.
224 *
225 * @param x the dividend
226 * @param y the divisor
227 * @return the largest (closest to positive infinity)
228 * {@code int} value that is less than or equal to the algebraic quotient.
229 */
230constexpr std::signed_integral auto FloorDiv(std::signed_integral auto x,
231 std::signed_integral auto y) {
232 auto quot = x / y;
233 auto rem = x % y;
234 // if the signs are different and modulo not zero, round down
235 if ((x < 0) != (y < 0) && rem != 0) {
236 --quot;
237 }
238 return quot;
239}
240
241/**
242 * Returns the floor modulus of the {@code int} arguments.
243 * <p>
244 * The floor modulus is {@code r = x - (floorDiv(x, y) * y)},
245 * has the same sign as the divisor {@code y} or is zero, and
246 * is in the range of {@code -std::abs(y) < r < +std::abs(y)}.
247 *
248 * @param x the dividend
249 * @param y the divisor
250 * @return the floor modulus {@code x - (floorDiv(x, y) * y)}
251 */
252constexpr std::signed_integral auto FloorMod(std::signed_integral auto x,
253 std::signed_integral auto y) {
254 return x - FloorDiv(x, y) * y;
255}
256
257/**
258 * Limits translation velocity.
259 *
260 * @param current Translation at current timestep.
261 * @param next Translation at next timestep.
262 * @param dt Timestep duration.
263 * @param maxVelocity Maximum translation velocity.
264 * @return Returns the next Translation2d limited to maxVelocity
265 */
267 const Translation2d& next,
268 units::second_t dt,
269 units::meters_per_second_t maxVelocity) {
270 if (maxVelocity < 0_mps) {
272 "maxVelocity must be a non-negative number, got {}!", maxVelocity);
273 return next;
274 }
275 Translation2d diff = next - current;
276 units::meter_t dist = diff.Norm();
277 if (dist < 1e-9_m) {
278 return next;
279 }
280 if (dist > maxVelocity * dt) {
281 // Move maximum allowed amount in direction of the difference
282 return current + diff * (maxVelocity * dt / dist);
283 }
284 return next;
285}
286
287/**
288 * Limits translation velocity.
289 *
290 * @param current Translation at current timestep.
291 * @param next Translation at next timestep.
292 * @param dt Timestep duration.
293 * @param maxVelocity Maximum translation velocity.
294 * @return Returns the next Translation3d limited to maxVelocity
295 */
297 const Translation3d& next,
298 units::second_t dt,
299 units::meters_per_second_t maxVelocity) {
300 if (maxVelocity < 0_mps) {
302 "maxVelocity must be a non-negative number, got {}!", maxVelocity);
303 return next;
304 }
305 Translation3d diff = next - current;
306 units::meter_t dist = diff.Norm();
307 if (dist < 1e-9_m) {
308 return next;
309 }
310 if (dist > maxVelocity * dt) {
311 // Move maximum allowed amount in direction of the difference
312 return current + diff * (maxVelocity * dt / dist);
313 }
314 return next;
315}
316
317} // namespace frc
#define WPILIB_DLLEXPORT
Definition SymbolExports.h:36
Represents a translation in 2D space.
Definition Translation2d.h:29
constexpr units::meter_t Norm() const
Returns the norm, or distance from the origin to the translation.
Definition Translation2d.h:106
Represents a translation in 3D space.
Definition Translation3d.h:30
constexpr units::meter_t Norm() const
Returns the norm, or distance from the origin to the translation.
Definition Translation3d.h:134
static void ReportError(const S &format, Args &&... args)
Definition MathShared.h:48
constexpr UnitType abs(const UnitType x) noexcept
Compute absolute value.
Definition math.h:726
constexpr UnitTypeLhs copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept
Copy sign.
Definition math.h:615
@ value
the parser finished reading a JSON value
Definition SystemServer.h:9
constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude=T{1.0})
Returns 0.0 if the given value is within the specified range around zero.
Definition MathUtil.h:38
constexpr std::signed_integral auto FloorMod(std::signed_integral auto x, std::signed_integral auto y)
Returns the floor modulus of the int arguments.
Definition MathUtil.h:252
WPILIB_DLLEXPORT constexpr units::radian_t AngleModulus(units::radian_t angle)
Wraps an angle to the range -π to π radians (-180 to 180 degrees).
Definition MathUtil.h:213
constexpr bool IsNear(T expected, T actual, T tolerance)
Checks if the given value matches an expected value within a certain tolerance.
Definition MathUtil.h:167
constexpr std::signed_integral auto FloorDiv(std::signed_integral auto x, std::signed_integral auto y)
Returns the largest (closest to positive infinity) int value that is less than or equal to the algebr...
Definition MathUtil.h:230
constexpr Translation2d SlewRateLimit(const Translation2d &current, const Translation2d &next, units::second_t dt, units::meters_per_second_t maxVelocity)
Limits translation velocity.
Definition MathUtil.h:266
constexpr T InputModulus(T input, T minimumInput, T maximumInput)
Returns modulus of input.
Definition MathUtil.h:141
constexpr T CopySignPow(T value, double exponent, T maxMagnitude=T{1.0})
Raises the input to the power of the given exponent while preserving its sign.
Definition MathUtil.h:120
constexpr T abs(const T x) noexcept
Compile-time absolute value function.
Definition abs.hpp:40
constexpr T1 copysign(const T1 x, const T2 y) noexcept
Compile-time copy sign function.
Definition copysign.hpp:41
constexpr common_t< T1, T2 > pow(const T1 base, const T2 exp_term) noexcept
Compile-time power function.
Definition pow.hpp:82
constexpr bool is_unit_t_v
Definition base.h:1870