openscvx 0.5.3.dev42__tar.gz → 0.5.3.dev43__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.
- {openscvx-0.5.3.dev42/openscvx.egg-info → openscvx-0.5.3.dev43}/PKG-INFO +1 -1
- openscvx-0.5.3.dev43/examples/drone/boresight_trace_mpcc.py +584 -0
- openscvx-0.5.3.dev43/examples/drone/logo_utils/circle.svg +3 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/svg_path_utils.py +66 -1
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/_version.py +3 -3
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/animated.py +4 -1
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/SOURCES.txt +2 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/scm_file_list.json +2 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/scm_version.json +2 -2
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/_extra.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/docs.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/lint.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/release.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-cvxpygen.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-examples.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-lie.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-mjx.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests-qpax.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.github/workflows/tests.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.gitignore +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/.gitmodules +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/CITATION.cff +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/CONTRIBUTING.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/LICENSE +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/README.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/batching_jit_grad.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/custom_algorithms_autotuners.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/08_mpcc.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/UsersGuide/09_mjx_dynamics.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/favicon.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/logo.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-client/index.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-recordings/drone_racing.viser +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/assets/viser-recordings/franka_fr3v2_pick_place.viser +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/changelog.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/citation.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/examples.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/index.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/installation.md +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/llms.txt +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/docs/versions.json +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/_viser_embed_export.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/brachistochrone_batched.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/flappy_bird.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/hypersensitive.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/impulsive.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/stl_integer_variable.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/abstract/stl_or.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/aircraft/dynamic_soaring.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/aircraft/supersonic_time_to_climb.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_camera.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_render.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/_sensor_view.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/logo.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/animations/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/3_dof_arm.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm_collision.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/7_dof_arm_vp.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_pick_place.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_viewplanning.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/arm/franka_fr3v2_viewplanning_nodal.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_obstacle_conditional.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_obstacle_stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_stl_or.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/car/dubins_car_waypoint_stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/double_integrator/_plotting.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/double_integrator/moving_safe_zones.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/2d_obstacle_avoidance_batched_ic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/cinema_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/drone_racing_batched_gates.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/openscvx_logo_single.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/logo_utils/quadrotor_mesh.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/multi_agent_circle_swap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/obstacle_avoidance_vmap_2d.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/drone/openscvx_logo.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_pick_place.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_viewplanning.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/panda_frax_waypoint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/ur5e_assets/ur5e.urdf +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/frax/ur5e_pen_trace.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/double_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/skydio_x2_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_3d_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_game.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mjx/triple_cartpole_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/double_integrator_discrete.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/dubins_car_circle_analytical.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/dubins_car_circle_discrete.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/mpc/realtime_double_integrator_drone_racing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/plotting.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/plotting_viser.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/3DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/6DoF_pdg_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/3DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/6DoF_pdg_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/dubins_car_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/6DoF_pdg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/6DoF_pdg_batched_ic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/rocket/ascent_launch_vehicle.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/halo_orbit.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/hohmann_transfer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/let_transfer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/examples/spacecraft/relative_loitering.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/figures/video_preview.png +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-dropin.css +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-hero.css +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/assets/stylesheets/home-viser.css +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/home.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/main.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-diagram.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-dropin-banner.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-hero.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-pipeline.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/partials/home-viser-strip.html +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/material/overrides/sitemap.xml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/mkdocs.yml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/__main__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/acceptance_ratio.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/adaptive_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/augmented_lagrangian.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/constant_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/autotuner/ramp_proximal_weight.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/history.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/hyperparams.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/loop.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/penalty.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/iteration.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/scvx/penalized_trust_region.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/state.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/algorithms/weights.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/config.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/discretize_linearize.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/linearize_discretize.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/linearize_discretize_sparse.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/bcoo_helpers.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/discretization/sparse_utils/sparse_jacobian.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/byof.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/lowering.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/expert/validation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/init/inverse_kinematics.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/_utils.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/frax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/menagerie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrations/mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/diffrax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/loader.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/cvxpy_variables.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/problem.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/lowered/unified.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/plotting.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/publication.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/coordinates.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/orbits.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/problem.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/post_processing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/propagation/propagation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/base.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/cones.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/cvxpy_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/solvers/qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/affine.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/augmentation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/builder.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/canonicalize.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/control.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/parameter.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/stljax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lower.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/stljax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/_lowerer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/_registry.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/arithmetic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/control.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/expr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/formulation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/lie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/state.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/lowerers/latex/vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/stljax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/preprocessing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/problem.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/symbolic/unified.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/cache.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/caching.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/printing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx/utils/utils.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/pyproject.toml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/mkdocs_copy_viser_client_hook.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/scripts/mkdocs_seo_hook.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/setup.cfg +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/_iteration_helpers.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_defaults_agreement.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_hyper_overrides.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/autotuner/test_update_weights_jit.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_algorithm_state_pytree.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_iteration_fn_jit.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_iteration_fn_parity.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/algorithms/test_make_solve_loop.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/conftest.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_impulsive.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_multishot_propagation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_cvxpy_export_errors.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_batched_export_roundtrip.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_bare_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_jit_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_vmap_brachistochrone.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/e2e/test_solve_jax_vmap_converged_no_drift.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/hohmann_analytical.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/test_mjx.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/integrations/test_mjx_dynamics.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/_iteration_callback_helpers.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_cvxpy_callback_jit_spike.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_cvxpy.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_moreau.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_qpax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_iteration_callback_vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_moreau_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_qpax_ptr_solver.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/solvers/test_subproblem_pytree.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_variable.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_augmentation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_gmsr.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_latex_formulation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_lower_latex.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_preprocessing.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/symbolic/test_unified.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_autotuning.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_discretization.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_examples.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_expert.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_init.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_initialize_syncs_guesses.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_integrators.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_loader.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_optimization_results.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_plotting.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_post_process_batched_tau_clamp.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_propagation.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_results_parameters.py +0 -0
- {openscvx-0.5.3.dev42 → openscvx-0.5.3.dev43}/tests/test_solve_batched_inference.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscvx
|
|
3
|
-
Version: 0.5.3.
|
|
3
|
+
Version: 0.5.3.dev43
|
|
4
4
|
Summary: A general Python-based successive convexification implementation which uses a JAX backend.
|
|
5
5
|
Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
|
|
6
6
|
License: Apache Software License
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
"""MPCC SVG tracing: a quadrotor draws an SVG path with its camera boresight.
|
|
2
|
+
|
|
3
|
+
This example unifies two ideas from the examples suite:
|
|
4
|
+
|
|
5
|
+
- ``drone/openscvx_logo.py`` — a 6-DOF quadrotor whose boresight traces the
|
|
6
|
+
OpenSCvx logo, with the target point parametrized by *time*
|
|
7
|
+
- ``mpc/double_integrator_drone_racing.py`` — model-predictive contouring
|
|
8
|
+
control (MPCC, [Romero et al. 2022](https://arxiv.org/abs/2108.13205)),
|
|
9
|
+
where progress along a reference path is a state the optimizer advances
|
|
10
|
+
|
|
11
|
+
Parametrizing the logo target by time couples the node count to the path
|
|
12
|
+
complexity (the one-shot version needs 500 nodes). Here the target is
|
|
13
|
+
parametrized by *arc length* instead: a cubic spline (``ox.Cinterp``) maps a
|
|
14
|
+
progress state to a point on the SVG path, and a progress-rate control
|
|
15
|
+
advances it as fast as drawing accuracy allows while a short receding horizon
|
|
16
|
+
retraces the path chunk by chunk. The "pen" is the point where the camera
|
|
17
|
+
boresight pierces the drawing plane, and the contouring cost — the pen's
|
|
18
|
+
squared distance to the path point at the current progress — is fully
|
|
19
|
+
symbolic, spline lookup included: no bring-your-own-function dynamics. Any
|
|
20
|
+
single-path SVG works unchanged (the OpenSCvx wordmark by default):
|
|
21
|
+
|
|
22
|
+
python boresight_trace_mpcc.py [path/to/drawing.svg]
|
|
23
|
+
|
|
24
|
+
Requires:
|
|
25
|
+
pip install svgpathtools
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
|
|
33
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
34
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
35
|
+
sys.path.append(grandparent_dir)
|
|
36
|
+
|
|
37
|
+
import openscvx as ox
|
|
38
|
+
from examples.drone.logo_utils.svg_path_utils import extract_svg_arc_length_path
|
|
39
|
+
from openscvx import Problem
|
|
40
|
+
|
|
41
|
+
###############################################################################
|
|
42
|
+
# Reference path: SVG -> arc-length-parametrized curve in a vertical plane
|
|
43
|
+
###############################################################################
|
|
44
|
+
|
|
45
|
+
_DEFAULT_SVG = os.path.join(current_dir, "logo_utils", "openscvx_logo_single.svg")
|
|
46
|
+
svg_file = sys.argv[1] if len(sys.argv) > 1 else _DEFAULT_SVG
|
|
47
|
+
|
|
48
|
+
path_width = 10.0 # Larger dimension of the drawing [m]
|
|
49
|
+
path_center = np.array([1.0, 0.0, 5.0]) # Center of the drawing plane
|
|
50
|
+
hover_pos = np.array([-5.0, 0.0, 5.0]) # Drone hovers here, looking +x at the plane
|
|
51
|
+
|
|
52
|
+
# Path 0 is the drawing in both bundled SVGs (later paths are background rects)
|
|
53
|
+
s_path, planar_points = extract_svg_arc_length_path(svg_file, path_indices=[0], width=path_width)
|
|
54
|
+
total_arc_length = s_path[-1]
|
|
55
|
+
|
|
56
|
+
# Lift the planar curve into the world: the drawing spans the y-z plane at
|
|
57
|
+
# x = path_center[0], with the SVG's y-axis pointing up (world z).
|
|
58
|
+
path_points = path_center + np.column_stack(
|
|
59
|
+
[np.zeros(len(s_path)), planar_points[:, 0], planar_points[:, 1]]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Spline tables extend the reference past the true end by more than one horizon
|
|
63
|
+
# of pen travel, holding it parked at the final path point. The progress reward
|
|
64
|
+
# keeps pulling the progress state forward — without the extension it saturates
|
|
65
|
+
# as the horizon runs out of table and the endgame dissolves into a hovering
|
|
66
|
+
# scribble — but the reference *point* stays put, so the pen decelerates into
|
|
67
|
+
# the endpoint and settles there instead of chasing a fictitious continuation
|
|
68
|
+
# off the wordmark. The loop stops as the pen crosses the true finish, so the
|
|
69
|
+
# parked tail collapses onto the endpoint rather than trailing ink past it.
|
|
70
|
+
pad_length = 10.0 # [m]
|
|
71
|
+
_ds = s_path[1] - s_path[0]
|
|
72
|
+
_s_pad = np.arange(1, int(pad_length / _ds)) * _ds
|
|
73
|
+
s_ref = np.concatenate([s_path, total_arc_length + _s_pad])
|
|
74
|
+
ref_points = np.concatenate([path_points, np.tile(path_points[-1], (len(_s_pad), 1))])
|
|
75
|
+
padded_arc_length = s_ref[-1]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def ref_point(s):
|
|
79
|
+
"""Numeric reference lookup used for guesses and analysis (matches Cinterp data)."""
|
|
80
|
+
return np.stack([np.interp(s, s_ref, ref_points[:, i]) for i in range(3)], axis=-1)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
boresight_body = np.array([1.0, 0.0, 0.0]) # Boresight is the body x-axis
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def look_at(from_pos, target):
|
|
87
|
+
"""Quaternion [w, x, y, z] rotating the body boresight onto ``target - from_pos``."""
|
|
88
|
+
a = target - from_pos
|
|
89
|
+
a = a / np.linalg.norm(a)
|
|
90
|
+
q = np.concatenate([[1.0 + boresight_body @ a], np.cross(boresight_body, a)])
|
|
91
|
+
return q / np.linalg.norm(q)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
###############################################################################
|
|
95
|
+
# MPCC problem: 6-DOF quadrotor + progress along the path
|
|
96
|
+
###############################################################################
|
|
97
|
+
|
|
98
|
+
n_mpc = 13 # Horizon nodes
|
|
99
|
+
horizon_duration = 2.4 # Horizon length [s]
|
|
100
|
+
dt_mpc = horizon_duration / (n_mpc - 1) # Executed step [s]
|
|
101
|
+
|
|
102
|
+
Q_TRACE = 4e1 # Contouring error weight (squared trace-vs-path distance)
|
|
103
|
+
Q_PROGRESS = 1e-1 # Progress reward
|
|
104
|
+
Q_SMOOTH = 2e-1 # Acceleration (thrust tilt) penalty: calms the airframe
|
|
105
|
+
|
|
106
|
+
trace_rate_max = 1.5 # Max speed of the traced point along the path [m/s]
|
|
107
|
+
trace_rate_guess = 1.0
|
|
108
|
+
|
|
109
|
+
m = 1.0 # Mass of the drone
|
|
110
|
+
g_const = -9.81
|
|
111
|
+
J_b = np.array([0.01, 0.01, 0.02]) # Body inertia of a 1 kg racing quad [kg m^2]
|
|
112
|
+
|
|
113
|
+
# --- States ---
|
|
114
|
+
# The x band holds a healthy standoff from the canvas: too close and the
|
|
115
|
+
# pointing geometry demands wild attitude swings, too far and every milliradian
|
|
116
|
+
# of attitude wobble smears centimeters of ink. The y-z room generously covers
|
|
117
|
+
# the drawing so the drone can track the pen's height and never gets cornered.
|
|
118
|
+
position = ox.State("position", shape=(3,))
|
|
119
|
+
position.max = np.array([-4.0, 7.0, 11.0])
|
|
120
|
+
position.min = np.array([-7.5, -7.0, 0.5])
|
|
121
|
+
position.initial = hover_pos
|
|
122
|
+
position.final = [ox.Free(0.0), ox.Free(0.0), ox.Free(0.0)]
|
|
123
|
+
|
|
124
|
+
velocity = ox.State("velocity", shape=(3,))
|
|
125
|
+
velocity.max = np.array([10.0, 10.0, 10.0])
|
|
126
|
+
velocity.min = np.array([-10.0, -10.0, -10.0])
|
|
127
|
+
velocity.initial = np.zeros(3)
|
|
128
|
+
velocity.final = [ox.Free(0.0), ox.Free(0.0), ox.Free(0.0)]
|
|
129
|
+
|
|
130
|
+
attitude = ox.State("attitude", shape=(4,)) # Quaternion [w, x, y, z]
|
|
131
|
+
attitude.max = np.ones(4)
|
|
132
|
+
attitude.min = -np.ones(4)
|
|
133
|
+
attitude.initial = look_at(hover_pos, ref_point(0.0))
|
|
134
|
+
attitude.final = [ox.Free(0.0)] * 4
|
|
135
|
+
|
|
136
|
+
angular_velocity = ox.State("angular_velocity", shape=(3,))
|
|
137
|
+
angular_velocity.max = np.array([6.0, 6.0, 6.0])
|
|
138
|
+
angular_velocity.min = np.array([-6.0, -6.0, -6.0])
|
|
139
|
+
angular_velocity.initial = np.zeros(3)
|
|
140
|
+
angular_velocity.final = [ox.Free(0.0)] * 3
|
|
141
|
+
|
|
142
|
+
progress = ox.State("progress", shape=(1,)) # Arc length along the SVG path
|
|
143
|
+
progress.min = np.array([0.0])
|
|
144
|
+
progress.max = np.array([padded_arc_length])
|
|
145
|
+
progress.initial = np.array([0.0])
|
|
146
|
+
progress.final = [ox.Maximize(0.0)]
|
|
147
|
+
|
|
148
|
+
trace_error = ox.State("trace_error", shape=(1,)) # Integrated squared trace error
|
|
149
|
+
trace_error.min = np.array([0.0])
|
|
150
|
+
trace_error.max = np.array([2.0])
|
|
151
|
+
trace_error.initial = np.array([0.0])
|
|
152
|
+
trace_error.final = [ox.Minimize(0.0)]
|
|
153
|
+
|
|
154
|
+
smoothness = ox.State("smoothness", shape=(1,)) # Integrated squared acceleration
|
|
155
|
+
smoothness.min = np.array([0.0])
|
|
156
|
+
smoothness.max = np.array([100.0])
|
|
157
|
+
smoothness.initial = np.array([0.0])
|
|
158
|
+
smoothness.final = [ox.Minimize(0.0)]
|
|
159
|
+
|
|
160
|
+
# --- Controls ---
|
|
161
|
+
thrust_force = ox.Control("thrust_force", shape=(3,)) # Body-frame thrust (z only)
|
|
162
|
+
thrust_force.max = np.array([0.0, 0.0, 4.179446268 * 9.81])
|
|
163
|
+
thrust_force.min = np.array([0.0, 0.0, 0.0])
|
|
164
|
+
|
|
165
|
+
torque = ox.Control("torque", shape=(3,))
|
|
166
|
+
torque.max = np.array([1.0, 1.0, 0.3])
|
|
167
|
+
torque.min = np.array([-1.0, -1.0, -0.3])
|
|
168
|
+
|
|
169
|
+
trace_rate = ox.Control("trace_rate", shape=(1,)) # d(progress)/dt
|
|
170
|
+
trace_rate.min = np.array([0.0]) # The pen only moves forward
|
|
171
|
+
trace_rate.max = np.array([trace_rate_max])
|
|
172
|
+
|
|
173
|
+
# --- Contouring error (fully symbolic via spline lookup of the path) ---
|
|
174
|
+
# The "pen" is where the boresight ray pierces the drawing plane; the error is
|
|
175
|
+
# its distance to the path point at the current progress. Penalizing the pen
|
|
176
|
+
# (rather than a pointing angle) makes the optimizer orchestrate position and
|
|
177
|
+
# attitude together — a pitched quadrotor necessarily accelerates sideways, so
|
|
178
|
+
# drawing accurately while hovering is a genuinely coupled 6-DOF problem.
|
|
179
|
+
p_ref = ox.Concat(
|
|
180
|
+
ox.Cinterp(progress[0], s_ref, ref_points[:, 0]),
|
|
181
|
+
ox.Cinterp(progress[0], s_ref, ref_points[:, 1]),
|
|
182
|
+
ox.Cinterp(progress[0], s_ref, ref_points[:, 2]),
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Guarded denominators: SCP iterates can wander through states the constraints
|
|
186
|
+
# only push back on softly (q near 0, boresight grazing the plane), and the
|
|
187
|
+
# linearization must stay finite there. Both guards are inactive at any
|
|
188
|
+
# solution the constraints accept.
|
|
189
|
+
attitude_normalized = attitude / ox.Sqrt(ox.Sum(attitude * attitude) + 1e-8)
|
|
190
|
+
boresight = ox.spatial.QDCM(attitude_normalized) @ ox.Constant(boresight_body)
|
|
191
|
+
|
|
192
|
+
pen = position + boresight * ((path_center[0] - position[0]) / ox.Max(boresight[0], 0.1))
|
|
193
|
+
pen_error = pen - p_ref
|
|
194
|
+
trace_cost = ox.Sum(pen_error * pen_error)
|
|
195
|
+
|
|
196
|
+
# --- Dynamics ---
|
|
197
|
+
# The smoothness integrand prices acceleration (tilt-induced lurching that
|
|
198
|
+
# drags the pen off the path) and velocity (damping that keeps the
|
|
199
|
+
# receding-horizon loop from building oscillations step over step). Velocity is
|
|
200
|
+
# weighted 3x: a becalmed airframe holds the pen on the line far more than it
|
|
201
|
+
# slows the pen's pace, so the heavier damping cuts trace error without
|
|
202
|
+
# stretching the flight. Together with the progress reward this sets the cruise
|
|
203
|
+
# pace of the pen.
|
|
204
|
+
J_b_diag = ox.linalg.Diag(J_b)
|
|
205
|
+
acceleration = (1.0 / m) * ox.spatial.QDCM(attitude_normalized) @ thrust_force + np.array(
|
|
206
|
+
[0.0, 0.0, g_const]
|
|
207
|
+
)
|
|
208
|
+
dynamics = {
|
|
209
|
+
"position": velocity,
|
|
210
|
+
"velocity": acceleration,
|
|
211
|
+
"attitude": 0.5 * ox.spatial.SSMP(angular_velocity) @ attitude_normalized,
|
|
212
|
+
"angular_velocity": ox.linalg.Diag(1.0 / J_b)
|
|
213
|
+
@ (torque - ox.spatial.SSM(angular_velocity) @ J_b_diag @ angular_velocity),
|
|
214
|
+
"progress": trace_rate,
|
|
215
|
+
"trace_error": trace_cost,
|
|
216
|
+
"smoothness": ox.Sum(acceleration * acceleration) + 3.0 * ox.Sum(velocity * velocity),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# --- Constraints ---
|
|
220
|
+
states = [position, velocity, attitude, angular_velocity, progress, trace_error, smoothness]
|
|
221
|
+
controls = [thrust_force, torque, trace_rate]
|
|
222
|
+
|
|
223
|
+
constraints = []
|
|
224
|
+
for state in [position, velocity, angular_velocity]:
|
|
225
|
+
constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
|
|
226
|
+
|
|
227
|
+
# Keep the camera facing the canvas (also keeps the pen intersection well-posed)
|
|
228
|
+
constraints.append(ox.ctcs(0.2 <= boresight[0]))
|
|
229
|
+
|
|
230
|
+
# --- Time: fixed horizon with a pinned first step (the executed segment) ---
|
|
231
|
+
# Dilation bounds keep the node spacing near-uniform: freely stretched
|
|
232
|
+
# segments integrate saturated dynamics over long intervals and blow up
|
|
233
|
+
t = ox.Time(
|
|
234
|
+
initial=0.0,
|
|
235
|
+
final=horizon_duration,
|
|
236
|
+
min=0.0,
|
|
237
|
+
max=horizon_duration,
|
|
238
|
+
time_dilation_min=0.5 * horizon_duration,
|
|
239
|
+
time_dilation_max=2.0 * horizon_duration,
|
|
240
|
+
)
|
|
241
|
+
constraints.append((t == dt_mpc).convex().at(1))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
###############################################################################
|
|
245
|
+
# Initial guess and warm-starting
|
|
246
|
+
###############################################################################
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def set_initial_guess():
|
|
250
|
+
"""Hover at the start location, boresight swept along the first path chunk."""
|
|
251
|
+
arc_guess = np.linspace(0.0, trace_rate_guess * horizon_duration, n_mpc)
|
|
252
|
+
position.guess = np.tile(hover_pos, (n_mpc, 1))
|
|
253
|
+
velocity.guess = np.zeros((n_mpc, 3))
|
|
254
|
+
attitude.guess = np.array([look_at(hover_pos, ref_point(s)) for s in arc_guess])
|
|
255
|
+
attitude.initial = attitude.guess[0]
|
|
256
|
+
angular_velocity.guess = np.zeros((n_mpc, 3))
|
|
257
|
+
progress.guess = arc_guess.reshape(-1, 1)
|
|
258
|
+
trace_error.guess = np.zeros((n_mpc, 1))
|
|
259
|
+
smoothness.guess = np.zeros((n_mpc, 1))
|
|
260
|
+
|
|
261
|
+
thrust_force.guess = np.tile([0.0, 0.0, -m * g_const], (n_mpc, 1))
|
|
262
|
+
torque.guess = np.zeros((n_mpc, 3))
|
|
263
|
+
trace_rate.guess = np.full((n_mpc, 1), trace_rate_guess)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def update_initial_conditions(nodes: dict):
|
|
267
|
+
"""Set initial conditions from node 1 of the previous solution (simulate one step).
|
|
268
|
+
|
|
269
|
+
Solutions can sit exactly on a state bound, so clip away the solver's
|
|
270
|
+
float roundoff before handing the values back as fixed initial conditions.
|
|
271
|
+
"""
|
|
272
|
+
for state in [position, velocity, attitude, angular_velocity, progress]:
|
|
273
|
+
state.initial = np.clip(nodes[state.name][1], state.min, state.max)
|
|
274
|
+
# Cost integrators restart from zero each horizon
|
|
275
|
+
trace_error.initial = np.array([0.0])
|
|
276
|
+
smoothness.initial = np.array([0.0])
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def shift_guess(nodes: dict):
|
|
280
|
+
"""Shift the previous solution by one node; extend the tip with a hover prior.
|
|
281
|
+
|
|
282
|
+
The freshly revealed tip node points its attitude along the path at the
|
|
283
|
+
extended progress but is otherwise seeded as a calm hover — zero velocity,
|
|
284
|
+
hover thrust, zero torque, position held from the last node. Repeating the
|
|
285
|
+
last node's momentum instead extrapolates any drift into the new node,
|
|
286
|
+
which the receding-horizon loop can amplify step over step; a hover prior
|
|
287
|
+
lets the optimizer discover the motion the path actually needs.
|
|
288
|
+
"""
|
|
289
|
+
ext_progress = min(
|
|
290
|
+
nodes["progress"][-1, 0] + nodes["trace_rate"][-1, 0] * dt_mpc, padded_arc_length
|
|
291
|
+
)
|
|
292
|
+
ext_attitude = look_at(nodes["position"][-1], ref_point(ext_progress))
|
|
293
|
+
|
|
294
|
+
position.guess = np.vstack([nodes["position"][1:], nodes["position"][-1:]])
|
|
295
|
+
velocity.guess = np.vstack([nodes["velocity"][1:], np.zeros((1, 3))])
|
|
296
|
+
attitude.guess = np.vstack([nodes["attitude"][1:], [ext_attitude]])
|
|
297
|
+
angular_velocity.guess = np.vstack([nodes["angular_velocity"][1:], np.zeros((1, 3))])
|
|
298
|
+
progress.guess = np.vstack([nodes["progress"][1:], [[ext_progress]]])
|
|
299
|
+
|
|
300
|
+
for integrator, key in [(trace_error, "trace_error"), (smoothness, "smoothness")]:
|
|
301
|
+
offset = nodes[key][1]
|
|
302
|
+
integrator.guess = np.maximum(
|
|
303
|
+
np.vstack([nodes[key][1:] - offset, nodes[key][-1:] - offset]), 0.0
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
thrust_force.guess = np.vstack([nodes["thrust_force"][1:], [[0.0, 0.0, -m * g_const]]])
|
|
307
|
+
torque.guess = np.vstack([nodes["torque"][1:], np.zeros((1, 3))])
|
|
308
|
+
trace_rate.guess = np.vstack([nodes["trace_rate"][1:], nodes["trace_rate"][-1:]])
|
|
309
|
+
|
|
310
|
+
# Time: shift and renormalize so the horizon starts at t = 0
|
|
311
|
+
ext_time = nodes["time"][-1, 0] + nodes["_time_dilation"][-1, 0] * (1.0 / (n_mpc - 1))
|
|
312
|
+
shifted_time = np.vstack([nodes["time"][1:], [[ext_time]]])
|
|
313
|
+
t.guess = shifted_time - shifted_time[0]
|
|
314
|
+
t._time_dilation_control.guess = np.vstack(
|
|
315
|
+
[nodes["_time_dilation"][1:], nodes["_time_dilation"][-1:]]
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
set_initial_guess()
|
|
320
|
+
|
|
321
|
+
problem = Problem(
|
|
322
|
+
dynamics=dynamics,
|
|
323
|
+
states=states,
|
|
324
|
+
controls=controls,
|
|
325
|
+
time=t,
|
|
326
|
+
constraints=constraints,
|
|
327
|
+
N=n_mpc,
|
|
328
|
+
algorithm={
|
|
329
|
+
"autotuner": ox.ConstantProximalWeight(),
|
|
330
|
+
"lam_cost": {"trace_error": Q_TRACE, "progress": Q_PROGRESS, "smoothness": Q_SMOOTH},
|
|
331
|
+
"lam_prox": 3e1, # Strong anchoring: weak prox never converges here
|
|
332
|
+
"lam_vc": 1e4, # Keep dynamics honest: far above the largest cost weight
|
|
333
|
+
"k_max": 30, # Warm-started horizons converge in a handful of iterations
|
|
334
|
+
},
|
|
335
|
+
float_dtype="float64",
|
|
336
|
+
# Integrator tolerance must stay well below the O(1e-4) trace-error
|
|
337
|
+
# integrand or it silently swallows the cost signal; 1e-6 is the fastest
|
|
338
|
+
# setting that preserves tracking accuracy
|
|
339
|
+
discretizer=ox.DiscretizeLinearizeVectorize(diffrax_kwargs={"atol": 1e-6, "rtol": 1e-6}),
|
|
340
|
+
# Relaxed subproblem tolerances: corner subproblems otherwise grind toward
|
|
341
|
+
# QOCO's defaults and dominate the worst-case step time
|
|
342
|
+
solver={"solver_args": {"abstol": 3e-5, "reltol": 3e-7, "enforce_dpp": True}},
|
|
343
|
+
)
|
|
344
|
+
problem.settings.dev.printing = False
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
###############################################################################
|
|
348
|
+
# Analysis: where does the boresight actually draw on the plane?
|
|
349
|
+
###############################################################################
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def qdcm(q):
|
|
353
|
+
"""Quaternion [w, x, y, z] to DCM (numeric twin of ox.spatial.QDCM)."""
|
|
354
|
+
w, x, y, z = q / np.linalg.norm(q)
|
|
355
|
+
return np.array(
|
|
356
|
+
[
|
|
357
|
+
[1 - 2 * (y**2 + z**2), 2 * (x * y - z * w), 2 * (x * z + y * w)],
|
|
358
|
+
[2 * (x * y + z * w), 1 - 2 * (x**2 + z**2), 2 * (y * z - x * w)],
|
|
359
|
+
[2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x**2 + y**2)],
|
|
360
|
+
]
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def boresight_trace(positions, attitudes):
|
|
365
|
+
"""Intersect the boresight ray with the drawing plane x = path_center[0]."""
|
|
366
|
+
points = []
|
|
367
|
+
for pos, att in zip(positions, attitudes):
|
|
368
|
+
direction = qdcm(att) @ boresight_body
|
|
369
|
+
points.append(pos + direction * (path_center[0] - pos[0]) / direction[0])
|
|
370
|
+
return np.array(points)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def trace_figure(traced, times):
|
|
374
|
+
"""Plotly figure: the SVG reference path against the drawn trace, colored by pen speed."""
|
|
375
|
+
import plotly.graph_objects as go
|
|
376
|
+
|
|
377
|
+
speed = np.linalg.norm(np.gradient(traced, times, axis=0), axis=1)
|
|
378
|
+
|
|
379
|
+
fig = go.Figure()
|
|
380
|
+
fig.add_trace(
|
|
381
|
+
go.Scatter(
|
|
382
|
+
x=path_points[:, 1],
|
|
383
|
+
y=path_points[:, 2],
|
|
384
|
+
mode="lines",
|
|
385
|
+
name="reference path",
|
|
386
|
+
line=dict(color="black", dash="dash", width=1),
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
fig.add_trace(
|
|
390
|
+
go.Scatter(
|
|
391
|
+
x=traced[:, 1],
|
|
392
|
+
y=traced[:, 2],
|
|
393
|
+
mode="markers",
|
|
394
|
+
name="pen trace",
|
|
395
|
+
marker=dict(
|
|
396
|
+
color=speed,
|
|
397
|
+
colorscale="Rainbow",
|
|
398
|
+
size=4,
|
|
399
|
+
colorbar=dict(title="pen speed [m/s]"),
|
|
400
|
+
showscale=True,
|
|
401
|
+
),
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
fig.update_layout(
|
|
405
|
+
title=f"Boresight pen trace — {os.path.basename(svg_file)}",
|
|
406
|
+
xaxis=dict(title="y [m]", scaleanchor="y"),
|
|
407
|
+
yaxis=dict(title="z [m]"),
|
|
408
|
+
legend=dict(x=0.01, y=0.99),
|
|
409
|
+
height=600,
|
|
410
|
+
)
|
|
411
|
+
return fig
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def deviation_figure(progress_dense, trace_err):
|
|
415
|
+
"""Plotly figure: trace deviation against arc length along the path."""
|
|
416
|
+
import plotly.graph_objects as go
|
|
417
|
+
|
|
418
|
+
fig = go.Figure()
|
|
419
|
+
fig.add_trace(
|
|
420
|
+
go.Scatter(
|
|
421
|
+
x=progress_dense,
|
|
422
|
+
y=trace_err * 100,
|
|
423
|
+
mode="lines",
|
|
424
|
+
name="trace error",
|
|
425
|
+
line=dict(color="black", width=1),
|
|
426
|
+
)
|
|
427
|
+
)
|
|
428
|
+
fig.update_layout(
|
|
429
|
+
title=f"Trace deviation — {os.path.basename(svg_file)}",
|
|
430
|
+
xaxis=dict(title="path length [m]"),
|
|
431
|
+
yaxis=dict(title="trace error [cm]"),
|
|
432
|
+
height=400,
|
|
433
|
+
)
|
|
434
|
+
return fig
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
###############################################################################
|
|
438
|
+
# Main: receding-horizon loop
|
|
439
|
+
###############################################################################
|
|
440
|
+
|
|
441
|
+
if __name__ == "__main__":
|
|
442
|
+
print("Quadrotor boresight tracing via MPCC")
|
|
443
|
+
print("=" * 60)
|
|
444
|
+
print(
|
|
445
|
+
f"Path: {os.path.basename(svg_file)}, {total_arc_length:.1f} m of stroke "
|
|
446
|
+
f"at {path_width:.0f} m wide | Horizon: {n_mpc} nodes / {horizon_duration} s"
|
|
447
|
+
)
|
|
448
|
+
print()
|
|
449
|
+
|
|
450
|
+
problem.initialize()
|
|
451
|
+
|
|
452
|
+
max_steps = 600
|
|
453
|
+
|
|
454
|
+
seg_positions = []
|
|
455
|
+
seg_attitudes = []
|
|
456
|
+
seg_progress = []
|
|
457
|
+
seg_times = []
|
|
458
|
+
solve_times = []
|
|
459
|
+
|
|
460
|
+
for step in range(max_steps):
|
|
461
|
+
problem.reset()
|
|
462
|
+
results = problem.solve()
|
|
463
|
+
results = problem.post_process()
|
|
464
|
+
nodes = results.nodes
|
|
465
|
+
solve_times.append(problem.timing_solve)
|
|
466
|
+
|
|
467
|
+
# The executed segment is the dense trajectory from node 0 to node 1
|
|
468
|
+
traj_time = results.trajectory["time"].flatten()
|
|
469
|
+
seg_end = np.searchsorted(traj_time, dt_mpc, side="right")
|
|
470
|
+
seg_positions.append(results.trajectory["position"][:seg_end].copy())
|
|
471
|
+
seg_attitudes.append(results.trajectory["attitude"][:seg_end].copy())
|
|
472
|
+
seg_progress.append(results.trajectory["progress"][:seg_end, 0].copy())
|
|
473
|
+
seg_times.append(traj_time[:seg_end] + step * dt_mpc)
|
|
474
|
+
|
|
475
|
+
cur_progress = nodes["progress"][0, 0]
|
|
476
|
+
print(
|
|
477
|
+
f"step {step:3d}: progress={cur_progress:7.2f} m "
|
|
478
|
+
f"({100 * cur_progress / total_arc_length:5.1f}%), "
|
|
479
|
+
f"trace_cost={nodes['trace_error'][-1, 0]:.5f}"
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
# The pen has crossed the true end of the drawing (the horizon may
|
|
483
|
+
# already be planning into the reference extension, which is fine)
|
|
484
|
+
if nodes["progress"][1, 0] >= total_arc_length:
|
|
485
|
+
break
|
|
486
|
+
|
|
487
|
+
update_initial_conditions(nodes)
|
|
488
|
+
shift_guess(nodes)
|
|
489
|
+
|
|
490
|
+
# --- Trace accuracy report ---
|
|
491
|
+
positions = np.concatenate(seg_positions)
|
|
492
|
+
attitudes = np.concatenate(seg_attitudes)
|
|
493
|
+
progress_dense = np.concatenate(seg_progress)
|
|
494
|
+
times = np.concatenate(seg_times)
|
|
495
|
+
|
|
496
|
+
traced = boresight_trace(positions, attitudes)
|
|
497
|
+
trace_err = np.linalg.norm(traced - ref_point(progress_dense), axis=1)
|
|
498
|
+
print(
|
|
499
|
+
f"\nTraced {progress_dense[-1]:.1f} / {total_arc_length:.1f} m "
|
|
500
|
+
f"in {times[-1]:.1f} s with {n_mpc}-node horizons"
|
|
501
|
+
)
|
|
502
|
+
print(
|
|
503
|
+
f"Trace error vs reference: mean {trace_err.mean() * 100:.2f} cm, "
|
|
504
|
+
f"max {trace_err.max() * 100:.2f} cm"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
# First step pays the JIT warmup, so report it separately
|
|
508
|
+
solve_ms = 1000 * np.array(solve_times[1:])
|
|
509
|
+
print(
|
|
510
|
+
f"Solve time per MPC step: mean {solve_ms.mean():.0f} ms, "
|
|
511
|
+
f"p50 {np.percentile(solve_ms, 50):.0f} ms, "
|
|
512
|
+
f"p99 {np.percentile(solve_ms, 99):.0f} ms, max {solve_ms.max():.0f} ms "
|
|
513
|
+
f"(first step {1000 * solve_times[0]:.0f} ms; replanning budget {1000 * dt_mpc:.0f} ms)"
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
trace_figure(traced, times).show()
|
|
517
|
+
deviation_figure(progress_dense, trace_err).show()
|
|
518
|
+
|
|
519
|
+
# --- Visualization ---
|
|
520
|
+
from examples.drone.logo_utils.quadrotor_mesh import make_quadrotor_mesh
|
|
521
|
+
from openscvx.plotting.viser import add_animation_controls, create_server
|
|
522
|
+
|
|
523
|
+
server = create_server(positions)
|
|
524
|
+
|
|
525
|
+
# The canvas: reference path as a continuous grey line, gold ink stroked on
|
|
526
|
+
# top of it as the animation plays (all trails are line segments — point
|
|
527
|
+
# clouds fail to render on some browser/GPU combinations)
|
|
528
|
+
server.scene.add_line_segments(
|
|
529
|
+
"/reference_path",
|
|
530
|
+
points=np.stack([path_points[:-1], path_points[1:]], axis=1).astype(np.float32),
|
|
531
|
+
colors=(140, 140, 145),
|
|
532
|
+
line_width=2.0,
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
def add_line_trail(name, trail, color, line_width):
|
|
536
|
+
"""Growing line-strip trail; returns an update callback for the animation.
|
|
537
|
+
|
|
538
|
+
The segment buffer keeps a constant size — not-yet-drawn segments are
|
|
539
|
+
collapsed to zero length instead of sliced away, because some clients
|
|
540
|
+
drop buffer updates that change size.
|
|
541
|
+
"""
|
|
542
|
+
segments = np.stack([trail[:-1], trail[1:]], axis=1).astype(np.float32)
|
|
543
|
+
handle = server.scene.add_line_segments(
|
|
544
|
+
name, points=segments, colors=color, line_width=line_width
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
def update(frame_idx: int) -> None:
|
|
548
|
+
shown = segments.copy()
|
|
549
|
+
shown[frame_idx:] = trail[min(frame_idx, len(trail) - 1)]
|
|
550
|
+
handle.points = shown
|
|
551
|
+
|
|
552
|
+
return update
|
|
553
|
+
|
|
554
|
+
# Trails load fully drawn; pressing Play rewinds and animates the drawing
|
|
555
|
+
update_ink = add_line_trail("/ink", traced, (255, 200, 60), line_width=5.0)
|
|
556
|
+
update_flight = add_line_trail("/flight_path", positions, (120, 170, 255), line_width=2.0)
|
|
557
|
+
|
|
558
|
+
# The artist: posed quadrotor mesh with its boresight beam to the pen
|
|
559
|
+
mesh_vertices, mesh_faces = make_quadrotor_mesh()
|
|
560
|
+
drone_handle = server.scene.add_mesh_simple(
|
|
561
|
+
"/drone",
|
|
562
|
+
vertices=np.asarray(mesh_vertices, dtype=np.float32),
|
|
563
|
+
faces=np.asarray(mesh_faces, dtype=np.uint32),
|
|
564
|
+
color=(150, 155, 165),
|
|
565
|
+
position=tuple(positions[0]),
|
|
566
|
+
wxyz=tuple(attitudes[0]),
|
|
567
|
+
)
|
|
568
|
+
beam_handle = server.scene.add_line_segments(
|
|
569
|
+
"/boresight_beam",
|
|
570
|
+
points=np.array([[positions[0], traced[0]]]),
|
|
571
|
+
colors=(255, 90, 90),
|
|
572
|
+
line_width=2.0,
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
def update_drone(frame_idx: int) -> None:
|
|
576
|
+
q = attitudes[frame_idx] / np.linalg.norm(attitudes[frame_idx])
|
|
577
|
+
drone_handle.position = tuple(float(x) for x in positions[frame_idx])
|
|
578
|
+
drone_handle.wxyz = tuple(float(x) for x in q)
|
|
579
|
+
|
|
580
|
+
def update_beam(frame_idx: int) -> None:
|
|
581
|
+
beam_handle.points = np.array([[positions[frame_idx], traced[frame_idx]]])
|
|
582
|
+
|
|
583
|
+
add_animation_controls(server, times, [update_ink, update_flight, update_drone, update_beam])
|
|
584
|
+
server.sleep_forever()
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import jax.numpy as jnp
|
|
4
4
|
import numpy as np
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from svgpathtools import svg2paths2
|
|
8
|
+
except ImportError:
|
|
9
|
+
raise ImportError(
|
|
10
|
+
"svgpathtools is required for the SVG-tracing examples. "
|
|
11
|
+
"Install it with: pip install svgpathtools"
|
|
12
|
+
) from None
|
|
6
13
|
|
|
7
14
|
|
|
8
15
|
def print_svg_path_attributes(svg_file_path):
|
|
@@ -79,3 +86,61 @@ def get_svg_path_function(svg_file_path, path_indices=None, preserve_aspect_rati
|
|
|
79
86
|
path_indices=path_indices,
|
|
80
87
|
preserve_aspect_ratio=preserve_aspect_ratio,
|
|
81
88
|
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def extract_svg_arc_length_path(svg_file_path, n_samples=1000, path_indices=None, width=20.0):
|
|
92
|
+
"""Sample an SVG path uniformly in arc length for progress-parametrized tracing.
|
|
93
|
+
|
|
94
|
+
Unlike :func:`extract_svg_path`, which parametrizes the path by a normalized
|
|
95
|
+
time in ``[0, 1]``, this returns the cumulative arc length ``s`` alongside
|
|
96
|
+
the sampled points, so a contouring (MPCC-style) problem can use ``s``
|
|
97
|
+
directly as its progress coordinate. Expects a single continuous path — a
|
|
98
|
+
pen lift between subpaths would be traced as a straight-line jump.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
svg_file_path: Path to the SVG file.
|
|
102
|
+
n_samples: Number of arc-length-uniform samples to return.
|
|
103
|
+
path_indices: Optional indices selecting which SVG paths to use.
|
|
104
|
+
width: Uniform scale target — the larger bounding-box dimension of the
|
|
105
|
+
returned points spans this many meters (aspect ratio preserved).
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Tuple ``(s, points)`` where ``s`` is a strictly increasing
|
|
109
|
+
``(n_samples,)`` array of arc length starting at 0, and ``points`` is
|
|
110
|
+
an ``(n_samples, 2)`` array of planar coordinates centered at the
|
|
111
|
+
origin with y up.
|
|
112
|
+
"""
|
|
113
|
+
paths, _, _ = svg2paths2(svg_file_path)
|
|
114
|
+
if path_indices is not None:
|
|
115
|
+
paths = [paths[i] for i in path_indices]
|
|
116
|
+
|
|
117
|
+
# Dense parameter-space sampling, allocated per segment by segment length
|
|
118
|
+
dense = []
|
|
119
|
+
total_length = sum(path.length() for path in paths)
|
|
120
|
+
for path in paths:
|
|
121
|
+
for seg in path:
|
|
122
|
+
n_seg = max(2, int(4 * n_samples * seg.length() / total_length))
|
|
123
|
+
for t in np.linspace(0, 1, n_seg, endpoint=False):
|
|
124
|
+
pt = seg.point(t)
|
|
125
|
+
dense.append([pt.real, pt.imag])
|
|
126
|
+
end = paths[-1][-1].point(1.0)
|
|
127
|
+
dense.append([end.real, end.imag])
|
|
128
|
+
dense = np.array(dense)
|
|
129
|
+
|
|
130
|
+
# Center at the origin with y up, then scale uniformly to the target width
|
|
131
|
+
dense[:, 1] = -dense[:, 1]
|
|
132
|
+
dense -= 0.5 * (dense.min(axis=0) + dense.max(axis=0))
|
|
133
|
+
dense *= width / (dense.max(axis=0) - dense.min(axis=0)).max()
|
|
134
|
+
|
|
135
|
+
# Cumulative arc length; drop stationary points so s is strictly increasing
|
|
136
|
+
step = np.linalg.norm(np.diff(dense, axis=0), axis=1)
|
|
137
|
+
keep = np.concatenate([[True], step > 1e-9])
|
|
138
|
+
dense = dense[keep]
|
|
139
|
+
s_dense = np.concatenate([[0.0], np.cumsum(step[step > 1e-9])])
|
|
140
|
+
|
|
141
|
+
# Resample uniformly in arc length
|
|
142
|
+
s = np.linspace(0.0, s_dense[-1], n_samples)
|
|
143
|
+
points = np.column_stack(
|
|
144
|
+
[np.interp(s, s_dense, dense[:, 0]), np.interp(s, s_dense, dense[:, 1])]
|
|
145
|
+
)
|
|
146
|
+
return s, points
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.5.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 5, 3, '
|
|
21
|
+
__version__ = version = '0.5.3.dev43'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 3, 'dev43')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g3b8696f53'
|