You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ScurveProfile plans a time-optimal rest-to-rest move under velocity,
acceleration and jerk limits as seven constant-jerk segments. Short moves
lose the cruise, shorter ones lose the acceleration plateaus as well; all
three shapes come out of one construction with the unused segments at zero
duration, so sampling costs the same wherever in the profile it lands.
Sampling is O(1), noexcept and allocation-free, as is planning.
SynchronizedTrajectory drives every axis from a single path parameter
s: 0 -> 1. Planning each axis independently and stretching the faster ones
does synchronise the endpoints and keeps every axis inside its limits, but
it still bows the path because each axis retains its own profile shape and
the ratios between them drift through the move. Measured on a two-axis move:
13.6 mm off the straight line, against 1.1e-16 for the path-parameter form.
Two findings from the tests:
A short move produced a cruise segment of about 1e-17 seconds because
the cruise duration was computed from leftover distance in every case --
effectively asking for zero as the difference of two values agreeing to
fifteen digits. Numerically harmless, but hasCruisePhase() then reported
that the move cruised, and predicates are what callers branch on.
Cruise presence is now decided by a closed-form comparison.
Forward Euler at 1 kHz lags the commanded position by half a step of
velocity -- 1.0 mm on a 2 m/s move -- and the error cancels to exactly
zero by the end of a symmetric rest-to-rest profile. An acceptance test
checking only final position therefore passes even though the machine
was in the wrong place throughout the move. Both halves are measured;
the header explicitly directs callers to sample the profile rather than
integrate it.
MotionLimits defaults to zeros and validate() rejects them: an unconfigured
axis must be one that may not move, not one with no ceiling. Finiteness is
checked before positivity because a NaN limit passes <= 0 and then passes
every downstream bound check as well.
Expected moves into its own header and names its error type explicitly, so
the trajectory module can use it without depending on the frame graph. The
allocation probe is renamed to test_no_allocation.cpp now that its coverage
extends beyond the frame graph.
Also fixes MOTIONKIT_BUILD_BENCHMARKS, which previously failed at configure
time because add_subdirectory() pointed at an empty, untracked directory
that was absent from fresh checkouts. Benchmarks now exist, CI builds and
runs them, and the install-consumer job exercises the new headers so the
installed package contract covers the trajectory API.
As a supporting repository fix, line endings are pinned to LF so Windows
checkouts can execute the project scripts. With core.autocrlf=true and no
.gitattributes, scripts/format.sh acquired a /usr/bin/env bash\r shebang,
making it unrunnable under both WSL and Git Bash while Linux CI remained
unaffected.
31 new tests; 108 total, green under GCC and Clang in Debug and Release,
ASan/UBSan, TSan and clang-tidy.
**Unset limits mean the axis may not move.**`MotionLimits` defaults to zeros
192
+
and `validate()` rejects them. Reading an unset limit as "no limit" makes
193
+
forgetting to configure an axis indistinguishable from configuring it for full
194
+
speed, and the difference is only observable on the machine. Finiteness is
195
+
checked before positivity, because a NaN limit passes `<= 0` and then passes
196
+
every bound check downstream too — comparisons against NaN are false however
197
+
they are written.
198
+
151
199
**Euler angles are an export format, never a representation.**
152
200
`toRPY` recovers pitch via `atan2(-m₂₀, hypot(m₀₀, m₁₀))` rather than
153
201
`asin(-m₂₀)`, for the same conditioning reason — and tool-down poses sit exactly
@@ -167,8 +215,8 @@ test wrong.
167
215
|---|---|
168
216
| GCC + Clang × Debug + Release |`-Wconversion` and `-Wold-style-cast` fire on different constructs per compiler |
169
217
|`-Werror` with `-Wconversion -Wsign-conversion -Wold-style-cast -Wshadow`| Silent narrowing in a pose pipeline is a field failure, not a warning |
170
-
| ASan + UBSan on all 77 tests, `-fno-sanitize-recover=all`| A UBSan finding fails the build rather than printing a note |
171
-
| TSan on the 73 ordinary tests | Ahead of the threaded executor in WP-08; the four allocator-interposition tests are excluded because TSan defines the same global allocation hooks |
218
+
| ASan + UBSan on all 108 tests, `-fno-sanitize-recover=all`| A UBSan finding fails the build rather than printing a note |
219
+
| TSan on the 101 ordinary tests | Ahead of the threaded executor in WP-08; the seven allocator-interposition tests are excluded because TSan defines the same global allocation hooks |
172
220
| clang-tidy, `--warnings-as-errors=*`| Rule set and exclusions justified in ADR-0002 |
173
221
|`scripts/format.sh --check` with clang-format 18 | Formatting is not a review topic, and CI runs the same check developers run |
174
222
|**install with repository tests off + downstream consumer compile and run**| Exercises only the installed package contract; it caught a real bug on first run when the exported target was `motionkit::motionkit_core` but consumers used `motionkit::core`|
@@ -181,7 +229,7 @@ Unit tests assert known values; the interesting ones assert **properties** over
181
229
thousands of uniformly sampled rotations from a fixed seed — a property test you
182
230
cannot replay is a flake, not a test.
183
231
184
-
Four allocation tests are instrumentation rather than ordinary unit tests. They
232
+
Seven allocation tests are instrumentation rather than ordinary unit tests. They
185
233
run in their own executable because their global `operator new`/`operator delete`
186
234
replacements affect an entire process. That target alone suppresses GNU's
187
235
`-Wmismatched-new-delete` diagnostic: the `malloc`/`free` pairing is deliberate
@@ -197,6 +245,38 @@ and is the mechanism being tested. The warning remains enabled everywhere else.
197
245
198
246
---
199
247
248
+
## Benchmarks
249
+
250
+
The claim that these operations are callable from a cyclic task is only worth as
251
+
much as the number, so there is a number. No google-benchmark: the library takes
252
+
no third-party dependencies, and a benchmark you cannot build straight after
0 commit comments