InterpolatePy 3.0.1__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/docs.yml +10 -10
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/pre-commit.yml +3 -3
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/publish.yml +2 -2
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/workflows/test.yml +3 -3
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/PKG-INFO +1 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/SOURCES.txt +2 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/PKG-INFO +1 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/quaternion-interpolation.md +1 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/log_quat_new_ex.py +0 -4
- interpolatepy-3.1.0/examples/lqi_spiral_frenet_ex.py +342 -0
- interpolatepy-3.1.0/examples/mlqi_spiral_frenet_ex.py +210 -0
- interpolatepy-3.1.0/interpolatepy/log_quat.py +470 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_core.py +13 -4
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/version.py +1 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/pyproject.toml +4 -1
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_log_quat.py +2 -219
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/uv.lock +62 -0
- interpolatepy-3.0.1/examples/log_quat_ex.py +0 -207
- interpolatepy-3.0.1/interpolatepy/log_quat.py +0 -950
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.editorconfig +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.gitattributes +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.github/FUNDING.yml +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.gitignore +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.pre-commit-config.yaml +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/.python-version +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/ALGORITHMS.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/dependency_links.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/requires.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/InterpolatePy.egg-info/top_level.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/LICENSE +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/README.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/codecov.yml +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/CMakeLists.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/CMakeLists.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_acc_splines.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_bspline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_cubic_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_motion.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_paths.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_quaternion.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_smoothing_search.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_smoothing_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/bind_tridiagonal.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/bindings/module.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/cmake/InterpolateCppConfig.cmake.in +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/cmake/version.hpp.in +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/CMakeLists.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_approx_smooth_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_cubic_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/bspline_interpolator_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/concepts_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_smoothing_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_acc1_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_acc2_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/cubic_spline_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/double_s_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/parabolic_linear_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/paths_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/polynomial_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/quaternion_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/shared/example_utils.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/examples/trapezoidal_example.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/approximation_bspline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline_interpolator.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/bspline_parameters.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/cubic_bspline_interpolation.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/bspline/smoothing_cubic_bspline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/concepts.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/config.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/double_s_trajectory.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/motion_types.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/parabolic_blend_trajectory.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/polynomial_trajectory.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/motion/trapezoidal_trajectory.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/circular_path.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/frenet_frame.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/linear_path.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/path/linear_traj.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/log_quaternion_interpolation.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/modified_log_quaternion_interpolation.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/quaternion.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/quaternion_spline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/quat/squad_c2.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_smoothing_spline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline_with_acc1.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/cubic_spline_with_acc2.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/smoothing_search.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/spline/spline_parameters.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/include/interpolatecpp/tridiagonal.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/approximation_bspline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/bspline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/bspline_interpolator.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/circular_path.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_bspline_interpolation.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_smoothing_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline_with_acc1.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/cubic_spline_with_acc2.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/double_s_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/frenet_frame.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/linear_path.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/linear_traj.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/log_quaternion_interpolation.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/modified_log_quaternion_interpolation.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/parabolic_blend_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/polynomial_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/quaternion.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/quaternion_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/smoothing_cubic_bspline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/smoothing_search.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/squad_c2.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/src/trapezoidal_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/CMakeLists.txt +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/shared/test_data.hpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_bspline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_bspline_variants.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_concepts.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_smoothing_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline_with_acc1.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_cubic_spline_with_acc2.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_double_s_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_parabolic_blend_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_paths.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_polynomial_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_quaternion.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_quaternion_spline.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_smoothing_search.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_trapezoidal_trajectory.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/cpp/tests/test_tridiagonal.cpp +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/algorithms.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/api-reference.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/architecture.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/assets/extra.css +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/assets/extra.js +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/changelog.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/contributing.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/examples.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/index.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/installation.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/quickstart.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/troubleshooting.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/motion-profiles.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/path-planning.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/tutorials/spline-interpolation.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/docs/user-guide.md +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_approx_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_cubic_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_interpolate_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/b_spline_smooth_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_smoot_search_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_smoothing_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_with_acc1_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/c_s_with_acc2_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/cubic_spline_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/double_s_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/frenet_frame_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/lin_poly_parabolic_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/linear_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/main.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/polynomials_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/protocols_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/quat_visualization_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/simple_paths_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/squad_c2_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/examples/trapezoidal_ex.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/__init__.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/__init__.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_bspline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_direct.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_motion.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_paths.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_quaternion.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_adapters/_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_api.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/_backend.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_approx.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_cubic.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_interpolate.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/b_spline_smooth.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_smoot_search.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_smoothing.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_with_acc1.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/c_s_with_acc2.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/cubic_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/double_s.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/frenet_frame.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/lin_poly_parabolic.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/linear.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/polynomials.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/protocols.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/quat_visualization.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/simple_paths.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/squad_c2.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/trapezoidal.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/interpolatepy/tridiagonal_inv.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/mkdocs.yml +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/setup.cfg +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/__init__.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/inv_test.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_b_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_b_spline_variants.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_backend.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_cubic_spline.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_lin_poly_parabolic.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_linear.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_motion_profiles.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_path_planning.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_polynomials.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_protocols.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_quat_interp.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_quat_visualization.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_smoothing.py +0 -0
- {interpolatepy-3.0.1 → interpolatepy-3.1.0}/tests/test_squad_c2.py +0 -0
|
@@ -28,15 +28,15 @@ concurrency:
|
|
|
28
28
|
jobs:
|
|
29
29
|
build:
|
|
30
30
|
runs-on: ubuntu-latest
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
steps:
|
|
33
33
|
- name: Checkout repository
|
|
34
|
-
uses: actions/checkout@
|
|
34
|
+
uses: actions/checkout@v6
|
|
35
35
|
with:
|
|
36
36
|
fetch-depth: 0
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
- name: Install uv
|
|
39
|
-
uses: astral-sh/setup-uv@
|
|
39
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
40
40
|
with:
|
|
41
41
|
enable-cache: true
|
|
42
42
|
cache-dependency-glob: uv.lock
|
|
@@ -46,14 +46,14 @@ jobs:
|
|
|
46
46
|
|
|
47
47
|
- name: Build documentation
|
|
48
48
|
run: uv run mkdocs build --clean --strict
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
- name: Setup Pages
|
|
51
51
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
52
|
-
uses: actions/configure-pages@
|
|
53
|
-
|
|
52
|
+
uses: actions/configure-pages@v6
|
|
53
|
+
|
|
54
54
|
- name: Upload artifact
|
|
55
55
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
56
|
-
uses: actions/upload-pages-artifact@
|
|
56
|
+
uses: actions/upload-pages-artifact@v5
|
|
57
57
|
with:
|
|
58
58
|
path: ./site
|
|
59
59
|
|
|
@@ -64,9 +64,9 @@ jobs:
|
|
|
64
64
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
65
65
|
runs-on: ubuntu-latest
|
|
66
66
|
needs: build
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
steps:
|
|
69
69
|
- name: Deploy to GitHub Pages
|
|
70
70
|
id: deployment
|
|
71
|
-
uses: actions/deploy-pages@
|
|
71
|
+
uses: actions/deploy-pages@v5
|
|
72
72
|
|
|
@@ -9,16 +9,16 @@ jobs:
|
|
|
9
9
|
pre-commit:
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
11
|
steps:
|
|
12
|
-
- uses: actions/checkout@
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
13
|
- name: Install uv
|
|
14
|
-
uses: astral-sh/setup-uv@
|
|
14
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
15
15
|
with:
|
|
16
16
|
enable-cache: true
|
|
17
17
|
cache-dependency-glob: uv.lock
|
|
18
18
|
- name: Sync dependencies
|
|
19
19
|
run: uv sync --locked --no-default-groups --group dev
|
|
20
20
|
- name: Cache pre-commit hook environments
|
|
21
|
-
uses: actions/cache@
|
|
21
|
+
uses: actions/cache@v5
|
|
22
22
|
with:
|
|
23
23
|
path: ~/.cache/pre-commit
|
|
24
24
|
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
|
@@ -12,11 +12,11 @@ jobs:
|
|
|
12
12
|
contents: read
|
|
13
13
|
environment: pypi
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
15
|
+
- uses: actions/checkout@v6
|
|
16
16
|
with:
|
|
17
17
|
fetch-depth: 0
|
|
18
18
|
- name: Install uv
|
|
19
|
-
uses: astral-sh/setup-uv@
|
|
19
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
20
20
|
with:
|
|
21
21
|
enable-cache: true
|
|
22
22
|
cache-dependency-glob: uv.lock
|
|
@@ -16,9 +16,9 @@ jobs:
|
|
|
16
16
|
env:
|
|
17
17
|
UV_PYTHON: ${{ matrix.python-version }}
|
|
18
18
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
20
|
- name: Install uv
|
|
21
|
-
uses: astral-sh/setup-uv@
|
|
21
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
22
22
|
with:
|
|
23
23
|
enable-cache: true
|
|
24
24
|
cache-dependency-glob: uv.lock
|
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
- name: Testing with coverage
|
|
28
28
|
run: uv run pytest tests --cov=interpolatepy --cov-report=xml --cov-report=term-missing
|
|
29
29
|
- name: Upload coverage to Codecov
|
|
30
|
-
uses: codecov/codecov-action@
|
|
30
|
+
uses: codecov/codecov-action@v6
|
|
31
31
|
with:
|
|
32
32
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
33
33
|
files: ./coverage.xml
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: InterpolatePy
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: A comprehensive Python library for generating smooth trajectories and curves with precise control over position, velocity, acceleration, and jerk profiles
|
|
5
5
|
Author-email: Giorgio Medico <giorgio.medico11@gmail.com>
|
|
6
6
|
Maintainer-email: Giorgio Medico <giorgio.medico11@gmail.com>
|
|
@@ -152,9 +152,10 @@ examples/double_s_ex.py
|
|
|
152
152
|
examples/frenet_frame_ex.py
|
|
153
153
|
examples/lin_poly_parabolic_ex.py
|
|
154
154
|
examples/linear_ex.py
|
|
155
|
-
examples/log_quat_ex.py
|
|
156
155
|
examples/log_quat_new_ex.py
|
|
156
|
+
examples/lqi_spiral_frenet_ex.py
|
|
157
157
|
examples/main.py
|
|
158
|
+
examples/mlqi_spiral_frenet_ex.py
|
|
158
159
|
examples/polynomials_ex.py
|
|
159
160
|
examples/protocols_ex.py
|
|
160
161
|
examples/quat_visualization_ex.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: InterpolatePy
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: A comprehensive Python library for generating smooth trajectories and curves with precise control over position, velocity, acceleration, and jerk profiles
|
|
5
5
|
Author-email: Giorgio Medico <giorgio.medico11@gmail.com>
|
|
6
6
|
Maintainer-email: Giorgio Medico <giorgio.medico11@gmail.com>
|
|
@@ -209,4 +209,4 @@ Interpolation near 180-degree rotations can be problematic because the interpola
|
|
|
209
209
|
|
|
210
210
|
- **[API Reference](../api-reference.md#quaternion)** for complete method documentation
|
|
211
211
|
- **[Algorithms Guide](../algorithms.md)** for mathematical foundations
|
|
212
|
-
- **Example scripts**: `examples/squad_c2_ex.py`, `examples/
|
|
212
|
+
- **Example scripts**: `examples/squad_c2_ex.py`, `examples/log_quat_new_ex.py`, `examples/quat_visualization_ex.py`
|
|
@@ -5,9 +5,6 @@ This example demonstrates the LogQuaternionInterpolation class (LQI method)
|
|
|
5
5
|
for smooth quaternion trajectory generation using logarithmic quaternion
|
|
6
6
|
representation with B-spline interpolation.
|
|
7
7
|
|
|
8
|
-
Note: This example has been updated to use LogQuaternionInterpolation instead
|
|
9
|
-
of the deprecated LogQuaternionBSpline class.
|
|
10
|
-
|
|
11
8
|
Key Features Demonstrated:
|
|
12
9
|
- Logarithmic quaternion space interpolation for smooth trajectories
|
|
13
10
|
- Algorithm 1 from Parker et al. (2023) for continuous axis-angle recovery
|
|
@@ -27,7 +24,6 @@ Mathematical Background:
|
|
|
27
24
|
import matplotlib.pyplot as plt
|
|
28
25
|
import numpy as np
|
|
29
26
|
|
|
30
|
-
# Using LogQuaternionInterpolation instead of deprecated LogQuaternionBSpline
|
|
31
27
|
from interpolatepy.log_quat import LogQuaternionInterpolation, ModifiedLogQuaternionInterpolation
|
|
32
28
|
from interpolatepy.quat_core import Quaternion
|
|
33
29
|
from interpolatepy.quat_spline import QuaternionSpline
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logarithmic Quaternion Interpolation (LQI) along a cylindrical helix oriented
|
|
3
|
+
with the Frenet frame.
|
|
4
|
+
|
|
5
|
+
Pipeline:
|
|
6
|
+
1. Define a cylindrical helix p(u) = (r*cos(u), r*sin(u), b*u) with analytical
|
|
7
|
+
derivatives so the Frenet frame can be evaluated in closed form. The radius
|
|
8
|
+
is constant, so curvature and torsion are constant along the curve.
|
|
9
|
+
2. Sample a sparse set of waypoints along the helix and compute the Frenet
|
|
10
|
+
frame [tangent, normal, binormal] at each waypoint.
|
|
11
|
+
3. Convert each Frenet rotation matrix to a unit quaternion.
|
|
12
|
+
4. Feed the (time, quaternion) waypoints to LogQuaternionInterpolation to
|
|
13
|
+
obtain a smooth C^2 orientation trajectory. LQI splines the rotation
|
|
14
|
+
vector r(u) = theta(u) * n_hat(u) as a single 3D quantity (in contrast
|
|
15
|
+
with mLQI, which splines theta and (X, Y, Z) on separate channels).
|
|
16
|
+
5. Evaluate the position helix and the interpolated orientation at a dense
|
|
17
|
+
set of times and visualize the resulting tool frames side by side with the
|
|
18
|
+
ground-truth Frenet frames.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import matplotlib.pyplot as plt
|
|
24
|
+
import numpy as np
|
|
25
|
+
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 (registers 3D projection)
|
|
26
|
+
|
|
27
|
+
from interpolatepy.frenet_frame import compute_trajectory_frames
|
|
28
|
+
from interpolatepy.frenet_frame import plot_frames
|
|
29
|
+
from interpolatepy.log_quat import LogQuaternionInterpolation
|
|
30
|
+
from interpolatepy.quat_core import Quaternion
|
|
31
|
+
from interpolatepy.quat_visualization import QuaternionTrajectoryVisualizer
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def cylindrical_helix_with_derivatives(
|
|
35
|
+
u: float, r: float = 1.0, b: float = 0.3
|
|
36
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
37
|
+
"""
|
|
38
|
+
Cylindrical helix (constant radius) position and first two derivatives.
|
|
39
|
+
|
|
40
|
+
p(u) = ( r*cos(u), r*sin(u), b*u )
|
|
41
|
+
dp/du = (-r*sin(u), r*cos(u), b )
|
|
42
|
+
d2p/du2 = (-r*cos(u), -r*sin(u), 0 )
|
|
43
|
+
"""
|
|
44
|
+
cos_u = np.cos(u)
|
|
45
|
+
sin_u = np.sin(u)
|
|
46
|
+
|
|
47
|
+
p = np.array([r * cos_u, r * sin_u, b * u])
|
|
48
|
+
dp_du = np.array([-r * sin_u, r * cos_u, b])
|
|
49
|
+
d2p_du2 = np.array([-r * cos_u, -r * sin_u, 0.0])
|
|
50
|
+
|
|
51
|
+
return p, dp_du, d2p_du2
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def frame_to_quaternion(frame: np.ndarray) -> Quaternion:
|
|
55
|
+
"""
|
|
56
|
+
Convert a 3x3 frame whose columns are [tangent, normal, binormal] into a
|
|
57
|
+
unit quaternion. The frame matrix is itself the rotation matrix from the
|
|
58
|
+
local Frenet basis to the world basis, so we can hand it directly to
|
|
59
|
+
Quaternion.from_rotation_matrix.
|
|
60
|
+
"""
|
|
61
|
+
return Quaternion.from_rotation_matrix(frame).unit()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def build_waypoints(
|
|
65
|
+
n_waypoints: int = 10,
|
|
66
|
+
u_min: float = 0.5,
|
|
67
|
+
u_max: float = 6.0 * np.pi,
|
|
68
|
+
r: float = 1.0,
|
|
69
|
+
b: float = 0.3,
|
|
70
|
+
) -> tuple[np.ndarray, list[Quaternion], np.ndarray, np.ndarray]:
|
|
71
|
+
"""
|
|
72
|
+
Sample the helix at n_waypoints values of u, compute Frenet frames there,
|
|
73
|
+
and return (times, quaternions, waypoint_positions, waypoint_frames).
|
|
74
|
+
|
|
75
|
+
Times are taken equal to the parameter u so the orientation evolution
|
|
76
|
+
matches the curve parameterization.
|
|
77
|
+
"""
|
|
78
|
+
u_waypoints = np.linspace(u_min, u_max, n_waypoints)
|
|
79
|
+
|
|
80
|
+
def helix_func(u: float) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
81
|
+
return cylindrical_helix_with_derivatives(u, r=r, b=b)
|
|
82
|
+
|
|
83
|
+
positions, frames = compute_trajectory_frames(helix_func, u_waypoints)
|
|
84
|
+
|
|
85
|
+
quaternions = [frame_to_quaternion(frames[i]) for i in range(len(u_waypoints))]
|
|
86
|
+
|
|
87
|
+
return u_waypoints, quaternions, positions, frames
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def evaluate_interpolated_trajectory(
|
|
91
|
+
lqi: LogQuaternionInterpolation,
|
|
92
|
+
u_dense: np.ndarray,
|
|
93
|
+
r: float,
|
|
94
|
+
b: float,
|
|
95
|
+
) -> tuple[np.ndarray, np.ndarray, list[Quaternion]]:
|
|
96
|
+
"""
|
|
97
|
+
Evaluate the helix positions, the LQI-interpolated orientation frames,
|
|
98
|
+
and the underlying interpolated quaternions at the dense parameter values.
|
|
99
|
+
"""
|
|
100
|
+
dense_positions = np.zeros((len(u_dense), 3))
|
|
101
|
+
dense_frames = np.zeros((len(u_dense), 3, 3))
|
|
102
|
+
dense_quaternions: list[Quaternion] = []
|
|
103
|
+
|
|
104
|
+
for i, u in enumerate(u_dense):
|
|
105
|
+
dense_positions[i], _, _ = cylindrical_helix_with_derivatives(u, r=r, b=b)
|
|
106
|
+
q = lqi.evaluate(u)
|
|
107
|
+
dense_frames[i] = q.to_rotation_matrix()
|
|
108
|
+
dense_quaternions.append(q)
|
|
109
|
+
|
|
110
|
+
return dense_positions, dense_frames, dense_quaternions
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def angular_error_deg(frame_truth: np.ndarray, frame_est: np.ndarray) -> float:
|
|
114
|
+
"""
|
|
115
|
+
Geodesic angle (in degrees) between two rotation matrices.
|
|
116
|
+
"""
|
|
117
|
+
r = frame_truth.T @ frame_est
|
|
118
|
+
cos_angle = np.clip(0.5 * (np.trace(r) - 1.0), -1.0, 1.0)
|
|
119
|
+
return np.degrees(np.arccos(cos_angle))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main() -> None:
|
|
123
|
+
print("LQI on a Cylindrical Helix with Frenet Frame Waypoints")
|
|
124
|
+
print("=" * 60)
|
|
125
|
+
|
|
126
|
+
# Helix parameters
|
|
127
|
+
r = 1.0 # constant radius
|
|
128
|
+
b = 0.3 # vertical rise per radian
|
|
129
|
+
u_min = 0.5
|
|
130
|
+
u_max = 6.0 * np.pi
|
|
131
|
+
n_waypoints = 1000
|
|
132
|
+
|
|
133
|
+
# Sample waypoints + their Frenet frames.
|
|
134
|
+
times, quaternions, wp_positions, _wp_frames = build_waypoints(
|
|
135
|
+
n_waypoints=n_waypoints,
|
|
136
|
+
u_min=u_min,
|
|
137
|
+
u_max=u_max,
|
|
138
|
+
r=r,
|
|
139
|
+
b=b,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
print(f"Sampled {n_waypoints} Frenet waypoints in u ∈ [{u_min:.2f}, {u_max:.2f}]")
|
|
143
|
+
print(f"Time/parameter range used for LQI: [{times[0]:.3f}, {times[-1]:.3f}]")
|
|
144
|
+
|
|
145
|
+
# Build the LQI interpolator over the waypoint orientations.
|
|
146
|
+
lqi = LogQuaternionInterpolation(
|
|
147
|
+
time_points=times,
|
|
148
|
+
quaternions=quaternions,
|
|
149
|
+
degree=3,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Dense evaluation of position + interpolated orientation.
|
|
153
|
+
n_dense = 240
|
|
154
|
+
u_dense = np.linspace(u_min, u_max, n_dense)
|
|
155
|
+
dense_positions, dense_frames_lqi, dense_quaternions_lqi = (
|
|
156
|
+
evaluate_interpolated_trajectory(lqi, u_dense, r=r, b=b)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Ground-truth Frenet frames on the same dense grid, for comparison.
|
|
160
|
+
def helix_func(u: float) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
161
|
+
return cylindrical_helix_with_derivatives(u, r=r, b=b)
|
|
162
|
+
|
|
163
|
+
_, dense_frames_truth = compute_trajectory_frames(helix_func, u_dense)
|
|
164
|
+
|
|
165
|
+
# Report worst-case angular deviation between interpolated orientation and
|
|
166
|
+
# the analytical Frenet frame.
|
|
167
|
+
errors = np.array([
|
|
168
|
+
angular_error_deg(dense_frames_truth[i], dense_frames_lqi[i])
|
|
169
|
+
for i in range(n_dense)
|
|
170
|
+
])
|
|
171
|
+
print(f"Mean angular error vs Frenet truth: {errors.mean():.3f} deg")
|
|
172
|
+
print(f"Max angular error vs Frenet truth: {errors.max():.3f} deg")
|
|
173
|
+
|
|
174
|
+
# ------------------------------------------------------------------
|
|
175
|
+
# Visualization
|
|
176
|
+
# ------------------------------------------------------------------
|
|
177
|
+
# Cap the number of waypoint markers actually drawn so the plots stay
|
|
178
|
+
# readable when n_waypoints is large (e.g. 1000). The interpolator still
|
|
179
|
+
# uses all of them — this only affects the visual overlay.
|
|
180
|
+
max_shown_waypoints = 30
|
|
181
|
+
if n_waypoints > max_shown_waypoints:
|
|
182
|
+
show_idx = np.linspace(0, n_waypoints - 1, max_shown_waypoints, dtype=int)
|
|
183
|
+
else:
|
|
184
|
+
show_idx = np.arange(n_waypoints)
|
|
185
|
+
wp_positions_shown = wp_positions[show_idx]
|
|
186
|
+
times_shown = times[show_idx]
|
|
187
|
+
quaternions_shown = [quaternions[i] for i in show_idx]
|
|
188
|
+
|
|
189
|
+
fig = plt.figure(figsize=(16, 7))
|
|
190
|
+
|
|
191
|
+
# Left panel: waypoints + interpolated LQI tool frames.
|
|
192
|
+
ax_left = fig.add_subplot(121, projection="3d")
|
|
193
|
+
plot_frames(ax_left, dense_positions, dense_frames_lqi, scale=0.6, skip=12)
|
|
194
|
+
ax_left.scatter(
|
|
195
|
+
wp_positions_shown[:, 0],
|
|
196
|
+
wp_positions_shown[:, 1],
|
|
197
|
+
wp_positions_shown[:, 2],
|
|
198
|
+
color="magenta",
|
|
199
|
+
s=40,
|
|
200
|
+
depthshade=False,
|
|
201
|
+
label=f"LQI waypoints ({len(show_idx)} of {n_waypoints} shown)",
|
|
202
|
+
)
|
|
203
|
+
ax_left.set_title("Helix + LQI-interpolated frames")
|
|
204
|
+
ax_left.set_xlabel("x")
|
|
205
|
+
ax_left.set_ylabel("y")
|
|
206
|
+
ax_left.set_zlabel("z")
|
|
207
|
+
ax_left.legend(loc="upper left")
|
|
208
|
+
|
|
209
|
+
# Right panel: ground-truth analytical Frenet frames on the dense grid.
|
|
210
|
+
ax_right = fig.add_subplot(122, projection="3d")
|
|
211
|
+
plot_frames(ax_right, dense_positions, dense_frames_truth, scale=0.6, skip=12)
|
|
212
|
+
ax_right.set_title("Analytical Frenet frames (reference)")
|
|
213
|
+
ax_right.set_xlabel("x")
|
|
214
|
+
ax_right.set_ylabel("y")
|
|
215
|
+
ax_right.set_zlabel("z")
|
|
216
|
+
|
|
217
|
+
for ax in (ax_left, ax_right):
|
|
218
|
+
ax.set_box_aspect([1, 1, 1])
|
|
219
|
+
|
|
220
|
+
plt.tight_layout()
|
|
221
|
+
|
|
222
|
+
# Quaternion trajectory in stereographic (MRP) projection space.
|
|
223
|
+
# This shows the orientation evolution in the *rotation* space rather than
|
|
224
|
+
# in the cartesian position space, with the input waypoints highlighted.
|
|
225
|
+
visualizer = QuaternionTrajectoryVisualizer()
|
|
226
|
+
visualizer.plot_3d_trajectory(
|
|
227
|
+
dense_quaternions_lqi,
|
|
228
|
+
waypoints=quaternions_shown,
|
|
229
|
+
waypoint_times=list(times_shown),
|
|
230
|
+
title="LQI quaternion trajectory (stereographic MRP projection)",
|
|
231
|
+
color="purple",
|
|
232
|
+
line_width=2.5,
|
|
233
|
+
point_size=15,
|
|
234
|
+
waypoint_color="magenta",
|
|
235
|
+
show_waypoint_labels=False,
|
|
236
|
+
figsize=(10, 8),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Bottom: angular error along the curve.
|
|
240
|
+
_fig2, ax_err = plt.subplots(figsize=(10, 3.5))
|
|
241
|
+
ax_err.plot(u_dense, errors, color="purple", linewidth=2.0)
|
|
242
|
+
ax_err.fill_between(u_dense, errors, alpha=0.25, color="purple")
|
|
243
|
+
ax_err.set_xlabel("Parameter u")
|
|
244
|
+
ax_err.set_ylabel("Angular error [deg]")
|
|
245
|
+
ax_err.set_title("Geodesic deviation: LQI orientation vs Frenet truth")
|
|
246
|
+
ax_err.grid(True, alpha=0.3)
|
|
247
|
+
plt.tight_layout()
|
|
248
|
+
|
|
249
|
+
# ------------------------------------------------------------------
|
|
250
|
+
# Rotation-vector decomposition produced internally by LQI.
|
|
251
|
+
# LQI splines a single 3D vector r(u) = theta(u) * n_hat(u). We plot
|
|
252
|
+
# both the raw components r_x, r_y, r_z and the (theta, n_hat)
|
|
253
|
+
# decomposition recovered from |r| and r/|r|.
|
|
254
|
+
# ------------------------------------------------------------------
|
|
255
|
+
r_dense = np.array([lqi.bspline_interpolator.evaluate(u) for u in u_dense])
|
|
256
|
+
r_wp = np.array([lqi.bspline_interpolator.evaluate(u) for u in times_shown])
|
|
257
|
+
|
|
258
|
+
theta_dense = np.linalg.norm(r_dense, axis=1)
|
|
259
|
+
theta_wp = np.linalg.norm(r_wp, axis=1)
|
|
260
|
+
|
|
261
|
+
# Unit axis from r / |r|, with a safe fallback near theta ~ 0.
|
|
262
|
+
eps_axis = 1e-12
|
|
263
|
+
safe_theta_dense = np.where(theta_dense > eps_axis, theta_dense, 1.0)
|
|
264
|
+
nhat_dense = r_dense / safe_theta_dense[:, None]
|
|
265
|
+
nhat_dense[theta_dense <= eps_axis] = np.array([1.0, 0.0, 0.0])
|
|
266
|
+
|
|
267
|
+
safe_theta_wp = np.where(theta_wp > eps_axis, theta_wp, 1.0)
|
|
268
|
+
nhat_wp = r_wp / safe_theta_wp[:, None]
|
|
269
|
+
nhat_wp[theta_wp <= eps_axis] = np.array([1.0, 0.0, 0.0])
|
|
270
|
+
|
|
271
|
+
_fig3, axes3 = plt.subplots(4, 1, figsize=(11, 9), sharex=True)
|
|
272
|
+
component_labels = [r"$\theta = \|r\|$ [rad]", r"$\hat n_x$", r"$\hat n_y$", r"$\hat n_z$"]
|
|
273
|
+
component_data = [theta_dense, nhat_dense[:, 0], nhat_dense[:, 1], nhat_dense[:, 2]]
|
|
274
|
+
waypoint_data = [theta_wp, nhat_wp[:, 0], nhat_wp[:, 1], nhat_wp[:, 2]]
|
|
275
|
+
component_colors = ["tab:orange", "tab:red", "tab:green", "tab:blue"]
|
|
276
|
+
|
|
277
|
+
for ax, label, curve, wp_vals, c in zip(
|
|
278
|
+
axes3, component_labels, component_data, waypoint_data, component_colors
|
|
279
|
+
):
|
|
280
|
+
ax.plot(u_dense, curve, color=c, linewidth=2.0, label="LQI")
|
|
281
|
+
ax.scatter(times_shown, wp_vals, color="magenta", s=25, zorder=5, label="waypoints")
|
|
282
|
+
ax.set_ylabel(label)
|
|
283
|
+
ax.grid(True, alpha=0.3)
|
|
284
|
+
ax.legend(loc="upper right", fontsize=8)
|
|
285
|
+
|
|
286
|
+
axes3[-1].set_xlabel("Parameter u")
|
|
287
|
+
axes3[0].set_title(
|
|
288
|
+
r"LQI internal state recovered from $r(u) = \theta(u)\,\hat n(u)$"
|
|
289
|
+
)
|
|
290
|
+
plt.tight_layout()
|
|
291
|
+
|
|
292
|
+
# Raw rotation-vector components r_x, r_y, r_z (what LQI actually splines).
|
|
293
|
+
_fig3b, axes3b = plt.subplots(3, 1, figsize=(11, 7), sharex=True)
|
|
294
|
+
raw_labels = [r"$r_x$", r"$r_y$", r"$r_z$"]
|
|
295
|
+
raw_colors = ["tab:red", "tab:green", "tab:blue"]
|
|
296
|
+
for ax, label, k, c in zip(axes3b, raw_labels, range(3), raw_colors):
|
|
297
|
+
ax.plot(u_dense, r_dense[:, k], color=c, linewidth=2.0, label="LQI")
|
|
298
|
+
ax.scatter(times_shown, r_wp[:, k], color="magenta", s=25, zorder=5, label="waypoints")
|
|
299
|
+
ax.set_ylabel(label)
|
|
300
|
+
ax.grid(True, alpha=0.3)
|
|
301
|
+
ax.legend(loc="upper right", fontsize=8)
|
|
302
|
+
axes3b[-1].set_xlabel("Parameter u")
|
|
303
|
+
axes3b[0].set_title(r"LQI spline state: rotation-vector components $r(u)$")
|
|
304
|
+
plt.tight_layout()
|
|
305
|
+
|
|
306
|
+
# ------------------------------------------------------------------
|
|
307
|
+
# Physical angular velocity (omega) and acceleration (alpha) in 3D.
|
|
308
|
+
# ------------------------------------------------------------------
|
|
309
|
+
omega = np.zeros((n_dense, 3))
|
|
310
|
+
alpha = np.zeros((n_dense, 3))
|
|
311
|
+
for i, u in enumerate(u_dense):
|
|
312
|
+
omega[i], alpha[i] = lqi.get_physical_kinematics(u)
|
|
313
|
+
|
|
314
|
+
omega_norm = np.linalg.norm(omega, axis=1)
|
|
315
|
+
alpha_norm = np.linalg.norm(alpha, axis=1)
|
|
316
|
+
|
|
317
|
+
_fig4, (ax_w, ax_a) = plt.subplots(2, 1, figsize=(11, 7), sharex=True)
|
|
318
|
+
|
|
319
|
+
for k, axis_name, c in zip(range(3), ("x", "y", "z"), ("tab:red", "tab:green", "tab:blue")):
|
|
320
|
+
ax_w.plot(u_dense, omega[:, k], color=c, linewidth=1.8, label=rf"$\omega_{axis_name}$")
|
|
321
|
+
ax_a.plot(u_dense, alpha[:, k], color=c, linewidth=1.8, label=rf"$\alpha_{axis_name}$")
|
|
322
|
+
|
|
323
|
+
ax_w.plot(u_dense, omega_norm, color="black", linewidth=1.2, linestyle="--", label=r"$\|\omega\|$")
|
|
324
|
+
ax_a.plot(u_dense, alpha_norm, color="black", linewidth=1.2, linestyle="--", label=r"$\|\alpha\|$")
|
|
325
|
+
|
|
326
|
+
ax_w.set_ylabel(r"Angular velocity $\omega$ [rad/u]")
|
|
327
|
+
ax_w.set_title("Physical angular velocity and acceleration from LQI")
|
|
328
|
+
ax_w.legend(loc="upper right", ncol=4, fontsize=9)
|
|
329
|
+
ax_w.grid(True, alpha=0.3)
|
|
330
|
+
|
|
331
|
+
ax_a.set_ylabel(r"Angular acceleration $\alpha$ [rad/u$^2$]")
|
|
332
|
+
ax_a.set_xlabel("Parameter u")
|
|
333
|
+
ax_a.legend(loc="upper right", ncol=4, fontsize=9)
|
|
334
|
+
ax_a.grid(True, alpha=0.3)
|
|
335
|
+
|
|
336
|
+
plt.tight_layout()
|
|
337
|
+
|
|
338
|
+
plt.show()
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if __name__ == "__main__":
|
|
342
|
+
main()
|