embodik 0.3.0__tar.gz → 0.4.1__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.
- {embodik-0.3.0 → embodik-0.4.1}/CHANGELOG.md +68 -0
- {embodik-0.3.0 → embodik-0.4.1}/PKG-INFO +105 -7
- {embodik-0.3.0 → embodik-0.4.1}/README.md +100 -5
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/kinematics_solver.hpp +29 -18
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/robot_model.hpp +20 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/src/kinematics_solver.cpp +32 -1
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/src/robot_model.cpp +71 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/index.md +7 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/kinematics_solver.md +10 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/examples/index.md +10 -0
- embodik-0.4.1/docs/gpu_solvers.md +83 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/index.md +1 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/installation.md +45 -16
- {embodik-0.3.0 → embodik-0.4.1}/mkdocs.yml +1 -0
- {embodik-0.3.0 → embodik-0.4.1}/pixi.toml +6 -2
- {embodik-0.3.0 → embodik-0.4.1}/pyproject.toml +14 -5
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/__init__.py +5 -5
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu/__init__.py +33 -5
- embodik-0.4.1/python/embodik/gpu/casadi_pph_sns.py +432 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu/warp_collision.py +37 -103
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/utils.py +13 -15
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/viser_helpers.py +2 -3
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/visualization.py +5 -27
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/visualization_pinocchio.py +2 -3
- {embodik-0.3.0 → embodik-0.4.1}/python_bindings/src/kinematics_solver_bindings.cpp +11 -0
- {embodik-0.3.0 → embodik-0.4.1}/python_bindings/src/robot_model_bindings.cpp +79 -0
- embodik-0.4.1/scripts/benchmark_pph_sns_batched.py +155 -0
- embodik-0.4.1/scripts/benchmark_pph_sns_comparison.py +299 -0
- embodik-0.4.1/scripts/export_pph_sns.py +43 -0
- embodik-0.4.1/scripts/test_pph_sns_cusadi.py +149 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_gpu_collision.py +1 -1
- embodik-0.4.1/test/test_joint_limit_recovery.py +189 -0
- embodik-0.3.0/python/embodik/_runtime_deps.py +0 -47
- {embodik-0.3.0 → embodik-0.4.1}/.gitattributes +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/.github/workflows/ci.yml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/.github/workflows/docs.yml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/.github/workflows/release.yml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/.gitignore +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/CMakeLists.txt +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/CONTRIBUTING.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/LICENSE +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/MANIFEST.in +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/constraints.hpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/ik_baseline.hpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/tasks.hpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/include/embodik/types.hpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/cpp_core/src/tasks.cpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/robot_model.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/tasks.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/utils.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/api/visualization.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/development.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/examples/basic_ik.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/examples/multi_task_ik.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/quickstart.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/docs/transforms.md +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/01_basic_ik_simple.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/02_collision_aware_IK.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/03_teleop_ik.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/04_gpu_batch_ik.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/05_gpu_collision_batch.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/06_gpu_solver_demo.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/07_parallel_trajectory_tracking.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/example_helpers/__init__.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/example_helpers/dual_arm_ik_helper.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/example_helpers/limit_profiles/alpha_extended.yaml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/example_helpers/limit_profiles/alpha_extended_all.yaml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/example_helpers/limit_profiles/beta_uniform_plus21.yaml +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/robot_model_example.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/utils/__init__.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/utils/robot_models.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/examples/visualization_example.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/fn_velocity_solve.casadi +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/pixi.lock +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/cli.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/examples/__init__.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/examples/basic_ik.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/examples/robot_model.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu/casadi_fi_pesns.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu/export_casadi_velocity_solve.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu/torch_kinematics.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/gpu_solver.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python/embodik/robot_visualizer.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python_bindings/CMakeLists.txt +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python_bindings/src/bindings.cpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/python_bindings/src/tasks_bindings.cpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/benchmark_fi_pesns.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/benchmark_gpu_batched.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/install_cusadi.sh +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/microbench_solve_velocity.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/patch_qhull_cmake.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/setup_pypirc.sh +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/test_cpp_extension.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/test_extension_direct.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/test_gpu_accuracy.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/test_import.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/test_python_utils.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/upload_pypi.sh +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/upload_testpypi.sh +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/scripts/version.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/CMakeLists.txt +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_embodik.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_fi_pesns.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_gpu_solver.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_robot_model.cpp +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_robot_model.py +0 -0
- {embodik-0.3.0 → embodik-0.4.1}/test/test_tasks.py +0 -0
|
@@ -5,9 +5,77 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-02-05
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
- **Removed Python `pin` package from runtime dependencies**: EmbodiK now uses native C++ bindings
|
|
12
|
+
exclusively for all Pinocchio functionality. The `pip pinocchio` (`pin`) package is no longer
|
|
13
|
+
required at runtime, only at build time.
|
|
14
|
+
- This resolves numpy dependency conflicts when using EmbodiK with packages like `hmnd_robot`
|
|
15
|
+
that have different numpy version requirements
|
|
16
|
+
- All rotation utilities (log3, exp3, quaternion conversions) now use native bindings
|
|
17
|
+
- Collision distance computation now uses native `RobotModel.compute_min_collision_distance()`
|
|
18
|
+
- Visualization defaults to direct Viser (no Pinocchio ViserVisualizer dependency)
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **Native math utilities via nanobind**: New C++ bindings for rotation/pose math
|
|
22
|
+
- `embodik.log3(R)`: Compute axis-angle from rotation matrix (replaces `pin.log3`)
|
|
23
|
+
- `embodik.exp3(omega)`: Compute rotation matrix from axis-angle (replaces `pin.exp3`)
|
|
24
|
+
- `embodik.matrix_to_quaternion_wxyz(R)`: Convert rotation matrix to (w,x,y,z) quaternion
|
|
25
|
+
- `embodik.quaternion_wxyz_to_matrix(w,x,y,z)`: Convert quaternion to rotation matrix
|
|
26
|
+
- **Native collision distance API**: RobotModel now exposes collision distance methods
|
|
27
|
+
- `RobotModel.compute_min_collision_distance()`: Get minimum distance across all collision pairs
|
|
28
|
+
- `RobotModel.compute_collision_distances()`: Get distances for all collision pairs
|
|
29
|
+
- **Optional Pinocchio visualization**: `pip install embodik[visualization-pinocchio]` for
|
|
30
|
+
Pinocchio's ViserVisualizer (requires `pin>=3.8.0`)
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Visualization now defaults to direct Viser implementation (no `pin` needed)
|
|
34
|
+
- `utils.py` functions (`get_pose_error_vector`, `compute_pose_error`, `Rt`) use native bindings
|
|
35
|
+
- `visualization.py` quaternion functions use native bindings
|
|
36
|
+
- GPU collision fallback uses native `RobotModel.compute_min_collision_distance()`
|
|
37
|
+
- Removed `_runtime_deps.py` (lazy Pinocchio import no longer needed)
|
|
38
|
+
|
|
39
|
+
### Migration Guide
|
|
40
|
+
If you were using Pinocchio Python bindings directly through EmbodiK:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# Old (v0.3.x) - required pip pinocchio
|
|
44
|
+
import pinocchio as pin
|
|
45
|
+
R_error = pose_goal.rotation @ pose_current.rotation.T
|
|
46
|
+
error = pin.log3(R_error)
|
|
47
|
+
q = pin.Quaternion(R)
|
|
48
|
+
|
|
49
|
+
# New (v0.4.0) - no pip pinocchio needed
|
|
50
|
+
import embodik as eik
|
|
51
|
+
R_error = pose_goal.rotation @ pose_current.rotation.T
|
|
52
|
+
error = eik.log3(R_error)
|
|
53
|
+
w, x, y, z = eik.matrix_to_quaternion_wxyz(R)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For collision distance computation:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# Old (v0.3.x) - required pip pinocchio
|
|
60
|
+
import pinocchio as pin
|
|
61
|
+
pin.updateGeometryPlacements(model, data, collision_model, collision_data)
|
|
62
|
+
for i in range(len(collision_model.collisionPairs)):
|
|
63
|
+
pin.computeDistance(collision_model, collision_data, i)
|
|
64
|
+
dist = collision_data.distanceResults[i].min_distance
|
|
65
|
+
|
|
66
|
+
# New (v0.4.0) - native API
|
|
67
|
+
robot_model.update_configuration(q)
|
|
68
|
+
dist = robot_model.compute_min_collision_distance()
|
|
69
|
+
```
|
|
70
|
+
|
|
8
71
|
## [0.3.0] - 2026-02-03
|
|
9
72
|
|
|
10
73
|
### Added
|
|
74
|
+
- **PPH-SNS Solver**: Alternative GPU-optimized solver (Parallel Penalized Hierarchical SNS)
|
|
75
|
+
- Soft top-k violation selection using softmax weights
|
|
76
|
+
- Limited rank-1 projector updates (1–2 violators per iteration)
|
|
77
|
+
- Achieves ~632,000 IK solves/second at batch size 10,000
|
|
78
|
+
- Benchmark comparison scripts: `benchmark-solver-comparison`, `benchmark-solver-batched`
|
|
11
79
|
- **GPU Acceleration**: Batched velocity IK solving with massive parallelism (100-500x speedup)
|
|
12
80
|
- Achieves ~670,000 IK solves/second at batch size 10,000
|
|
13
81
|
- Ideal for RL training (4096+ parallel environments), motion planning, and dataset generation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: embodik
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: High-performance inverse kinematics solver optimized for cross-embodiment VLA/AI applications
|
|
5
5
|
Keywords: robotics,inverse-kinematics,optimization,motion-planning
|
|
6
6
|
Author-Email: Andy Park <andypark.purdue@gmail.com>
|
|
@@ -23,13 +23,16 @@ Project-URL: Documentation, https://embodik.readthedocs.io
|
|
|
23
23
|
Project-URL: Bug Reports, https://github.com/embodik/embodik/issues
|
|
24
24
|
Requires-Python: >=3.10
|
|
25
25
|
Requires-Dist: numpy>=1.26.0
|
|
26
|
-
Requires-Dist: pin>=3.8.0
|
|
27
26
|
Provides-Extra: visualization
|
|
28
27
|
Requires-Dist: viser>=0.1.0; extra == "visualization"
|
|
29
28
|
Requires-Dist: trimesh>=3.0.0; extra == "visualization"
|
|
30
29
|
Provides-Extra: visualization-legacy
|
|
31
30
|
Requires-Dist: viser>=0.1.0; extra == "visualization-legacy"
|
|
32
31
|
Requires-Dist: yourdfpy>=0.0.52; extra == "visualization-legacy"
|
|
32
|
+
Provides-Extra: visualization-pinocchio
|
|
33
|
+
Requires-Dist: pin>=3.8.0; extra == "visualization-pinocchio"
|
|
34
|
+
Requires-Dist: viser>=0.1.0; extra == "visualization-pinocchio"
|
|
35
|
+
Requires-Dist: trimesh>=3.0.0; extra == "visualization-pinocchio"
|
|
33
36
|
Provides-Extra: examples
|
|
34
37
|
Requires-Dist: pyyaml>=6.0; extra == "examples"
|
|
35
38
|
Requires-Dist: robot_descriptions>=1.0.0; extra == "examples"
|
|
@@ -67,6 +70,7 @@ EmbodiK is a high-performance inverse kinematics (IK) library for cross-embodime
|
|
|
67
70
|
- **Multiple Solvers**: Single-step and full multi-task velocity IK
|
|
68
71
|
- **Singularity Robust**: Advanced inverse methods for stable solutions
|
|
69
72
|
- **Constraint Support**: Joint limits and operational space constraints
|
|
73
|
+
- **Limit Recovery**: Configurable joint limit recovery gain when outside bounds
|
|
70
74
|
- **Collision Avoidance**: Self-collision detection and avoidance
|
|
71
75
|
- **Visualization**: Interactive 3D visualization with Viser
|
|
72
76
|
- **Robot Models**: Built-in support for common robots (Panda, IIWA)
|
|
@@ -74,6 +78,10 @@ EmbodiK is a high-performance inverse kinematics (IK) library for cross-embodime
|
|
|
74
78
|
|
|
75
79
|
## Installation
|
|
76
80
|
|
|
81
|
+
> **Note (v0.4.0+)**: EmbodiK no longer requires the Python `pin` package at runtime.
|
|
82
|
+
> All Pinocchio functionality is exposed through native C++ bindings. This resolves
|
|
83
|
+
> numpy dependency conflicts when using EmbodiK alongside packages like `hmnd_robot`.
|
|
84
|
+
|
|
77
85
|
### Option A: Fresh Environment (No existing Pinocchio)
|
|
78
86
|
|
|
79
87
|
If you don't have Pinocchio/Boost installed locally, installation is straightforward:
|
|
@@ -83,14 +91,14 @@ python3 -m venv .venv
|
|
|
83
91
|
source .venv/bin/activate
|
|
84
92
|
pip install -U pip
|
|
85
93
|
|
|
86
|
-
# Install build dependencies
|
|
94
|
+
# Install build dependencies (pin is needed for build only, not runtime)
|
|
87
95
|
pip install pin scikit-build-core nanobind cmake ninja
|
|
88
96
|
|
|
89
97
|
# Set CMAKE_PREFIX_PATH and install
|
|
90
98
|
export CMAKE_PREFIX_PATH=$(python -c "import pinocchio, pathlib; print(pathlib.Path(pinocchio.__file__).resolve().parents[4])")
|
|
91
99
|
pip install --no-build-isolation embodik
|
|
92
100
|
|
|
93
|
-
# Verify
|
|
101
|
+
# Verify (no pin import needed!)
|
|
94
102
|
python -c "import embodik; print(embodik.__version__, embodik.RobotModel)"
|
|
95
103
|
```
|
|
96
104
|
|
|
@@ -106,7 +114,7 @@ pip install -U pip
|
|
|
106
114
|
# IMPORTANT: Clear local Pinocchio paths to avoid library conflicts
|
|
107
115
|
unset LD_LIBRARY_PATH CMAKE_PREFIX_PATH pinocchio_DIR
|
|
108
116
|
|
|
109
|
-
# Install build dependencies
|
|
117
|
+
# Install build dependencies (pin is needed for build only, not runtime)
|
|
110
118
|
pip install pin scikit-build-core nanobind cmake ninja
|
|
111
119
|
|
|
112
120
|
# Set CMAKE_PREFIX_PATH to the PyPI pin package
|
|
@@ -115,7 +123,7 @@ export CMAKE_PREFIX_PATH=$(python -c "import pinocchio, pathlib; print(pathlib.P
|
|
|
115
123
|
# Install embodik
|
|
116
124
|
pip install --no-build-isolation embodik
|
|
117
125
|
|
|
118
|
-
# Verify
|
|
126
|
+
# Verify (no pin import needed!)
|
|
119
127
|
python -c "import embodik; print(embodik.__version__, embodik.RobotModel)"
|
|
120
128
|
```
|
|
121
129
|
|
|
@@ -173,6 +181,37 @@ if result.status == embodik.SolverStatus.SUCCESS:
|
|
|
173
181
|
|
|
174
182
|
## API Overview
|
|
175
183
|
|
|
184
|
+
### Native Math Utilities
|
|
185
|
+
|
|
186
|
+
EmbodiK provides native bindings for rotation and pose math (no Python `pin` package needed):
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
import embodik as eik
|
|
190
|
+
import numpy as np
|
|
191
|
+
|
|
192
|
+
# Rotation matrix to axis-angle (replaces pin.log3)
|
|
193
|
+
R = np.eye(3)
|
|
194
|
+
omega = eik.log3(R) # Returns [0, 0, 0]
|
|
195
|
+
|
|
196
|
+
# Axis-angle to rotation matrix (replaces pin.exp3)
|
|
197
|
+
omega = np.array([0, 0, np.pi/4])
|
|
198
|
+
R = eik.exp3(omega)
|
|
199
|
+
|
|
200
|
+
# Rotation matrix to quaternion (wxyz format)
|
|
201
|
+
w, x, y, z = eik.matrix_to_quaternion_wxyz(R)
|
|
202
|
+
|
|
203
|
+
# Quaternion to rotation matrix
|
|
204
|
+
R = eik.quaternion_wxyz_to_matrix(w, x, y, z)
|
|
205
|
+
|
|
206
|
+
# Create SE3 transform
|
|
207
|
+
T = eik.Rt(R=R, t=np.array([1, 0, 0]))
|
|
208
|
+
|
|
209
|
+
# Collision distance (no pin needed)
|
|
210
|
+
robot = eik.RobotModel("robot.urdf")
|
|
211
|
+
robot.update_configuration(q)
|
|
212
|
+
min_distance = robot.compute_min_collision_distance()
|
|
213
|
+
```
|
|
214
|
+
|
|
176
215
|
### High-Level API (Recommended)
|
|
177
216
|
|
|
178
217
|
EmbodiK provides a high-level API built on top of Pinocchio for easy robot modeling and IK solving:
|
|
@@ -247,6 +286,8 @@ The repository includes several example scripts:
|
|
|
247
286
|
| `robot_model_example.py` | Robot model usage and configuration |
|
|
248
287
|
| `visualization_example.py` | Interactive 3D visualization examples |
|
|
249
288
|
| `scripts/benchmark_fi_pesns.py` | FI-PeSNS vs CPU accuracy and performance benchmark |
|
|
289
|
+
| `scripts/benchmark_pph_sns_comparison.py` | FI-PeSNS vs PPH-SNS solver comparison (CPU + GPU) |
|
|
290
|
+
| `scripts/benchmark_pph_sns_batched.py` | Batched GPU benchmark for both solvers |
|
|
250
291
|
|
|
251
292
|
### Running Examples
|
|
252
293
|
|
|
@@ -286,6 +327,8 @@ See the [Examples Documentation](docs/examples/index.md) for detailed guides.
|
|
|
286
327
|
|
|
287
328
|
## GPU Acceleration
|
|
288
329
|
|
|
330
|
+
> **Note:** GPU solvers (FI-PeSNS, PPH-SNS) are **experimental** and require further validation. Use with caution in production systems.
|
|
331
|
+
|
|
289
332
|
EmbodiK supports GPU-accelerated batched velocity IK solving for massive parallelism, ideal for:
|
|
290
333
|
|
|
291
334
|
- **RL Training**: 4096+ parallel environments in Isaac Gym/Orbit
|
|
@@ -370,6 +413,9 @@ velocities = result.velocities # (batch_size, n_dof)
|
|
|
370
413
|
| `pixi run -e cuda check-gpu` | Verify CasADi + CusADi + CUDA |
|
|
371
414
|
| `pixi run -e cuda install-cusadi` | Install CusADi from GitHub |
|
|
372
415
|
| `pixi run -e cuda export-casadi` | Export FI-PeSNS velocity solve function |
|
|
416
|
+
| `pixi run -e cuda export-pph-sns` | Export PPH-SNS velocity solve function |
|
|
417
|
+
| `pixi run -e cuda benchmark-solver-comparison` | Compare FI-PeSNS vs PPH-SNS (CPU + GPU) |
|
|
418
|
+
| `pixi run -e cuda benchmark-solver-batched` | Batched GPU benchmark for both solvers |
|
|
373
419
|
| `pixi run -e cuda demo-gpu` | Run GPU solver demo/benchmark |
|
|
374
420
|
| `pixi run -e cuda demo-ik-gpu` | Interactive IK with GPU benchmark panel |
|
|
375
421
|
| `pixi run -e cuda benchmark-gpu` | Batch IK performance benchmark |
|
|
@@ -379,9 +425,33 @@ velocities = result.velocities # (batch_size, n_dof)
|
|
|
379
425
|
| `pixi run -e cuda demo-parallel-tracking` | 100 robots tracking trajectories in parallel |
|
|
380
426
|
| `pixi run -e cuda test-gpu` | Run GPU-specific tests |
|
|
381
427
|
|
|
428
|
+
### GPU Solvers: FI-PeSNS and PPH-SNS
|
|
429
|
+
|
|
430
|
+
EmbodiK provides two GPU-optimized velocity IK solvers, both suitable for CusADi compilation:
|
|
431
|
+
|
|
432
|
+
| Solver | Description | Best For |
|
|
433
|
+
|--------|-------------|----------|
|
|
434
|
+
| **FI-PeSNS** | Fixed-Iteration Penalized eSNS | Default choice, proven accuracy |
|
|
435
|
+
| **PPH-SNS** | Parallel Penalized Hierarchical SNS | Alternative with soft top-k violation selection |
|
|
436
|
+
|
|
437
|
+
Both achieve **100% constraint satisfaction** with zero violations. FI-PeSNS is typically ~7% faster at large batch sizes; PPH-SNS offers a different formulation with limited rank-1 projector updates.
|
|
438
|
+
|
|
439
|
+
**Benchmark (10,000 instances, 7-DOF Panda):**
|
|
440
|
+
|
|
441
|
+
| Solver | Time | Throughput |
|
|
442
|
+
|--------|------|------------|
|
|
443
|
+
| FI-PeSNS | 14.8 ms | **675,000 solves/sec** |
|
|
444
|
+
| PPH-SNS | 15.8 ms | **632,000 solves/sec** |
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
# Compare both solvers
|
|
448
|
+
pixi run -e cuda benchmark-solver-comparison
|
|
449
|
+
pixi run -e cuda benchmark-solver-batched
|
|
450
|
+
```
|
|
451
|
+
|
|
382
452
|
### FI-PeSNS: Fixed-Iteration Penalized eSNS
|
|
383
453
|
|
|
384
|
-
|
|
454
|
+
**FI-PeSNS** is the primary GPU solver—a variant of eSNS that trades exact constraint saturation for simpler, parallelizable penalty-based enforcement:
|
|
385
455
|
|
|
386
456
|
**Key Features:**
|
|
387
457
|
- **SRINV**: Singularity-Robust Inverse for numerical stability
|
|
@@ -418,6 +488,33 @@ for i in range(k_max):
|
|
|
418
488
|
|
|
419
489
|
*GPU benchmarks on NVIDIA RTX A2000 8GB with CusADi-compiled CUDA kernels.*
|
|
420
490
|
|
|
491
|
+
### PPH-SNS: Parallel Penalized Hierarchical SNS
|
|
492
|
+
|
|
493
|
+
**PPH-SNS** is an alternative GPU-native design with:
|
|
494
|
+
|
|
495
|
+
- **Soft top-k violation selection** using softmax weights
|
|
496
|
+
- **Limited rank-1 projector updates** (1–2 violators per iteration)
|
|
497
|
+
- **Aggressive penalty ramping** (γ=3.0)
|
|
498
|
+
- **Fixed-depth unrolling** for CusADi compilation
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
# Export PPH-SNS (writes to ~/.local/cusadi/src/casadi_functions/)
|
|
502
|
+
pixi run -e cuda export-pph-sns
|
|
503
|
+
|
|
504
|
+
# Compile to CUDA kernel
|
|
505
|
+
cd ~/.local/cusadi && python run_codegen.py --fn=fn_pph_sns_velocity_solve
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
```python
|
|
509
|
+
from embodik.gpu.casadi_pph_sns import build_pph_sns_single_task
|
|
510
|
+
|
|
511
|
+
fn = build_pph_sns_single_task(
|
|
512
|
+
n_dof=7, task_dim=6, n_constraints=7,
|
|
513
|
+
k_max=14, m_max=2, # Outer iterations, max saturations per iteration
|
|
514
|
+
)
|
|
515
|
+
velocity, scales = fn(target, jacobian.flatten(), C, lower, upper)
|
|
516
|
+
```
|
|
517
|
+
|
|
421
518
|
### Parallel Trajectory Tracking Demo
|
|
422
519
|
|
|
423
520
|
Visualize GPU parallelization with 100 robot instances simultaneously tracking different trajectories:
|
|
@@ -518,6 +615,7 @@ Full documentation is available at: **https://embodik.github.io/embodik/**
|
|
|
518
615
|
|
|
519
616
|
- [Installation Guide](docs/installation.md) - Detailed installation instructions
|
|
520
617
|
- [Quickstart](docs/quickstart.md) - Get started in 5 minutes
|
|
618
|
+
- [GPU Solvers](docs/gpu_solvers.md) - FI-PeSNS and PPH-SNS GPU-accelerated solvers
|
|
521
619
|
- [API Reference](docs/api/index.md) - Complete API documentation
|
|
522
620
|
- [Examples](docs/examples/index.md) - Example code and tutorials
|
|
523
621
|
- [Development Guide](docs/development.md) - Contributing and development
|
|
@@ -18,6 +18,7 @@ EmbodiK is a high-performance inverse kinematics (IK) library for cross-embodime
|
|
|
18
18
|
- **Multiple Solvers**: Single-step and full multi-task velocity IK
|
|
19
19
|
- **Singularity Robust**: Advanced inverse methods for stable solutions
|
|
20
20
|
- **Constraint Support**: Joint limits and operational space constraints
|
|
21
|
+
- **Limit Recovery**: Configurable joint limit recovery gain when outside bounds
|
|
21
22
|
- **Collision Avoidance**: Self-collision detection and avoidance
|
|
22
23
|
- **Visualization**: Interactive 3D visualization with Viser
|
|
23
24
|
- **Robot Models**: Built-in support for common robots (Panda, IIWA)
|
|
@@ -25,6 +26,10 @@ EmbodiK is a high-performance inverse kinematics (IK) library for cross-embodime
|
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
27
28
|
|
|
29
|
+
> **Note (v0.4.0+)**: EmbodiK no longer requires the Python `pin` package at runtime.
|
|
30
|
+
> All Pinocchio functionality is exposed through native C++ bindings. This resolves
|
|
31
|
+
> numpy dependency conflicts when using EmbodiK alongside packages like `hmnd_robot`.
|
|
32
|
+
|
|
28
33
|
### Option A: Fresh Environment (No existing Pinocchio)
|
|
29
34
|
|
|
30
35
|
If you don't have Pinocchio/Boost installed locally, installation is straightforward:
|
|
@@ -34,14 +39,14 @@ python3 -m venv .venv
|
|
|
34
39
|
source .venv/bin/activate
|
|
35
40
|
pip install -U pip
|
|
36
41
|
|
|
37
|
-
# Install build dependencies
|
|
42
|
+
# Install build dependencies (pin is needed for build only, not runtime)
|
|
38
43
|
pip install pin scikit-build-core nanobind cmake ninja
|
|
39
44
|
|
|
40
45
|
# Set CMAKE_PREFIX_PATH and install
|
|
41
46
|
export CMAKE_PREFIX_PATH=$(python -c "import pinocchio, pathlib; print(pathlib.Path(pinocchio.__file__).resolve().parents[4])")
|
|
42
47
|
pip install --no-build-isolation embodik
|
|
43
48
|
|
|
44
|
-
# Verify
|
|
49
|
+
# Verify (no pin import needed!)
|
|
45
50
|
python -c "import embodik; print(embodik.__version__, embodik.RobotModel)"
|
|
46
51
|
```
|
|
47
52
|
|
|
@@ -57,7 +62,7 @@ pip install -U pip
|
|
|
57
62
|
# IMPORTANT: Clear local Pinocchio paths to avoid library conflicts
|
|
58
63
|
unset LD_LIBRARY_PATH CMAKE_PREFIX_PATH pinocchio_DIR
|
|
59
64
|
|
|
60
|
-
# Install build dependencies
|
|
65
|
+
# Install build dependencies (pin is needed for build only, not runtime)
|
|
61
66
|
pip install pin scikit-build-core nanobind cmake ninja
|
|
62
67
|
|
|
63
68
|
# Set CMAKE_PREFIX_PATH to the PyPI pin package
|
|
@@ -66,7 +71,7 @@ export CMAKE_PREFIX_PATH=$(python -c "import pinocchio, pathlib; print(pathlib.P
|
|
|
66
71
|
# Install embodik
|
|
67
72
|
pip install --no-build-isolation embodik
|
|
68
73
|
|
|
69
|
-
# Verify
|
|
74
|
+
# Verify (no pin import needed!)
|
|
70
75
|
python -c "import embodik; print(embodik.__version__, embodik.RobotModel)"
|
|
71
76
|
```
|
|
72
77
|
|
|
@@ -124,6 +129,37 @@ if result.status == embodik.SolverStatus.SUCCESS:
|
|
|
124
129
|
|
|
125
130
|
## API Overview
|
|
126
131
|
|
|
132
|
+
### Native Math Utilities
|
|
133
|
+
|
|
134
|
+
EmbodiK provides native bindings for rotation and pose math (no Python `pin` package needed):
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import embodik as eik
|
|
138
|
+
import numpy as np
|
|
139
|
+
|
|
140
|
+
# Rotation matrix to axis-angle (replaces pin.log3)
|
|
141
|
+
R = np.eye(3)
|
|
142
|
+
omega = eik.log3(R) # Returns [0, 0, 0]
|
|
143
|
+
|
|
144
|
+
# Axis-angle to rotation matrix (replaces pin.exp3)
|
|
145
|
+
omega = np.array([0, 0, np.pi/4])
|
|
146
|
+
R = eik.exp3(omega)
|
|
147
|
+
|
|
148
|
+
# Rotation matrix to quaternion (wxyz format)
|
|
149
|
+
w, x, y, z = eik.matrix_to_quaternion_wxyz(R)
|
|
150
|
+
|
|
151
|
+
# Quaternion to rotation matrix
|
|
152
|
+
R = eik.quaternion_wxyz_to_matrix(w, x, y, z)
|
|
153
|
+
|
|
154
|
+
# Create SE3 transform
|
|
155
|
+
T = eik.Rt(R=R, t=np.array([1, 0, 0]))
|
|
156
|
+
|
|
157
|
+
# Collision distance (no pin needed)
|
|
158
|
+
robot = eik.RobotModel("robot.urdf")
|
|
159
|
+
robot.update_configuration(q)
|
|
160
|
+
min_distance = robot.compute_min_collision_distance()
|
|
161
|
+
```
|
|
162
|
+
|
|
127
163
|
### High-Level API (Recommended)
|
|
128
164
|
|
|
129
165
|
EmbodiK provides a high-level API built on top of Pinocchio for easy robot modeling and IK solving:
|
|
@@ -198,6 +234,8 @@ The repository includes several example scripts:
|
|
|
198
234
|
| `robot_model_example.py` | Robot model usage and configuration |
|
|
199
235
|
| `visualization_example.py` | Interactive 3D visualization examples |
|
|
200
236
|
| `scripts/benchmark_fi_pesns.py` | FI-PeSNS vs CPU accuracy and performance benchmark |
|
|
237
|
+
| `scripts/benchmark_pph_sns_comparison.py` | FI-PeSNS vs PPH-SNS solver comparison (CPU + GPU) |
|
|
238
|
+
| `scripts/benchmark_pph_sns_batched.py` | Batched GPU benchmark for both solvers |
|
|
201
239
|
|
|
202
240
|
### Running Examples
|
|
203
241
|
|
|
@@ -237,6 +275,8 @@ See the [Examples Documentation](docs/examples/index.md) for detailed guides.
|
|
|
237
275
|
|
|
238
276
|
## GPU Acceleration
|
|
239
277
|
|
|
278
|
+
> **Note:** GPU solvers (FI-PeSNS, PPH-SNS) are **experimental** and require further validation. Use with caution in production systems.
|
|
279
|
+
|
|
240
280
|
EmbodiK supports GPU-accelerated batched velocity IK solving for massive parallelism, ideal for:
|
|
241
281
|
|
|
242
282
|
- **RL Training**: 4096+ parallel environments in Isaac Gym/Orbit
|
|
@@ -321,6 +361,9 @@ velocities = result.velocities # (batch_size, n_dof)
|
|
|
321
361
|
| `pixi run -e cuda check-gpu` | Verify CasADi + CusADi + CUDA |
|
|
322
362
|
| `pixi run -e cuda install-cusadi` | Install CusADi from GitHub |
|
|
323
363
|
| `pixi run -e cuda export-casadi` | Export FI-PeSNS velocity solve function |
|
|
364
|
+
| `pixi run -e cuda export-pph-sns` | Export PPH-SNS velocity solve function |
|
|
365
|
+
| `pixi run -e cuda benchmark-solver-comparison` | Compare FI-PeSNS vs PPH-SNS (CPU + GPU) |
|
|
366
|
+
| `pixi run -e cuda benchmark-solver-batched` | Batched GPU benchmark for both solvers |
|
|
324
367
|
| `pixi run -e cuda demo-gpu` | Run GPU solver demo/benchmark |
|
|
325
368
|
| `pixi run -e cuda demo-ik-gpu` | Interactive IK with GPU benchmark panel |
|
|
326
369
|
| `pixi run -e cuda benchmark-gpu` | Batch IK performance benchmark |
|
|
@@ -330,9 +373,33 @@ velocities = result.velocities # (batch_size, n_dof)
|
|
|
330
373
|
| `pixi run -e cuda demo-parallel-tracking` | 100 robots tracking trajectories in parallel |
|
|
331
374
|
| `pixi run -e cuda test-gpu` | Run GPU-specific tests |
|
|
332
375
|
|
|
376
|
+
### GPU Solvers: FI-PeSNS and PPH-SNS
|
|
377
|
+
|
|
378
|
+
EmbodiK provides two GPU-optimized velocity IK solvers, both suitable for CusADi compilation:
|
|
379
|
+
|
|
380
|
+
| Solver | Description | Best For |
|
|
381
|
+
|--------|-------------|----------|
|
|
382
|
+
| **FI-PeSNS** | Fixed-Iteration Penalized eSNS | Default choice, proven accuracy |
|
|
383
|
+
| **PPH-SNS** | Parallel Penalized Hierarchical SNS | Alternative with soft top-k violation selection |
|
|
384
|
+
|
|
385
|
+
Both achieve **100% constraint satisfaction** with zero violations. FI-PeSNS is typically ~7% faster at large batch sizes; PPH-SNS offers a different formulation with limited rank-1 projector updates.
|
|
386
|
+
|
|
387
|
+
**Benchmark (10,000 instances, 7-DOF Panda):**
|
|
388
|
+
|
|
389
|
+
| Solver | Time | Throughput |
|
|
390
|
+
|--------|------|------------|
|
|
391
|
+
| FI-PeSNS | 14.8 ms | **675,000 solves/sec** |
|
|
392
|
+
| PPH-SNS | 15.8 ms | **632,000 solves/sec** |
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
# Compare both solvers
|
|
396
|
+
pixi run -e cuda benchmark-solver-comparison
|
|
397
|
+
pixi run -e cuda benchmark-solver-batched
|
|
398
|
+
```
|
|
399
|
+
|
|
333
400
|
### FI-PeSNS: Fixed-Iteration Penalized eSNS
|
|
334
401
|
|
|
335
|
-
|
|
402
|
+
**FI-PeSNS** is the primary GPU solver—a variant of eSNS that trades exact constraint saturation for simpler, parallelizable penalty-based enforcement:
|
|
336
403
|
|
|
337
404
|
**Key Features:**
|
|
338
405
|
- **SRINV**: Singularity-Robust Inverse for numerical stability
|
|
@@ -369,6 +436,33 @@ for i in range(k_max):
|
|
|
369
436
|
|
|
370
437
|
*GPU benchmarks on NVIDIA RTX A2000 8GB with CusADi-compiled CUDA kernels.*
|
|
371
438
|
|
|
439
|
+
### PPH-SNS: Parallel Penalized Hierarchical SNS
|
|
440
|
+
|
|
441
|
+
**PPH-SNS** is an alternative GPU-native design with:
|
|
442
|
+
|
|
443
|
+
- **Soft top-k violation selection** using softmax weights
|
|
444
|
+
- **Limited rank-1 projector updates** (1–2 violators per iteration)
|
|
445
|
+
- **Aggressive penalty ramping** (γ=3.0)
|
|
446
|
+
- **Fixed-depth unrolling** for CusADi compilation
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
# Export PPH-SNS (writes to ~/.local/cusadi/src/casadi_functions/)
|
|
450
|
+
pixi run -e cuda export-pph-sns
|
|
451
|
+
|
|
452
|
+
# Compile to CUDA kernel
|
|
453
|
+
cd ~/.local/cusadi && python run_codegen.py --fn=fn_pph_sns_velocity_solve
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
```python
|
|
457
|
+
from embodik.gpu.casadi_pph_sns import build_pph_sns_single_task
|
|
458
|
+
|
|
459
|
+
fn = build_pph_sns_single_task(
|
|
460
|
+
n_dof=7, task_dim=6, n_constraints=7,
|
|
461
|
+
k_max=14, m_max=2, # Outer iterations, max saturations per iteration
|
|
462
|
+
)
|
|
463
|
+
velocity, scales = fn(target, jacobian.flatten(), C, lower, upper)
|
|
464
|
+
```
|
|
465
|
+
|
|
372
466
|
### Parallel Trajectory Tracking Demo
|
|
373
467
|
|
|
374
468
|
Visualize GPU parallelization with 100 robot instances simultaneously tracking different trajectories:
|
|
@@ -469,6 +563,7 @@ Full documentation is available at: **https://embodik.github.io/embodik/**
|
|
|
469
563
|
|
|
470
564
|
- [Installation Guide](docs/installation.md) - Detailed installation instructions
|
|
471
565
|
- [Quickstart](docs/quickstart.md) - Get started in 5 minutes
|
|
566
|
+
- [GPU Solvers](docs/gpu_solvers.md) - FI-PeSNS and PPH-SNS GPU-accelerated solvers
|
|
472
567
|
- [API Reference](docs/api/index.md) - Complete API documentation
|
|
473
568
|
- [Examples](docs/examples/index.md) - Example code and tutorials
|
|
474
569
|
- [Development Guide](docs/development.md) - Contributing and development
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
#pragma once
|
|
10
10
|
|
|
11
|
+
#include <algorithm>
|
|
11
12
|
#include <cstdint>
|
|
12
13
|
#include <embodik/robot_model.hpp>
|
|
13
14
|
#include <embodik/tasks.hpp>
|
|
@@ -189,6 +190,15 @@ public:
|
|
|
189
190
|
*/
|
|
190
191
|
void set_damping(double damping) { damping_ = damping; }
|
|
191
192
|
|
|
193
|
+
/**
|
|
194
|
+
* @brief Set recovery gain for joint limit violations.
|
|
195
|
+
*
|
|
196
|
+
* Values are clamped to [0, 1]. Higher values recover faster.
|
|
197
|
+
*/
|
|
198
|
+
void set_limit_recovery_gain(double gain) {
|
|
199
|
+
limit_recovery_gain_ = std::clamp(gain, 0.0, 1.0);
|
|
200
|
+
}
|
|
201
|
+
|
|
192
202
|
/**
|
|
193
203
|
* @brief Enable verbose debugging for position IK iterations.
|
|
194
204
|
* @param enable True to print/log per-iteration errors and store traces.
|
|
@@ -280,6 +290,24 @@ public:
|
|
|
280
290
|
std::vector<std::pair<std::string, std::string>>
|
|
281
291
|
get_active_collision_pairs() const;
|
|
282
292
|
|
|
293
|
+
/**
|
|
294
|
+
* @brief Calculate velocity box constraints based on position, velocity, and
|
|
295
|
+
* acceleration limits.
|
|
296
|
+
*
|
|
297
|
+
* Velocity limits are computed as:
|
|
298
|
+
* min(position_margin/dt, velocity_limit, sqrt(2*accel*margin))
|
|
299
|
+
*
|
|
300
|
+
* @param position_margin_lower Distance from current position to lower limit
|
|
301
|
+
* @param position_margin_upper Distance from current position to upper limit
|
|
302
|
+
* @param velocity_limit Maximum allowed velocity
|
|
303
|
+
* @param acceleration_limit Maximum allowed acceleration
|
|
304
|
+
* @param dt Time step
|
|
305
|
+
* @return Pair of (lower_velocity_limit, upper_velocity_limit)
|
|
306
|
+
*/
|
|
307
|
+
std::pair<double, double> calculate_velocity_box_constraint(
|
|
308
|
+
double position_margin_lower, double position_margin_upper,
|
|
309
|
+
double velocity_limit, double acceleration_limit, double dt) const;
|
|
310
|
+
|
|
283
311
|
private:
|
|
284
312
|
std::shared_ptr<RobotModel> robot_;
|
|
285
313
|
std::vector<std::shared_ptr<Task>> tasks_;
|
|
@@ -294,6 +322,7 @@ private:
|
|
|
294
322
|
double norm_threshold_ = 1e10;
|
|
295
323
|
int max_zero_scale_iterations_ = 2;
|
|
296
324
|
bool position_ik_debug_ = false;
|
|
325
|
+
double limit_recovery_gain_ = 0.5;
|
|
297
326
|
|
|
298
327
|
// Constraint options
|
|
299
328
|
bool use_velocity_limits_ = true;
|
|
@@ -349,24 +378,6 @@ private:
|
|
|
349
378
|
bool collision_pair_allowed(const std::string &a, const std::string &b) const;
|
|
350
379
|
std::optional<CollisionConstraintResult> compute_collision_constraint();
|
|
351
380
|
|
|
352
|
-
/**
|
|
353
|
-
* @brief Calculate velocity box constraints based on position, velocity, and
|
|
354
|
-
* acceleration limits
|
|
355
|
-
*
|
|
356
|
-
* Velocity limits are computed as:
|
|
357
|
-
* min(position_margin/dt, velocity_limit, sqrt(2*accel*margin))
|
|
358
|
-
*
|
|
359
|
-
* @param position_margin_lower Distance from current position to lower limit
|
|
360
|
-
* @param position_margin_upper Distance from current position to upper limit
|
|
361
|
-
* @param velocity_limit Maximum allowed velocity
|
|
362
|
-
* @param acceleration_limit Maximum allowed acceleration
|
|
363
|
-
* @param dt Time step
|
|
364
|
-
* @return Pair of (lower_velocity_limit, upper_velocity_limit)
|
|
365
|
-
*/
|
|
366
|
-
std::pair<double, double> calculate_velocity_box_constraint(
|
|
367
|
-
double position_margin_lower, double position_margin_upper,
|
|
368
|
-
double velocity_limit, double acceleration_limit, double dt) const;
|
|
369
|
-
|
|
370
381
|
public:
|
|
371
382
|
// ========== Position IK Methods ==========
|
|
372
383
|
|
|
@@ -259,6 +259,26 @@ public:
|
|
|
259
259
|
return collision_model_ != nullptr && collision_data_ != nullptr;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* @brief Compute minimum collision distance at current configuration.
|
|
264
|
+
*
|
|
265
|
+
* Updates geometry placements and computes distances for all collision pairs,
|
|
266
|
+
* returning the minimum distance found.
|
|
267
|
+
*
|
|
268
|
+
* @return Minimum collision distance, or infinity if no collision geometry
|
|
269
|
+
*/
|
|
270
|
+
double compute_min_collision_distance() const;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @brief Compute collision distances for all pairs at current configuration.
|
|
274
|
+
*
|
|
275
|
+
* Updates geometry placements and computes distances for all collision pairs.
|
|
276
|
+
*
|
|
277
|
+
* @return Vector of distances for each collision pair, in order of
|
|
278
|
+
* collision_model.collisionPairs
|
|
279
|
+
*/
|
|
280
|
+
std::vector<double> compute_collision_distances() const;
|
|
281
|
+
|
|
262
282
|
// Access to URDF path for visualization
|
|
263
283
|
const std::string &urdf_path() const { return urdf_path_; }
|
|
264
284
|
|
|
@@ -691,8 +691,17 @@ KinematicsSolver::compute_collision_constraint() {
|
|
|
691
691
|
std::pair<double, double> KinematicsSolver::calculate_velocity_box_constraint(
|
|
692
692
|
double position_margin_lower, double position_margin_upper,
|
|
693
693
|
double velocity_limit, double acceleration_limit, double dt) const {
|
|
694
|
+
constexpr double kMarginEpsilon = 1e-4;
|
|
695
|
+
const double raw_margin_lower = position_margin_lower;
|
|
696
|
+
const double raw_margin_upper = position_margin_upper;
|
|
697
|
+
const bool outside_lower = raw_margin_lower < -kMarginEpsilon;
|
|
698
|
+
const bool outside_upper = raw_margin_upper < -kMarginEpsilon;
|
|
699
|
+
|
|
700
|
+
if (outside_lower && outside_upper) {
|
|
701
|
+
return std::make_pair(-velocity_limit, velocity_limit);
|
|
702
|
+
}
|
|
694
703
|
|
|
695
|
-
// Clamp margins to be non-negative
|
|
704
|
+
// Clamp margins to be non-negative for nominal bounds.
|
|
696
705
|
position_margin_lower = std::max(0.0, position_margin_lower);
|
|
697
706
|
position_margin_upper = std::max(0.0, position_margin_upper);
|
|
698
707
|
|
|
@@ -712,6 +721,28 @@ std::pair<double, double> KinematicsSolver::calculate_velocity_box_constraint(
|
|
|
712
721
|
double upper_limit =
|
|
713
722
|
std::min({vel_from_pos_upper, velocity_limit, vel_from_accel_upper});
|
|
714
723
|
|
|
724
|
+
if (outside_lower) {
|
|
725
|
+
const double violation = -raw_margin_lower;
|
|
726
|
+
const double recovery_min = (limit_recovery_gain_ * violation) / dt;
|
|
727
|
+
const double recovery_lower = std::min(recovery_min, velocity_limit);
|
|
728
|
+
if (recovery_lower > lower_limit) {
|
|
729
|
+
lower_limit = recovery_lower;
|
|
730
|
+
}
|
|
731
|
+
} else if (outside_upper) {
|
|
732
|
+
const double violation = -raw_margin_upper;
|
|
733
|
+
const double recovery_max = -(limit_recovery_gain_ * violation) / dt;
|
|
734
|
+
const double recovery_upper = std::max(recovery_max, -velocity_limit);
|
|
735
|
+
if (recovery_upper < upper_limit) {
|
|
736
|
+
upper_limit = recovery_upper;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (lower_limit > upper_limit) {
|
|
741
|
+
const double midpoint = 0.5 * (lower_limit + upper_limit);
|
|
742
|
+
lower_limit = midpoint;
|
|
743
|
+
upper_limit = midpoint;
|
|
744
|
+
}
|
|
745
|
+
|
|
715
746
|
return std::make_pair(lower_limit, upper_limit);
|
|
716
747
|
}
|
|
717
748
|
|