Skip to main content

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.

Corner blending and speed profile

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.

Trapezoidal vs S-curve profiles

(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_radius is the cut-back distance along each adjacent leg of the corner, not an arc radius: a blend replaces the final r of the incoming segment and the first r of 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 via blend_radii) where exact pass-through matters.
  • Corners within ~2.6 degrees of a full reversal (the path doubles back on itself) cannot be blended: Hybrid falls back to a full stop at the waypoint, UseBlending raises a ValidationError.
  • An S-curve trajectory is jerk-limited end to end only with the default Hermite blends. Forcing blend_shape = Parabolic on 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 by T/duration, accelerations quadratically, jerks cubically, so all limits remain satisfied). Durations shorter than the current one raise a ValidationError.

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

justblend-0.1.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (204.7 kB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (201.3 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (201.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (200.9 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (199.5 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (198.4 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (198.7 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

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

Hashes for justblend-0.1.0.tar.gz
Algorithm Hash digest
SHA256 cf8ee6163d506a97958bd23de71a759e37c6cff7a45c019cfcf727d31295929a
MD5 9232e5ebf3655e78f1bfe50d245aa9ad
BLAKE2b-256 711feaf62bcb9f08a5ca9dcbf7578927a2ac0982dcd3cc25cb65d2516b31f25d

See more details on using hashes here.

Provenance

The following attestation bundles were made for justblend-0.1.0.tar.gz:

Publisher: publish.yml on justagist/justblend

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 1580b8eca418e5b4a641a81c5b4f61fef7189ad595596b9985beeca8d320732c
MD5 e867683b6cabfc77c7fb59610e9e69b8
BLAKE2b-256 cf0b5cccba08c5118b853de94294e7423de6dd9edabf129600cddd55bc444641

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 184eca870c213bf68d7da048f99a4012195607dce739eb61b0eb8517aabe5296
MD5 bbbe690bf09f540c4a4b48297450cbca
BLAKE2b-256 679e01c0aa1e6271f41de315cb882ff8e363162bb7b9c927cb69958ca9dd3077

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 bbb392bc8be726d3ef46a620ee507dfae0a5ff1e24e7e9f4d325cae01863512a
MD5 d88c6957ff984cde20155afcbf211885
BLAKE2b-256 93e81561eeef2b405fd5c630fbd04e3b752d2626a41985f48fcd5293ace2b471

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 f1f62ced56754bcd6eff593a7d28f7d537450782d209e61661281b842c5b0f97
MD5 84a4846d0cccd5049add6e7cd0bf6948
BLAKE2b-256 bbbe13b3c4c05a5ab1fb95120d585c4dd1ca495050dc19e4d94ba74fd2de6b33

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 bb59d5aef635f013f3e9bf98a36f982413747ccf3e6098288c3df027204734a6
MD5 dbe723eebba38e8d15a9a94d17c0b8c9
BLAKE2b-256 a2d1ae84643ebbfc12a2e56a2b50e2c4c9647cd2c9bb0ec647a62f1c435ec4c0

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c6fdc3589cc975501e919e0b7154f2b16e6653f0f58e895aeb4ece920835c330
MD5 45ef9da7fa02a3396332bd211ba1818b
BLAKE2b-256 cdace1eb2d3794422255b5c4b872eaabe9eac506409f1158404e52a2598d07b9

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

File hashes

Hashes for justblend-0.1.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 5e9667c8f37ce492a2afe1d764b22347b7b31a2df2d212328b11e424f727ee2c
MD5 59377913c027cfbe60cf857519e833fd
BLAKE2b-256 2866bdd85d56baac5839812fc7a517c40d3df95fd434c94dc0e3261cfc9310a9

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page