Closed-form N-dimensional trajectory generator with velocity/acceleration/jerk limiting and smooth corner blends
Project description
justblend
Closed-form N-dimensional trajectory generation with smooth corner blends. General-purpose: joint trajectories, Cartesian paths, CNC tool paths, animation curves -- anywhere you have a list of points in some D-dimensional space and want a velocity/acceleration/jerk-limited smooth trajectory through them.
Highlights
- Closed form, microsecond generation. No optimisation loop, no discretisation: typical multi-waypoint problems generate in roughly 0.05-0.1 ms and match ruckig's per-segment time-optimal durations on polyline motions (see benchmarks).
- Two velocity profiles. Bang-bang trapezoidal for the fastest rest-to-rest motion, or 7-phase jerk-limited S-curve when jerk limits are given.
- Three corner modes, two blend shapes. Stop at every waypoint, blend through corners, or a hybrid that blends where feasible. The cubic-Hermite blend has zero acceleration at its boundaries, so an S-curve trajectory stays jerk-limited through the corner.
- Analytic guarantees. Limits are satisfied by construction; the corner-cut deviation and per-waypoint passage times come back in closed form.
- Boundary speeds and duration stretching for moving-start replanning and speed control.
- C++17 and Python. Static library with clean CMake exports; pybind11 bindings with type stubs.
The output Trajectory is lazy: it owns a list of analytic segments and
can be sampled at any time t in [0, duration] without precomputing a
dense buffer.
(figures regenerate with python docs/generate_readme_figures.py)
Getting started -- C++
#include <justblend/justblend.hpp>
Eigen::MatrixXd waypoints(4, 2);
waypoints << 0.0, 0.0,
1.0, 0.5,
1.5,-0.2,
0.0, 0.0;
justblend::Limits limits;
limits.v_max = (Eigen::VectorXd(2) << 1.5, 1.2).finished();
limits.a_max = (Eigen::VectorXd(2) << 3.0, 2.5).finished();
limits.j_max = (Eigen::VectorXd(2) << 20.0, 18.0).finished();
justblend::GenerationOptions opts;
opts.blend_radius = 0.15;
opts.corner_handling = justblend::CornerHandling::HYBRID;
justblend::SCurveTrajectoryGenerator gen(2);
gen.setLimits(limits);
gen.setOptions(opts);
auto traj = gen.generate(waypoints);
auto s = traj.sample(0.5); // single sample
auto dense = traj.samples(0.002); // uniform dt sweep
Getting started -- Python
import numpy as np
import justblend as jb
waypoints = np.array([[0.0, 0.0], [1.0, 0.5], [1.5, -0.2], [0.0, 0.0]])
limits = jb.Limits(
v_max=np.array([1.5, 1.2]),
a_max=np.array([3.0, 2.5]),
j_max=np.array([20.0, 18.0]),
)
opts = jb.GenerationOptions(
blend_radius=0.15,
corner_handling=jb.CornerHandling.HYBRID,
)
gen = jb.SCurveTrajectoryGenerator(dim=2)
gen.set_limits(limits)
gen.set_options(opts)
traj = gen.generate(waypoints)
s = traj.sample(0.5)
dense = traj.samples(dt=0.002)
arbitrary = traj.samples(np.linspace(0, traj.duration(), 1000))
Corner semantics
blend_radiusis the cut-back distance along each adjacent leg of the corner, not an arc radius: a blend replaces the finalrof the incoming segment and the firstrof the outgoing one.- Blended corners cut the corner -- the trajectory does not pass through a
blended interior waypoint. Use
StrictCorners(or a per-corner radius viablend_radii) where exact pass-through matters. - Corners within ~2.6 degrees of a full reversal (the path doubles back on
itself) cannot be blended:
Hybridfalls back to a full stop at the waypoint,UseBlendingraises aValidationError. - An S-curve trajectory is jerk-limited end to end only with the default
Hermite blends. Forcing
blend_shape = Parabolicon the S-curve generator keeps the velocity and acceleration limits but introduces acceleration steps at blend boundaries, i.e. unbounded jerk there.
All input-validation failures throw justblend::ValidationError, which the
Python bindings raise as a ValueError subclass.
Boundary speeds, timing and deviation queries
generate() accepts optional tangential boundary speeds along the first and
last segment direction; infeasible requests (too fast for the limits or for
the available distance) raise a ValidationError:
auto traj = gen.generate(waypoints, /*v_start=*/0.3, /*v_end=*/0.0);
traj = gen.generate(waypoints, v_start=0.3, v_end=0.0)
GenerationOptions carries velocity_scaling_factor and
acceleration_scaling_factor in (0, 1] (matching MoveIt's semantics):
they scale v_max / a_max at generation time; j_max is never scaled.
The Trajectory exposes timing and corner-cut metadata:
segment_start_times()-- start time of each segment plus the total duration as the last entry.waypoint_times()-- passage time per waypoint; for blended corners this is the time of closest approach (the blend midpoint).corner_deviations()/max_corner_deviation()-- closed-form distance between each blended corner's waypoint and the blend path ((r/4)·|d_out - d_in|for parabolic,(3r/16)·|d_out - d_in|for Hermite blends; zero for non-blend corners). Useful to bound how far the blended trajectory strays from the original polyline.stretched_to(duration)-- uniformly time-dilated copy with exactly the requested total duration (same path; velocities scale byT/duration, accelerations quadratically, jerks cubically, so all limits remain satisfied). Durations shorter than the current one raise aValidationError.
Install -- C++
Requirements: CMake ≥ 3.18, a C++17 compiler, and Eigen 3.3+ (any package
that exposes find_package(Eigen3 NO_MODULE) -- the Debian/Ubuntu
libeigen3-dev package, vcpkg, conan, brew, or a manual install all work).
Build and install to a prefix
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
-DJUSTBLEND_BUILD_TESTS=OFF \
-DJUSTBLEND_BUILD_EXAMPLES=OFF \
-DJUSTBLEND_BUILD_PYTHON_BINDINGS=OFF
cmake --build build --parallel
cmake --install build --prefix /your/install/prefix # omit --prefix for /usr/local
This installs:
<prefix>/include/justblend/*.hpp<prefix>/lib/libjustblend.a<prefix>/lib/cmake/justblend/justblend{Config,ConfigVersion,Targets}.cmake
Use from another CMake project
After installing, consume justblend with find_package:
find_package(justblend 0.1.0 REQUIRED)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE justblend::justblend)
Then configure your project with -DCMAKE_PREFIX_PATH=/your/install/prefix
(or set justblend_DIR=<prefix>/lib/cmake/justblend).
You don't have to install: justblend also exports its targets from the
build tree, so pointing CMAKE_PREFIX_PATH at the build directory works
just as well.
To vendor justblend as a subdirectory (git submodule, FetchContent, or a
plain copy), drop add_subdirectory(path/to/justblend) into your
CMakeLists.txt -- the same justblend::justblend target becomes
available, and the test / example / Python-binding targets stay OFF by
default when consumed this way.
Run tests and examples
cmake -S . -B build -DJUSTBLEND_BUILD_TESTS=ON -DJUSTBLEND_BUILD_EXAMPLES=ON
cmake --build build --parallel
ctest --test-dir build --output-on-failure
./build/examples/cpp/example_basic_2d
Install -- Python
pip install .
pip install fetches scikit-build-core and pybind11 automatically and
drives the same CMake build under the hood -- no separate C++ install step
needed. Eigen 3.3+ must still be installed on the host.
Benchmarks
justblend is benchmarked against ruckig and toppra on shared scenarios;
instructions, result tables and plots live in
benchmarks/README.md (regenerated by
python benchmarks/run.py).
License
MIT -- see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file justblend-0.1.0.tar.gz.
File metadata
- Download URL: justblend-0.1.0.tar.gz
- Upload date:
- Size: 24.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf8ee6163d506a97958bd23de71a759e37c6cff7a45c019cfcf727d31295929a
|
|
| MD5 |
9232e5ebf3655e78f1bfe50d245aa9ad
|
|
| BLAKE2b-256 |
711feaf62bcb9f08a5ca9dcbf7578927a2ac0982dcd3cc25cb65d2516b31f25d
|
Provenance
The following attestation bundles were made for justblend-0.1.0.tar.gz:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0.tar.gz -
Subject digest:
cf8ee6163d506a97958bd23de71a759e37c6cff7a45c019cfcf727d31295929a - Sigstore transparency entry: 2195005265
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 204.7 kB
- Tags: CPython 3.14t, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1580b8eca418e5b4a641a81c5b4f61fef7189ad595596b9985beeca8d320732c
|
|
| MD5 |
e867683b6cabfc77c7fb59610e9e69b8
|
|
| BLAKE2b-256 |
cf0b5cccba08c5118b853de94294e7423de6dd9edabf129600cddd55bc444641
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
1580b8eca418e5b4a641a81c5b4f61fef7189ad595596b9985beeca8d320732c - Sigstore transparency entry: 2195005305
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 201.3 kB
- Tags: CPython 3.14, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
184eca870c213bf68d7da048f99a4012195607dce739eb61b0eb8517aabe5296
|
|
| MD5 |
bbbe690bf09f540c4a4b48297450cbca
|
|
| BLAKE2b-256 |
679e01c0aa1e6271f41de315cb882ff8e363162bb7b9c927cb69958ca9dd3077
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
184eca870c213bf68d7da048f99a4012195607dce739eb61b0eb8517aabe5296 - Sigstore transparency entry: 2195005296
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 201.0 kB
- Tags: CPython 3.13, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbb392bc8be726d3ef46a620ee507dfae0a5ff1e24e7e9f4d325cae01863512a
|
|
| MD5 |
d88c6957ff984cde20155afcbf211885
|
|
| BLAKE2b-256 |
93e81561eeef2b405fd5c630fbd04e3b752d2626a41985f48fcd5293ace2b471
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
bbb392bc8be726d3ef46a620ee507dfae0a5ff1e24e7e9f4d325cae01863512a - Sigstore transparency entry: 2195005281
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 200.9 kB
- Tags: CPython 3.12, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1f62ced56754bcd6eff593a7d28f7d537450782d209e61661281b842c5b0f97
|
|
| MD5 |
84a4846d0cccd5049add6e7cd0bf6948
|
|
| BLAKE2b-256 |
bbbe13b3c4c05a5ab1fb95120d585c4dd1ca495050dc19e4d94ba74fd2de6b33
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
f1f62ced56754bcd6eff593a7d28f7d537450782d209e61661281b842c5b0f97 - Sigstore transparency entry: 2195005291
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 199.5 kB
- Tags: CPython 3.11, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb59d5aef635f013f3e9bf98a36f982413747ccf3e6098288c3df027204734a6
|
|
| MD5 |
dbe723eebba38e8d15a9a94d17c0b8c9
|
|
| BLAKE2b-256 |
a2d1ae84643ebbfc12a2e56a2b50e2c4c9647cd2c9bb0ec647a62f1c435ec4c0
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
bb59d5aef635f013f3e9bf98a36f982413747ccf3e6098288c3df027204734a6 - Sigstore transparency entry: 2195005298
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 198.4 kB
- Tags: CPython 3.10, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6fdc3589cc975501e919e0b7154f2b16e6653f0f58e895aeb4ece920835c330
|
|
| MD5 |
45ef9da7fa02a3396332bd211ba1818b
|
|
| BLAKE2b-256 |
cdace1eb2d3794422255b5c4b872eaabe9eac506409f1158404e52a2598d07b9
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
c6fdc3589cc975501e919e0b7154f2b16e6653f0f58e895aeb4ece920835c330 - Sigstore transparency entry: 2195005277
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type:
File details
Details for the file justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 198.7 kB
- Tags: CPython 3.9, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e9667c8f37ce492a2afe1d764b22347b7b31a2df2d212328b11e424f727ee2c
|
|
| MD5 |
59377913c027cfbe60cf857519e833fd
|
|
| BLAKE2b-256 |
2866bdd85d56baac5839812fc7a517c40d3df95fd434c94dc0e3261cfc9310a9
|
Provenance
The following attestation bundles were made for justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on justagist/justblend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
5e9667c8f37ce492a2afe1d764b22347b7b31a2df2d212328b11e424f727ee2c - Sigstore transparency entry: 2195005287
- Sigstore integration time:
-
Permalink:
justagist/justblend@74e9dcbcec1b6e5cf141029548214f049481192d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/justagist
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@74e9dcbcec1b6e5cf141029548214f049481192d -
Trigger Event:
push
-
Statement type: