Skip to content

Commit 690f252

Browse files
committed
feat(motion_profiles): add S-curve (jerk-limited) profile
Deploy roadmap item 60: seven-phase rest-to-rest profile planned with the closed-form three-step method of Biagiotti & Melchiorri §3.4 (vMax reached; else aMax reached; else neither). The boundary states are integrated once, and each sample evaluates one constant-jerk cubic. Add math::Cbrt (generic wrapper, no ARM override), which the step-3 branch needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q53m7MZDmHwocmKbhhd2o5
1 parent e205500 commit 690f252

16 files changed

Lines changed: 463 additions & 255 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Refer to the documentation to quickly integrate and utilize the library's signal
2424
| [Optimization](doc/optimization/README.md) | Gradient Descent, SGD / Adam Step Optimisers |
2525
| [Regularization](doc/regularization/README.md) | L1 (Lasso), L2 (Ridge) |
2626
| [Math](doc/math/README.md) | CORDIC, Quaternion, MatrixNorms, Step Response Metrics, MatrixExponential, Angle Utilities, Tridiagonal (Thomas) Solver |
27-
| [Motion Profiles](doc/motion_profiles/README.md) | Polynomial (Cubic/Quintic) Point-to-Point Profile, Trapezoidal (LSPB) Profile |
27+
| [Motion Profiles](doc/motion_profiles/README.md) | Polynomial (Cubic/Quintic) Point-to-Point Profile, Trapezoidal (LSPB) Profile, S-Curve (Jerk-Limited) Profile |
2828
| [Solvers](doc/solvers/README.md) | Gaussian Elimination, Levinson-Durbin, Durand-Kerner, Cholesky, DARE, Runge-Kutta ODE Integrators (RK4 + Dormand-Prince), Spectral Radius & Discrete Stability Margin, QR Decomposition (Householder / Givens), LU Decomposition with Partial Pivoting, Singular Value Decomposition (Golub-Kahan), Symplectic Integrators (Semi-Implicit Euler, Velocity Verlet) |
2929
| [Nonlinear Control](doc/nonlinear_control/README.md) | Feedback Linearization, Backstepping Control, Model Reference Adaptive Control (MRAC) |
3030
| [Robust Control](doc/robust_control/README.md) | Active Disturbance Rejection Control (ADRC + ESO), Sliding Mode Control (SMC), Disturbance Observer (DOB), H∞ State-Feedback Control |

‎ROADMAP.md‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ Difficulty legend:
2727
| # | Component | Target module | Difficulty |
2828
|----|------------------------------------------------------|-------------------------|------------|
2929
| 53 | SE(3) transform, twists, wrenches, adjoint, exp/log | `math` | ★★☆☆☆ |
30-
| 60 | S-curve (jerk-limited) profile | `motion_profiles` (new) | ★★☆☆☆ |
3130
| 61 | Cubic spline interpolation (vector-valued) | `math` | ★★☆☆☆ |
3231
| 62 | Damped pseudo-inverse + null-space projector | `solvers` | ★★☆☆☆ |
3332
| 63 | Incremental QR least squares (rank-revealing) | `estimators/offline` | ★★★☆☆ |
@@ -57,13 +56,6 @@ the library does not yet expose. Detailed below under
5756
- **Reuses:** `Geometry3D` (`RotationAboutAxis`, `SkewSymmetric`), `Quaternion`.
5857
- **Spec:** [roadmap/math/SE3Transform](roadmap/math/SE3Transform/implementation.md).
5958

60-
### 60. S-curve (jerk-limited) profile
61-
- **What:** Seven-segment jerk-bounded rest-to-rest move with all short-move cases in closed form.
62-
- **Embedded value:** Continuous acceleration — far less vibration than a trapezoid for precision axes.
63-
- **Algorithm / paper:** L. Biagiotti, C. Melchiorri (2008), §3.4.
64-
- **Reuses:** Items 55 (types) and 56 (synchronization scheme).
65-
- **Spec:** [roadmap/motion_profiles/SCurveProfile](roadmap/motion_profiles/SCurveProfile/implementation.md).
66-
6759
### 61. Cubic spline interpolation
6860
- **What:** C² vector-valued spline through `K` knots, clamped or natural ends, shared factorization across
6961
channels.

‎doc/motion_profiles/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Time laws for point-to-point motion of a single axis: position, velocity, accele
88
|-------------------------------------------------|---------------------------------------------------------------------------------------------|
99
| [Polynomial Profile](PolynomialProfile.md) | Cubic (position + velocity) or quintic (+ acceleration) boundary-matched time law over a fixed duration |
1010
| [Trapezoidal Profile](TrapezoidalProfile.md) | Minimum-time trapezoid / triangle velocity profile (LSPB) and a fixed-duration plan for multi-axis synchronization |
11+
| [S-Curve Profile](SCurveProfile.md) | Seven-phase jerk-limited rest-to-rest profile with a closed-form three-step planner — continuous acceleration |
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# S-Curve (Jerk-Limited) Profile
2+
3+
## Overview & Motivation
4+
5+
A trapezoidal velocity profile bounds speed and acceleration, but acceleration jumps instantaneously at every corner. Those jumps are impulses in jerk: they excite structural resonances, wear gearing, and make payloads wobble or liquids spill. The S-curve (double-S) profile also bounds **jerk**, so acceleration ramps linearly in and out and is continuous, giving far less vibration than a trapezoid. The price is a slightly longer move.
6+
7+
For a rest-to-rest move the phase durations have a closed form: planning is a handful of arithmetic operations, with no iteration. Three limits drive it: a velocity ceiling $v_{\max}$, an acceleration ceiling $a_{\max}$ and a jerk ceiling $j_{\max}$. Short moves never reach the ceilings; the corresponding phases shrink to zero length.
8+
9+
## Mathematical Theory
10+
11+
### Phase Structure
12+
13+
A rest-to-rest move of length $h > 0$ is built from seven constant-jerk phases:
14+
15+
| Phase | Jerk | Acceleration | Duration |
16+
|-------|------|--------------|----------|
17+
| 1 | $+j_{\max}$ | ramps $0 \to a_{lim}$ | $T_j$ |
18+
| 2 | $0$ | constant $a_{lim}$ | $T_a - 2T_j$ |
19+
| 3 | $-j_{\max}$ | ramps $a_{lim} \to 0$ | $T_j$ |
20+
| 4 | $0$ | zero, cruise at $v_{lim}$ | $T_v$ |
21+
| 5 | $-j_{\max}$ | ramps $0 \to -a_{lim}$ | $T_j$ |
22+
| 6 | $0$ | constant $-a_{lim}$ | $T_a - 2T_j$ |
23+
| 7 | $+j_{\max}$ | ramps $-a_{lim} \to 0$ | $T_j$ |
24+
25+
Phases 1–3 form the acceleration stage of length $T_a$; phases 5–7 mirror it, so the profile is symmetric about its midpoint and the total time is $T = 2T_a + T_v$. Integrating the stage gives the peaks and the distance:
26+
27+
$$a_{lim} = j_{\max} T_j, \qquad v_{lim} = a_{lim}\,(T_a - T_j), \qquad h = v_{lim}\,(T_a + T_v)$$
28+
29+
### Closed-Form Three-Step Planner
30+
31+
This is the case $v_0 = v_1 = 0$ of Biagiotti and Melchiorri, §3.4. Each step assumes one more ceiling is unreachable.
32+
33+
**Step 1 — assume $v_{\max}$ is reached** ($v_{lim} = v_{\max}$):
34+
35+
$$
36+
\text{if } v_{\max} j_{\max} \ge a_{\max}^2:\; T_j = \frac{a_{\max}}{j_{\max}},\; T_a = T_j + \frac{v_{\max}}{a_{\max}}
37+
\qquad\text{else}\qquad
38+
T_j = \sqrt{\frac{v_{\max}}{j_{\max}}},\; T_a = 2T_j
39+
$$
40+
41+
$$T_v = \frac{h}{v_{\max}} - T_a$$
42+
43+
The first case has an acceleration plateau; the second has a pure triangular ramp with peak $j_{\max}T_j < a_{\max}$. Here $T = h/v_{\max} + T_a$, so the jerk limit costs exactly $a_{\max}/j_{\max}$ over a trapezoid with the same $v_{\max}, a_{\max}$.
44+
45+
**Step 2 — if $T_v < 0$**, set $T_v = 0$ and assume $a_{\max}$ is still reached, $T_j = a_{\max}/j_{\max}$. Then $h = a_{\max} T_a (T_a - T_j)$ is a quadratic in $T_a$:
46+
47+
$$\Delta = \frac{a_{\max}^4}{j_{\max}^2} + 4\,a_{\max} h, \qquad T_a = \frac{a_{\max}^2/j_{\max} + \sqrt{\Delta}}{2\,a_{\max}}$$
48+
49+
**Step 3 — if $T_a < 2T_j$**, the plateau would have negative length, so $a_{\max}$ is unreachable too. Both ramps are triangles, $T_a = 2T_j$ and $h = 2 j_{\max} T_j^3$:
50+
51+
$$T_j = \sqrt[3]{\frac{h}{2\,j_{\max}}}, \qquad T_a = 2T_j$$
52+
53+
Step 2 applies when $h < v_{\max} T_a$, and step 3 when $h < 2 a_{\max}^3 / j_{\max}^2$ (from $T_a \ge 2T_j \iff \Delta \ge 9 a_{\max}^4/j_{\max}^2$). Neighbouring branches agree on $T$ at each threshold, so $T$ is continuous in $h$. No duration is ever negative: in step 1, $T_a - 2T_j = v_{\max}/a_{\max} - a_{\max}/j_{\max} \ge 0$ is the branch condition itself; step 2 is guarded by the step-3 test; step 3 has $T_a - 2T_j = 0$. Zero-length phases collapse out of the profile.
54+
55+
### Exact Evaluation
56+
57+
Within a phase of jerk $j_i$ starting from state $(s_i, v_i, a_i)$, with local time $\tau$:
58+
59+
$$a = a_i + j_i\tau, \qquad v = v_i + a_i\tau + \tfrac12 j_i\tau^2, \qquad s = s_i + v_i\tau + \tfrac12 a_i\tau^2 + \tfrac16 j_i\tau^3$$
60+
61+
The seven boundary states are integrated exactly once, at planning time, with these polynomials. Each sample then locates its phase and evaluates a cubic in time. At and beyond $T$ the profile holds the goal at rest; before $t = 0$ it holds the start. A negative move direction negates the offset, velocity, acceleration and jerk.
62+
63+
### Time Scaling for Synchronization
64+
65+
Replanning with $(v_{\max}/\lambda,\; a_{\max}/\lambda^2,\; j_{\max}/\lambda^3)$ stretches an axis by $\lambda \ge 1$. Every planner term scales consistently ($T_j \to \lambda T_j$, $T_a \to \lambda T_a$, $\Delta \to \lambda^{-2}\Delta$) and the branch tests are invariant, so the duration scales by exactly $\lambda$ in every branch. Axes stretched to a common $T$ finish together.
66+
67+
## Complexity Analysis
68+
69+
| Operation | Time | Space | Notes |
70+
|-----------|------|-------|-------|
71+
| Plan | $O(1)$ | $O(1)$ | At most one square root and one cube root, no iteration |
72+
| Sample | $O(1)$ | $O(1)$ | Locate one of seven phases, evaluate one cubic |
73+
74+
Stored state is a few fixed-size tables of phase boundaries on the stack; no heap is used. Cost is independent of $h$ and of the branch taken.
75+
76+
## Step-by-Step Walkthrough
77+
78+
Limits $(v_{\max}, a_{\max}, j_{\max}) = (1, 2, 10)$, $h = 0.3$.
79+
80+
1. Step 1: $v_{\max} j_{\max} = 10 \ge 4$, so $T_j = 0.2$, $T_a = 0.7$, $T_v = 0.3 - 0.7 = -0.4 < 0$.
81+
2. Step 2: $T_v = 0$, $\Delta = 0.16 + 2.4 = 2.56$, $T_a = (0.4 + 1.6)/4 = 0.5 \ge 2T_j = 0.4$, so step 3 is not needed. Then $a_{lim} = 2$, $v_{lim} = 2(0.5 - 0.2) = 0.6$, $T = 1.0$.
82+
83+
Boundary states $(a, v, s)$ of the acceleration stage: at $t = 0.2$, $(2,\, 0.2,\, 0.0133)$; at $t = 0.3$, $(2,\, 0.4,\, 0.0433)$; at $t = 0.5$, $(0,\, 0.6,\, 0.15)$. The mirrored stage ends at $s = 0.3$, at rest. A sample at $t = 0.4$ lies in phase 3 with $\tau = 0.1$: $a = 1$, $v = 0.55$, $s = 0.0917$.
84+
85+
| $(v_{\max}, a_{\max}, j_{\max})$ | $h$ | Branch | $T_j$ | $T_a$ | $T_v$ | $T$ |
86+
|----------------------------------|-----|--------|-------|-------|-------|-----|
87+
| (1, 2, 10) | 5 | 1, plateau | 0.2 | 0.7 | 4.3 | 5.7 |
88+
| (1, 2, 1) | 5 | 1, triangle | 1 | 2 | 3 | 7 |
89+
| (1, 2, 10) | 0.3 | 2 | 0.2 | 0.5 | 0 | 1.0 |
90+
| (1, 2, 10) | 0.02 | 3 | 0.1 | 0.2 | 0 | 0.4 |
91+
92+
For $(1, 2, 10)$ the thresholds are $h \ge 0.7$ for step 1 and $h \ge 2a_{\max}^3/j_{\max}^2 = 0.16$ for step 2. The last row has $a_{lim} = 1$ and $v_{lim} = 0.1$.
93+
94+
## Pitfalls & Edge Cases
95+
96+
- **Positive limits.** The velocity, acceleration and jerk ceilings must all be strictly positive.
97+
- **Zero distance.** With $h = 0$ every phase has zero length; the profile is the start state, at rest.
98+
- **Rest-to-rest only.** Non-zero boundary velocities need the iterative acceleration reduction of the general case, which is not covered.
99+
- **Float-only.** Only floating-point types are supported; there is no fixed-point variant.
100+
- **Validation.** On 20 000 random $(h, v_{\max}, a_{\max}, j_{\max})$ plans, every plan ended at $h$ at rest and never exceeded the limits.
101+
102+
## Variants & Generalizations
103+
104+
- **Trapezoid** — the limit $j_{\max} \to \infty$: jerk phases vanish and acceleration steps.
105+
- **Non-zero boundary velocities** — same seven phases, with the acceleration ceiling reduced iteratively until the move is feasible.
106+
107+
## Applications
108+
109+
- **Pick-and-place, CNC, semiconductor handling** — fast moves without exciting resonances or payload sway; time-scaled axes finish together.
110+
111+
## Connections to Other Algorithms
112+
113+
| Algorithm | Relationship |
114+
|-----------|--------------|
115+
| Trapezoidal profile | Jerk-unbounded predecessor; shares the synchronization scheme |
116+
| Quintic polynomial profile | Fixed duration and smooth, but limits are not enforced directly |
117+
118+
## References & Further Reading
119+
120+
- Biagiotti, L. and Melchiorri, C., *Trajectory Planning for Automatic Machines and Robots*, Springer, 2008, §3.4.

‎numerical/math/Math.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ template float math::Abs<float>(float);
88
#ifndef MATH_SQRT_OVERRIDE
99
template float math::Sqrt<float>(float);
1010
#endif
11+
#ifndef MATH_CBRT_OVERRIDE
12+
template float math::Cbrt<float>(float);
13+
#endif
1114
#ifndef MATH_SIN_OVERRIDE
1215
template float math::Sin<float>(float);
1316
#endif

‎numerical/math/Math.hpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ namespace math
2525
}
2626
#endif
2727

28+
#ifndef MATH_CBRT_OVERRIDE
29+
template<typename T>
30+
constexpr T Cbrt(T x)
31+
{
32+
static_assert(std::is_floating_point_v<T>, "T must be a floating-point type");
33+
return std::cbrt(x);
34+
}
35+
#endif
36+
2837
#ifndef MATH_SIN_OVERRIDE
2938
template<typename T>
3039
constexpr T Sin(T x)
@@ -252,6 +261,9 @@ namespace math
252261
#ifndef MATH_SQRT_OVERRIDE
253262
extern template float Sqrt<float>(float);
254263
#endif
264+
#ifndef MATH_CBRT_OVERRIDE
265+
extern template float Cbrt<float>(float);
266+
#endif
255267
#ifndef MATH_SIN_OVERRIDE
256268
extern template float Sin<float>(float);
257269
#endif

‎numerical/math/test/TestMath.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ TEST_F(TestMath, Copysign_transfers_sign)
127127
EXPECT_FLOAT_EQ(math::Copysign(-3.0f, 1.0f), 3.0f);
128128
}
129129

130+
TEST_F(TestMath, Cbrt_known_value)
131+
{
132+
EXPECT_NEAR(math::Cbrt(27.0f), 3.0f, kEps);
133+
EXPECT_NEAR(math::Cbrt(-0.001f), -0.1f, kEps);
134+
}
135+
130136
TEST_F(TestMath, Fmod_known_value)
131137
{
132138
EXPECT_NEAR(math::Fmod(5.5f, 2.0f), 1.5f, kEps);

‎numerical/motion_profiles/CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ target_link_libraries(numerical.motion_profiles ${NUMERICAL_VISIBILITY}
1313
target_sources(numerical.motion_profiles PRIVATE
1414
MotionProfileTypes.hpp
1515
PolynomialProfile.hpp
16+
SCurveProfile.hpp
1617
TrapezoidalProfile.hpp
1718
)
1819

1920
numerical_add_coverage_sources(numerical.motion_profiles
2021
PolynomialProfile.cpp
22+
SCurveProfile.cpp
2123
TrapezoidalProfile.cpp
2224
)
2325

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "numerical/motion_profiles/SCurveProfile.hpp"
2+
3+
namespace motion_profiles
4+
{
5+
template class SCurveProfile<float>;
6+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#pragma once
2+
3+
#include "infra/util/ReallyAssert.hpp"
4+
#include "numerical/math/CompilerOptimizations.hpp"
5+
#include "numerical/math/Math.hpp"
6+
#include "numerical/motion_profiles/MotionProfileTypes.hpp"
7+
#include <algorithm>
8+
#include <array>
9+
#include <cstddef>
10+
#include <type_traits>
11+
12+
namespace motion_profiles
13+
{
14+
template<typename T>
15+
class SCurveProfile
16+
{
17+
static_assert(std::is_floating_point_v<T>, "SCurveProfile supports floating-point types only");
18+
19+
public:
20+
SCurveProfile(T q0, T qf, const MotionLimits<T>& limits);
21+
22+
OPTIMIZE_FOR_SPEED TrajectoryState<T> Sample(T t) const;
23+
T Duration() const;
24+
bool ReachesMaxAccel() const;
25+
bool ReachesMaxVel() const;
26+
27+
private:
28+
static constexpr std::size_t phases{ 7 };
29+
static constexpr std::array<T, phases> jerkSign{ T{ 1 }, T{}, T{ -1 }, T{}, T{ -1 }, T{}, T{ 1 } };
30+
31+
void Plan(const MotionLimits<T>& limits);
32+
void Integrate(T jerkPhase, T accelPhase, T cruisePhase);
33+
34+
T q0;
35+
T direction;
36+
T distance;
37+
T jMax;
38+
std::array<T, phases + 1> segStart{};
39+
std::array<T, phases + 1> segPos{};
40+
std::array<T, phases + 1> segVel{};
41+
std::array<T, phases + 1> segAcc{};
42+
T tf{};
43+
bool reachesMaxAccel{ false };
44+
bool reachesMaxVel{ false };
45+
};
46+
47+
template<typename T>
48+
SCurveProfile<T>::SCurveProfile(T q0, T qf, const MotionLimits<T>& limits)
49+
: q0{ q0 }
50+
, direction{ qf >= q0 ? T{ 1 } : T{ -1 } }
51+
, distance{ math::Abs(qf - q0) }
52+
, jMax{ limits.jMax }
53+
{
54+
really_assert(limits.vMax > T{});
55+
really_assert(limits.aMax > T{});
56+
really_assert(limits.jMax > T{});
57+
58+
if (distance > T{})
59+
Plan(limits);
60+
}
61+
62+
template<typename T>
63+
void SCurveProfile<T>::Plan(const MotionLimits<T>& limits)
64+
{
65+
const T vMax{ limits.vMax };
66+
const T aMax{ limits.aMax };
67+
68+
T tj{ aMax / jMax };
69+
T ta{ tj + vMax / aMax };
70+
if (vMax * jMax < aMax * aMax)
71+
{
72+
tj = math::Sqrt(vMax / jMax);
73+
ta = T{ 2 } * tj;
74+
}
75+
76+
T tv{ distance / vMax - ta };
77+
if (tv < T{})
78+
{
79+
tv = T{};
80+
tj = aMax / jMax;
81+
const T delta{ aMax * aMax * aMax * aMax / (jMax * jMax) + T{ 4 } * aMax * distance };
82+
ta = (aMax * aMax / jMax + math::Sqrt(delta)) / (T{ 2 } * aMax);
83+
84+
if (ta < T{ 2 } * tj)
85+
{
86+
tj = math::Cbrt(distance / (T{ 2 } * jMax));
87+
ta = T{ 2 } * tj;
88+
}
89+
}
90+
91+
const T accelPhase{ std::max(ta - T{ 2 } * tj, T{}) };
92+
reachesMaxAccel = accelPhase > T{};
93+
reachesMaxVel = tv > T{};
94+
Integrate(tj, accelPhase, tv);
95+
}
96+
97+
template<typename T>
98+
void SCurveProfile<T>::Integrate(T jerkPhase, T accelPhase, T cruisePhase)
99+
{
100+
const std::array<T, phases> duration{ jerkPhase, accelPhase, jerkPhase, cruisePhase, jerkPhase, accelPhase, jerkPhase };
101+
102+
for (std::size_t i = 0; i < phases; ++i)
103+
{
104+
const T tau{ duration[i] };
105+
const T jerk{ jerkSign[i] * jMax };
106+
segPos[i + 1] = segPos[i] + tau * (segVel[i] + tau * (segAcc[i] / T{ 2 } + tau * jerk / T{ 6 }));
107+
segVel[i + 1] = segVel[i] + tau * (segAcc[i] + tau * jerk / T{ 2 });
108+
segAcc[i + 1] = segAcc[i] + tau * jerk;
109+
segStart[i + 1] = segStart[i] + tau;
110+
}
111+
112+
tf = segStart[phases];
113+
}
114+
115+
template<typename T>
116+
OPTIMIZE_FOR_SPEED TrajectoryState<T> SCurveProfile<T>::Sample(T t) const
117+
{
118+
t = std::max(t, T{});
119+
if (t >= tf)
120+
return { q0 + direction * distance, T{}, T{}, T{} };
121+
122+
std::size_t i{ 0 };
123+
while (i + 1 < phases && segStart[i + 1] <= t)
124+
++i;
125+
126+
const T tau{ t - segStart[i] };
127+
const T jerk{ jerkSign[i] * jMax };
128+
return {
129+
q0 + direction * (segPos[i] + tau * (segVel[i] + tau * (segAcc[i] / T{ 2 } + tau * jerk / T{ 6 }))),
130+
direction * (segVel[i] + tau * (segAcc[i] + tau * jerk / T{ 2 })),
131+
direction * (segAcc[i] + tau * jerk),
132+
direction * jerk
133+
};
134+
}
135+
136+
template<typename T>
137+
T SCurveProfile<T>::Duration() const
138+
{
139+
return tf;
140+
}
141+
142+
template<typename T>
143+
bool SCurveProfile<T>::ReachesMaxAccel() const
144+
{
145+
return reachesMaxAccel;
146+
}
147+
148+
template<typename T>
149+
bool SCurveProfile<T>::ReachesMaxVel() const
150+
{
151+
return reachesMaxVel;
152+
}
153+
154+
#ifdef NUMERICAL_TOOLBOX_COVERAGE_BUILD
155+
extern template class SCurveProfile<float>;
156+
#endif
157+
}

0 commit comments

Comments
 (0)