griip-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griip_sdk-0.1.0/PKG-INFO +72 -0
- griip_sdk-0.1.0/README.md +40 -0
- griip_sdk-0.1.0/pyproject.toml +47 -0
- griip_sdk-0.1.0/src/griip_sdk/__init__.py +4 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/cartesian_pose.py +219 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/config_store.py +114 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/README.md +109 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/__init__.py +38 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/config/routine.yaml +9 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/core.py +732 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/main.py +479 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/routines.py +190 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/run_hand_eye_calibration.py +133 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/hand_eye_calib/transforms.py +57 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/scanning/__init__.py +11 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/scanning/environment_scanning.py +275 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/scanning/run_environment_scanning.py +147 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/conftest.py +33 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/test_environment_scanning.py +378 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/test_hand_eye_calibration_service.py +316 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/test_hand_eye_helpers.py +211 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/tests/test_transforms.py +130 -0
- griip_sdk-0.1.0/src/griip_sdk/calibration/units.py +104 -0
- griip_sdk-0.1.0/src/griip_sdk/generated/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/generated/_griip_sdk_pb2_generated.py +86 -0
- griip_sdk-0.1.0/src/griip_sdk/generated/griip_sdk_pb2.py +24 -0
- griip_sdk-0.1.0/src/griip_sdk/generated/griip_sdk_pb2.pyi +561 -0
- griip_sdk-0.1.0/src/griip_sdk/generated/griip_sdk_pb2_grpc.py +515 -0
- griip_sdk-0.1.0/src/griip_sdk/grpc_adapters/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/grpc_adapters/servicer_as_stub.py +125 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/gripper.py +158 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/mocks/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/robot_client.py +1373 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/robotiq_2f140_gripper.py +266 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/tests/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/tests/conftest.py +8 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/tests/test_reset_to_normal.py +101 -0
- griip_sdk-0.1.0/src/griip_sdk/hardware/tests/test_robot_client_lifecycle.py +134 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/README.md +186 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/__init__.py +8 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/__main__.py +244 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/_actors.py +273 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/_geometry.py +59 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/editor.py +1143 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/object_io.py +53 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/sphere_generation_tool/urdf_io.py +312 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/conftest.py +123 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/test_editor_math.py +79 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/test_editor_smoke.py +469 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/test_mesh_loading.py +66 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/test_object_io.py +72 -0
- griip_sdk-0.1.0/src/griip_sdk/modeling/tests/test_urdf_io.py +357 -0
- griip_sdk-0.1.0/src/griip_sdk/orchestration/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/config/__init__.py +5 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/config/loader.py +108 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/config/models.py +269 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/config/registry.py +16 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/config/tests/test_loader.py +69 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/__init__.py +1 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/bench.yaml +85 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/collision_cuboids/full.json +257 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/collision_cuboids/hand_eye.json +122 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/gripper_collision_spheres/minimal.json +1 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/hand_eye_routine.yaml +9 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/dormakaba/cells/bench/io/bench.json +4 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/collision_cuboids/rnd.json +287 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/gripper_collision_spheres/robotiq_2f140.json +4 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/gripper_collision_spheres/robotiq_2f140_vp15_closed.json +236 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/io/rnd.json +34 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/object_spheres/a10v.json +9 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/object_spheres/vp15bdy.json +1 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/cells/rnd/rnd.yaml +259 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/grasps/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/grasps/a10v.json +11042 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/grasps/generate_grasps.py +301 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/grasps/vp15_mcalpine.json +13802 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/bin_shaker.py +86 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/camera.py +132 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/ft_sensor.py +347 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/io_client.py +117 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/mocks/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/mocks/camera_mock.py +55 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/mocks/gripper_client_mock.py +120 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/mocks/io_client_mock.py +28 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/mocks/robot_client_mock.py +575 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/hardware/torquing_client.py +23 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/bin_cuboid_extraction.py +76 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/callbacks.py +43 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/errors.py +19 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/grasp_queue.py +71 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/pick_and_place_manager.py +1665 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/roi_scheduler.py +150 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/tests/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/tests/conftest.py +156 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/tests/test_pick_and_place_manager.py +507 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/tests/test_pick_result_adapter.py +148 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/trajectory_store.py +170 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/types.py +238 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/orchestrators/vp15_aligner.py +62 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/part_ids.py +36 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/placement_handlers/__init__.py +17 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/placement_handlers/a10v.py +621 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/placement_handlers/vp15.py +281 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/robotiq_2f140_collision_model.py +200 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/run.py +1074 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/run_grpc.py +9 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/tests/__init__.py +0 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/projects/mcalpine/tests/test_part_ids.py +25 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/run_environment_scanning.py +93 -0
- griip_sdk-0.1.0/src/griip_sdk/prototyping/run_hand_eye_calibration.py +86 -0
griip_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: griip-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GRIIP SDK: hardware control and orchestration for bin picking
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Keywords: Vention,GRIIP,SDK,Robot,Gripper,Automation
|
|
7
|
+
Author: VentionCo
|
|
8
|
+
Requires-Python: >=3.10,<3.11
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Requires-Dist: PyYAML (>=6.0,<7.0)
|
|
14
|
+
Requires-Dist: defusedxml (>=0.7.1,<0.8.0)
|
|
15
|
+
Requires-Dist: grpcio (>=1.78.0)
|
|
16
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
17
|
+
Requires-Dist: numpy (>=1.24)
|
|
18
|
+
Requires-Dist: omegaconf (>=2.3.0,<3.0.0)
|
|
19
|
+
Requires-Dist: opencv-python-headless (>=4.9.0,<5.0.0)
|
|
20
|
+
Requires-Dist: paho-mqtt (>=2.1.0,<3.0.0)
|
|
21
|
+
Requires-Dist: protobuf (>=4.0)
|
|
22
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
23
|
+
Requires-Dist: pymodbus (>=3.12.1,<4.0.0)
|
|
24
|
+
Requires-Dist: scipy (>=1.11.0,<2.0)
|
|
25
|
+
Requires-Dist: spatialmath-python (>=1.1.15,<2.0.0)
|
|
26
|
+
Requires-Dist: toppra (>=0.6.0)
|
|
27
|
+
Requires-Dist: trimesh (>=4.11,<5.0)
|
|
28
|
+
Requires-Dist: urchin (>=0.0.30,<0.0.31)
|
|
29
|
+
Requires-Dist: vention-firmware-grpc-client (>=1.0.0)
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# griip-sdk
|
|
33
|
+
|
|
34
|
+
Python SDK for hardware control and orchestration of gripper-based bin picking on Vention's MachineMotion platform.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install griip-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Prerequisites for hardware control
|
|
43
|
+
|
|
44
|
+
`griip-sdk` depends on two private wheels that are not on PyPI. Install them manually from GitHub Releases before using hardware features:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# vamp_planner
|
|
48
|
+
pip install "https://github.com/VentionCo/platform-products/releases/download/vamp-wheel-v0.6.1-20260313/vamp_planner-<version>-cp310-cp310-linux_aarch64.whl"
|
|
49
|
+
|
|
50
|
+
# foundationstereoclient
|
|
51
|
+
pip install "https://github.com/VentionCo/platform-products/releases/download/foundationstereoclient/v1.11.0/foundationstereoclient-<version>-cp310-cp310-linux_aarch64.whl"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> These manual steps are temporary. Both packages will eventually become MachineMotion-side services, at which point this step goes away.
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from griip_sdk import BaseGripper, GripperMoveResult, Robotiq2F140Gripper
|
|
60
|
+
|
|
61
|
+
gripper = Robotiq2F140Gripper(host="<machineMotion-ip>")
|
|
62
|
+
result: GripperMoveResult = gripper.move(position=0.5)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What's NOT shipped on PyPI
|
|
66
|
+
|
|
67
|
+
The demo runners under `griip_sdk.prototyping.run_*` import `mmai_griip_api` (the gRPC service). They are not part of the public API and will fail to import unless `mmai-griip-api` is installed alongside the SDK (e.g. via the Debian package on a MachineMotion).
|
|
68
|
+
|
|
69
|
+
## Releases
|
|
70
|
+
|
|
71
|
+
Versioning is hand-bumped semver. Bump `version` in `pyproject.toml` before triggering the PyPI publish workflow (`publish-griip-sdk-pypi.yml`).
|
|
72
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# griip-sdk
|
|
2
|
+
|
|
3
|
+
Python SDK for hardware control and orchestration of gripper-based bin picking on Vention's MachineMotion platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install griip-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Prerequisites for hardware control
|
|
12
|
+
|
|
13
|
+
`griip-sdk` depends on two private wheels that are not on PyPI. Install them manually from GitHub Releases before using hardware features:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# vamp_planner
|
|
17
|
+
pip install "https://github.com/VentionCo/platform-products/releases/download/vamp-wheel-v0.6.1-20260313/vamp_planner-<version>-cp310-cp310-linux_aarch64.whl"
|
|
18
|
+
|
|
19
|
+
# foundationstereoclient
|
|
20
|
+
pip install "https://github.com/VentionCo/platform-products/releases/download/foundationstereoclient/v1.11.0/foundationstereoclient-<version>-cp310-cp310-linux_aarch64.whl"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> These manual steps are temporary. Both packages will eventually become MachineMotion-side services, at which point this step goes away.
|
|
24
|
+
|
|
25
|
+
## Quickstart
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from griip_sdk import BaseGripper, GripperMoveResult, Robotiq2F140Gripper
|
|
29
|
+
|
|
30
|
+
gripper = Robotiq2F140Gripper(host="<machineMotion-ip>")
|
|
31
|
+
result: GripperMoveResult = gripper.move(position=0.5)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## What's NOT shipped on PyPI
|
|
35
|
+
|
|
36
|
+
The demo runners under `griip_sdk.prototyping.run_*` import `mmai_griip_api` (the gRPC service). They are not part of the public API and will fail to import unless `mmai-griip-api` is installed alongside the SDK (e.g. via the Debian package on a MachineMotion).
|
|
37
|
+
|
|
38
|
+
## Releases
|
|
39
|
+
|
|
40
|
+
Versioning is hand-bumped semver. Bump `version` in `pyproject.toml` before triggering the PyPI publish workflow (`publish-griip-sdk-pypi.yml`).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "griip-sdk"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "GRIIP SDK: hardware control and orchestration for bin picking"
|
|
5
|
+
authors = ["VentionCo"]
|
|
6
|
+
license = "Proprietary"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
keywords = ["Vention", "GRIIP", "SDK", "Robot", "Gripper", "Automation"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Operating System :: POSIX :: Linux",
|
|
11
|
+
"Programming Language :: Python :: 3.10",
|
|
12
|
+
]
|
|
13
|
+
packages = [{ include = "griip_sdk", from = "src" }]
|
|
14
|
+
|
|
15
|
+
[tool.poetry.dependencies]
|
|
16
|
+
python = ">=3.10,<3.11"
|
|
17
|
+
grpcio = ">=1.78.0"
|
|
18
|
+
numpy = ">=1.24"
|
|
19
|
+
protobuf = ">=4.0"
|
|
20
|
+
toppra = ">=0.6.0"
|
|
21
|
+
pydantic = "^2.12.5"
|
|
22
|
+
omegaconf = "^2.3.0"
|
|
23
|
+
pymodbus = "^3.12.1"
|
|
24
|
+
urchin = "^0.0.30"
|
|
25
|
+
trimesh = "^4.11"
|
|
26
|
+
defusedxml = "^0.7.1"
|
|
27
|
+
spatialmath-python = "^1.1.15"
|
|
28
|
+
httpx = "^0.28.1"
|
|
29
|
+
paho-mqtt = "^2.1.0"
|
|
30
|
+
opencv-python-headless = "^4.9.0"
|
|
31
|
+
scipy = ">=1.11.0,<2.0"
|
|
32
|
+
PyYAML = "^6.0"
|
|
33
|
+
vention-firmware-grpc-client = ">=1.0.0"
|
|
34
|
+
|
|
35
|
+
[tool.poetry.group.dev.dependencies]
|
|
36
|
+
pytest = "^8.3.4"
|
|
37
|
+
pytest-cov = "6.3.0"
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
line-length = 140
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
testpaths = ["src"]
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["poetry-core>=2.0.0"]
|
|
47
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
from typing import List, Union, Optional
|
|
2
|
+
from griip_sdk.calibration.units import AngleUnit, DistanceUnit, Meters, MetersQuaternions
|
|
3
|
+
from scipy.spatial.transform import Rotation as ScipyRotation # type: ignore
|
|
4
|
+
import numpy as np
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from spatialmath import SE3, SO3, UnitQuaternion
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
NumpyNumber = Union[np.float64, np.float32, np.int64, np.int32]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# for the vision api.
|
|
14
|
+
def to_float_array(items: List[Union[float, NumpyNumber]]) -> List[float]:
|
|
15
|
+
return list(map(lambda a: float(a), items))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Point:
|
|
20
|
+
# xyz point
|
|
21
|
+
x: Meters
|
|
22
|
+
y: Meters
|
|
23
|
+
z: Meters
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def from_array(xyz: Union[List[Meters], np.ndarray]) -> "Point":
|
|
27
|
+
return Point(xyz[0], xyz[1], xyz[2])
|
|
28
|
+
|
|
29
|
+
def to_array(self) -> List[Meters]:
|
|
30
|
+
return [self.x, self.y, self.z]
|
|
31
|
+
|
|
32
|
+
def to_vector(self) -> np.ndarray:
|
|
33
|
+
return np.array(self.to_array())
|
|
34
|
+
|
|
35
|
+
# returns a new point, with value x + add_shift.x, y + add_shift.y, ...
|
|
36
|
+
# does not modify existing.
|
|
37
|
+
def translate(self, add_shift: "Point") -> "Point":
|
|
38
|
+
a = self.to_vector()
|
|
39
|
+
b = add_shift.to_vector()
|
|
40
|
+
return Point.from_array(a + b)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CartesianPose:
|
|
44
|
+
pose: SE3
|
|
45
|
+
position: Point
|
|
46
|
+
rotation: SO3
|
|
47
|
+
|
|
48
|
+
CAMERA_ANGLE_UNIT = AngleUnit.Radians
|
|
49
|
+
CAMERA_DISTANCE_UNIT = DistanceUnit.Meters
|
|
50
|
+
|
|
51
|
+
def __init__(self, position: Point, rotation: SO3):
|
|
52
|
+
if isinstance(rotation, ScipyRotation):
|
|
53
|
+
rotation = SO3(rotation.as_matrix())
|
|
54
|
+
self.pose = SE3.Rt(R=rotation, t=position.to_array())
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def position(self) -> Point:
|
|
58
|
+
return Point.from_array(self.pose.t)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def rotation(self) -> SO3:
|
|
62
|
+
return self.pose.R
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def from_SE3(pose: SE3) -> "CartesianPose":
|
|
66
|
+
return CartesianPose(Point.from_array(pose.t), pose.R)
|
|
67
|
+
|
|
68
|
+
def to_SE3(self) -> SE3:
|
|
69
|
+
return SE3(self.to_homogeneous_matrix())
|
|
70
|
+
|
|
71
|
+
def __str__(self):
|
|
72
|
+
return f"CartesianPose({self.pose.strline('rpy/yxz')})"
|
|
73
|
+
|
|
74
|
+
def __repr__(self):
|
|
75
|
+
return str(self)
|
|
76
|
+
|
|
77
|
+
def invert(self) -> SE3:
|
|
78
|
+
return self.pose.inv()
|
|
79
|
+
|
|
80
|
+
def transform_to(self, from_frame: Optional[SE3], to_frame: Optional[SE3]) -> SE3:
|
|
81
|
+
# get homo. matrices.
|
|
82
|
+
bTc = self.pose
|
|
83
|
+
aTb = SE3() # defaults
|
|
84
|
+
cTd = SE3() # defaults.
|
|
85
|
+
|
|
86
|
+
if from_frame is not None:
|
|
87
|
+
aTb = from_frame
|
|
88
|
+
if to_frame is not None:
|
|
89
|
+
cTd = to_frame.inv()
|
|
90
|
+
|
|
91
|
+
# calculate the transformation matrix from the local frame to the target frame
|
|
92
|
+
aTd = aTb @ bTc @ cTd
|
|
93
|
+
|
|
94
|
+
# Convert the resulting transformation matrix to a CartesianPose
|
|
95
|
+
return aTd
|
|
96
|
+
|
|
97
|
+
def to_canonical_frame(self, from_frame: SE3) -> SE3:
|
|
98
|
+
# Transform the position
|
|
99
|
+
return self.transform_to(from_frame, None)
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def from_moveit(position_and_quat: MetersQuaternions) -> SE3:
|
|
103
|
+
# position_and_quat is [x, y, z, qx, qy, qz, qw]
|
|
104
|
+
pos = position_and_quat[:3]
|
|
105
|
+
quat = UnitQuaternion(s=position_and_quat[-1], v=position_and_quat[3:6])
|
|
106
|
+
return SE3.Rt(R=quat.SO3(), t=pos)
|
|
107
|
+
|
|
108
|
+
def to_moveit(self) -> MetersQuaternions:
|
|
109
|
+
pos = self.pose.t
|
|
110
|
+
quat = UnitQuaternion(self.pose.R).vec_xyzs
|
|
111
|
+
return to_float_array(pos + list(quat))
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def from_camera_units(pose_vector: List[Union[Meters, float]]) -> SE3:
|
|
115
|
+
"""
|
|
116
|
+
Take a 6 dimensional pose vector in camera units and return a SE3 object.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
pose_vector: A list of 6 elements representing the pose in camera units.
|
|
120
|
+
The first three elements are the position (x, y, z) in meters.
|
|
121
|
+
The last three elements are the rotation vector (rx, ry, rz) in radians or degrees
|
|
122
|
+
depending on the CAMERA_ANGLE_UNIT setting.
|
|
123
|
+
Returns:
|
|
124
|
+
pose: A spatialmath SE3 object representing the pose.
|
|
125
|
+
"""
|
|
126
|
+
pos = DistanceUnit.Convert(
|
|
127
|
+
pose_vector[:3],
|
|
128
|
+
from_unit=CartesianPose.CAMERA_DISTANCE_UNIT,
|
|
129
|
+
to_unit=DistanceUnit.Meters,
|
|
130
|
+
)
|
|
131
|
+
units = "deg" if CartesianPose.CAMERA_ANGLE_UNIT == AngleUnit.Degrees else "rad"
|
|
132
|
+
|
|
133
|
+
angle = np.linalg.norm(pose_vector[3:])
|
|
134
|
+
axis = pose_vector[3:] / angle if angle != 0 else np.array([1.0, 0.0, 0.0])
|
|
135
|
+
|
|
136
|
+
return SE3.Rt(R=SO3.AngleAxis(angle, axis, unit=units), t=pos)
|
|
137
|
+
|
|
138
|
+
def to_camera_units(self) -> List[Union[Meters, float]]:
|
|
139
|
+
"""
|
|
140
|
+
Convert the CartesianPose to a 6 dimensional pose vector in camera units.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
pose_vector: A list of 6 floats representing the pose in camera units.
|
|
144
|
+
The first three elements are the position (x, y, z) in meters.
|
|
145
|
+
The last three elements are the rotation vector (rx, ry, rz) in radians or degrees
|
|
146
|
+
depending on the CAMERA_ANGLE_UNIT setting.
|
|
147
|
+
"""
|
|
148
|
+
rot_vec, pos_vec = self.pose.rtvec()
|
|
149
|
+
if CartesianPose.CAMERA_ANGLE_UNIT == AngleUnit.Degrees:
|
|
150
|
+
rot_vec = np.degrees(rot_vec)
|
|
151
|
+
|
|
152
|
+
return to_float_array(list(pos_vec) + list(rot_vec))
|
|
153
|
+
|
|
154
|
+
@staticmethod
|
|
155
|
+
def from_homogeneous_matrix(homogeneous_matrix: np.ndarray) -> SE3:
|
|
156
|
+
return SE3(homogeneous_matrix)
|
|
157
|
+
|
|
158
|
+
def to_homogeneous_matrix(self) -> np.ndarray:
|
|
159
|
+
return self.pose.A
|
|
160
|
+
|
|
161
|
+
def diff(self, other: SE3):
|
|
162
|
+
"""
|
|
163
|
+
Compute the rotational and translational difference between this pose and another pose.
|
|
164
|
+
Args:
|
|
165
|
+
other: Another CartesianPose to compare against.
|
|
166
|
+
Returns:
|
|
167
|
+
rotation_error_rad: The rotational difference in radians [0, pi].
|
|
168
|
+
|
|
169
|
+
translation_error: The translational difference in meters.
|
|
170
|
+
"""
|
|
171
|
+
# Angular distance [0, pi] between two rotations.
|
|
172
|
+
rotation_error_rad = self.pose.angdist(other)
|
|
173
|
+
translation_error = np.linalg.norm(self.pose.t - other.t)
|
|
174
|
+
return rotation_error_rad, translation_error
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
import math
|
|
179
|
+
|
|
180
|
+
def assert_float_array_eq(a1: List[float], a2: List[float]) -> None:
|
|
181
|
+
assert len(a1) == len(a1)
|
|
182
|
+
for i in range(len(a1)):
|
|
183
|
+
a, b = a1[i], a2[i]
|
|
184
|
+
assert math.isclose(a, b, rel_tol=1e-8)
|
|
185
|
+
|
|
186
|
+
def test_moveit_pose_conversions(move_it_pose: MetersQuaternions) -> None:
|
|
187
|
+
p = CartesianPose.from_moveit(move_it_pose)
|
|
188
|
+
camera_units_pose = p.to_camera_units()
|
|
189
|
+
p2 = CartesianPose.from_camera_units(camera_units_pose)
|
|
190
|
+
assert_float_array_eq(move_it_pose, p2.to_moveit())
|
|
191
|
+
|
|
192
|
+
def test_camera_pose_conversions(camera_units_pose: List[float]) -> None:
|
|
193
|
+
p = CartesianPose.from_camera_units(camera_units_pose)
|
|
194
|
+
move_it_pose = p.to_moveit()
|
|
195
|
+
p2 = CartesianPose.from_moveit(move_it_pose)
|
|
196
|
+
assert_float_array_eq(camera_units_pose, p2.to_camera_units())
|
|
197
|
+
|
|
198
|
+
test_camera_pose_conversions(
|
|
199
|
+
camera_units_pose=[
|
|
200
|
+
-0.36732380688872235,
|
|
201
|
+
0.31711499960934564,
|
|
202
|
+
-0.10696901055083409,
|
|
203
|
+
2.711732123419181,
|
|
204
|
+
1.505353005792903,
|
|
205
|
+
-0.07767478350482122,
|
|
206
|
+
]
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
test_moveit_pose_conversions(
|
|
210
|
+
move_it_pose=[
|
|
211
|
+
-0.366833055335579,
|
|
212
|
+
0.32453574184240414,
|
|
213
|
+
-0.10696901055083409,
|
|
214
|
+
0.8738757958661387,
|
|
215
|
+
0.48511117474910254,
|
|
216
|
+
-0.025031275275235965,
|
|
217
|
+
0.01953655012953711,
|
|
218
|
+
]
|
|
219
|
+
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""File-based configuration store for calibration data.
|
|
2
|
+
|
|
3
|
+
Reads versioned JSON files from /data/vention/calib_data/ (or a custom root).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
import time
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
DEFAULT_CALIBRATION_DATA_PATH = "/data/vention/calib_data"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _utc_ts() -> str:
|
|
24
|
+
return time.strftime("%Y-%m-%dT%H-%M-%SZ", time.gmtime())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _digest_bytes(data: bytes) -> str:
|
|
28
|
+
return hashlib.sha256(data).hexdigest()[:16]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _atomic_write(path: Path, data: bytes) -> None:
|
|
32
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
with tempfile.NamedTemporaryFile(dir=path.parent, delete=False) as tmp:
|
|
34
|
+
tmp.write(data)
|
|
35
|
+
tmp.flush()
|
|
36
|
+
tmp_name = tmp.name
|
|
37
|
+
Path(tmp_name).replace(path)
|
|
38
|
+
os.chmod(path, 0o664)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class VersionInfo:
|
|
43
|
+
name: str
|
|
44
|
+
version: str
|
|
45
|
+
path: Path
|
|
46
|
+
size: int
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CalibrationConfigStore:
|
|
50
|
+
"""Reads versioned JSON calibration files from a directory tree.
|
|
51
|
+
|
|
52
|
+
File layout:
|
|
53
|
+
root/
|
|
54
|
+
X_cam_flg__luxonis__18443010D1A8F71200__left/
|
|
55
|
+
2025-09-11T17-03-41Z__abcd1234.json
|
|
56
|
+
calibio__luxonis__18443010D1A8F71200/
|
|
57
|
+
2025-09-11T17-03-41Z__deadbeef.json
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, root: Path | str = DEFAULT_CALIBRATION_DATA_PATH) -> None:
|
|
61
|
+
self.root = Path(root)
|
|
62
|
+
|
|
63
|
+
def format_filename(self, version: str, digest: str) -> str:
|
|
64
|
+
return f"{version}__{digest}.json"
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def format_config_name(*parts: str) -> str:
|
|
68
|
+
return "__".join(parts)
|
|
69
|
+
|
|
70
|
+
def add_new_version(self, name: str, data: Any) -> VersionInfo:
|
|
71
|
+
version = _utc_ts()
|
|
72
|
+
folder = self.root / name
|
|
73
|
+
payload = (json.dumps(data, indent=2, sort_keys=True) + "\n").encode("utf-8")
|
|
74
|
+
digest = _digest_bytes(payload)
|
|
75
|
+
filename = self.format_filename(version, digest)
|
|
76
|
+
path = folder / filename
|
|
77
|
+
if not path.exists():
|
|
78
|
+
_atomic_write(path, payload)
|
|
79
|
+
return VersionInfo(name=name, version=filename, path=path, size=path.stat().st_size)
|
|
80
|
+
|
|
81
|
+
def update_version(self, version_info: VersionInfo, data: Any) -> VersionInfo:
|
|
82
|
+
payload = (json.dumps(data, indent=2, sort_keys=True) + "\n").encode("utf-8")
|
|
83
|
+
_atomic_write(version_info.path, payload)
|
|
84
|
+
return VersionInfo(
|
|
85
|
+
name=version_info.name,
|
|
86
|
+
version=version_info.version,
|
|
87
|
+
path=version_info.path,
|
|
88
|
+
size=version_info.path.stat().st_size,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def load_latest(self, name: str) -> Any:
|
|
92
|
+
info = self._latest_info(name)
|
|
93
|
+
logger.info("Calibration store: loading latest '%s' from %s", name, info.path)
|
|
94
|
+
return self._load_path(info.path)
|
|
95
|
+
|
|
96
|
+
def list_versions(self, name: str) -> list[VersionInfo]:
|
|
97
|
+
folder = self.root / name
|
|
98
|
+
if not folder.exists():
|
|
99
|
+
return []
|
|
100
|
+
items = []
|
|
101
|
+
for json_path in sorted(folder.glob("*.json"), key=lambda path: path.name, reverse=True):
|
|
102
|
+
if json_path.is_file():
|
|
103
|
+
items.append(VersionInfo(name=name, version=json_path.name, path=json_path, size=json_path.stat().st_size))
|
|
104
|
+
return items
|
|
105
|
+
|
|
106
|
+
def _latest_info(self, name: str) -> VersionInfo:
|
|
107
|
+
versions = self.list_versions(name)
|
|
108
|
+
if not versions:
|
|
109
|
+
raise FileNotFoundError(f"No calibration versions found for '{name}' in {self.root / name}")
|
|
110
|
+
return versions[0]
|
|
111
|
+
|
|
112
|
+
def _load_path(self, path: Path) -> Any:
|
|
113
|
+
with path.open("r", encoding="utf-8") as file_handle:
|
|
114
|
+
return json.load(file_handle)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Hand-Eye Calibration Module
|
|
2
|
+
|
|
3
|
+
This module performs hand-eye calibration for robot-camera systems.
|
|
4
|
+
|
|
5
|
+
## HTTP API Usage
|
|
6
|
+
|
|
7
|
+
The calibration is exposed via an HTTP API.
|
|
8
|
+
Configuration is loaded from `configs/robot_cells/<robot_cell>.yaml` which contains
|
|
9
|
+
all robot, planner, and service URLs.
|
|
10
|
+
|
|
11
|
+
### Calibrate
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
POST /calibration/hand-eye/calibrate
|
|
15
|
+
Content-Type: application/json
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
"robot_cell": "vp15_mcalpine",
|
|
19
|
+
"calibration_routine": "mcalpine_cell_routine.yaml",
|
|
20
|
+
"cam_name": "luxonis",
|
|
21
|
+
"sensor": "left"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Evaluate
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
POST /calibration/hand-eye/evaluate
|
|
29
|
+
Content-Type: application/json
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
"robot_cell": "vp15_mcalpine",
|
|
33
|
+
"calibration_routine": "mcalpine_cell_routine.yaml"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Status
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
GET /calibration/hand-eye/status
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Teach Mode (Set Home Position)
|
|
44
|
+
|
|
45
|
+
Use teach mode to manually position the robot and save a new home position:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 1. Start teach mode (enables freedrive)
|
|
49
|
+
POST /calibration/hand-eye/teach/start
|
|
50
|
+
Content-Type: application/json
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
"robot_cell": "vp15_mcalpine",
|
|
54
|
+
"calibration_routine": "mcalpine_cell_routine.yaml"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# 2. Manually move the robot to desired home position...
|
|
58
|
+
|
|
59
|
+
# 3. Check current position (optional)
|
|
60
|
+
GET /calibration/hand-eye/teach/status
|
|
61
|
+
|
|
62
|
+
# 4. Save the position and exit freedrive
|
|
63
|
+
POST /calibration/hand-eye/teach/save
|
|
64
|
+
|
|
65
|
+
# Or cancel without saving
|
|
66
|
+
POST /calibration/hand-eye/teach/cancel
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
The `robot_cell` parameter should match a config file in `configs/robot_cells/`:
|
|
72
|
+
- `vp15_mcalpine` loads `configs/robot_cells/vp15_mcalpine.yaml`
|
|
73
|
+
- `a10v_mcalpine` loads `configs/robot_cells/a10v_mcalpine.yaml`
|
|
74
|
+
|
|
75
|
+
These configs include:
|
|
76
|
+
- Robot settings (server_url, robot_ip, robot_model, cuboids)
|
|
77
|
+
- Service URLs (curobo planner, vision server)
|
|
78
|
+
- Motion planning settings (use_vamp)
|
|
79
|
+
- Payload and gripper configuration
|
|
80
|
+
|
|
81
|
+
## Requirements
|
|
82
|
+
|
|
83
|
+
- Robot must be connected and VRMCS driver running
|
|
84
|
+
- Motion planner server running (URL from config)
|
|
85
|
+
- MMAI Vision camera server running (URL from config)
|
|
86
|
+
- Charuco calibration board visible to camera
|
|
87
|
+
|
|
88
|
+
## Python API Usage
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from griip_sdk.calibration.hand_eye_calib import HandEyeCalibrationService
|
|
92
|
+
from griip_sdk.grpc_adapters.servicer_as_stub import ServicerAsStub
|
|
93
|
+
|
|
94
|
+
calibrator = HandEyeCalibrationService(
|
|
95
|
+
robo=robot_client,
|
|
96
|
+
cam=camera_client,
|
|
97
|
+
store=config_store,
|
|
98
|
+
planning_stub=ServicerAsStub(planning_service),
|
|
99
|
+
config=calibration_routine_dict,
|
|
100
|
+
cam_intr=camera_intrinsics,
|
|
101
|
+
dist_coeffs=distortion_coefficients,
|
|
102
|
+
im_width=image_width,
|
|
103
|
+
im_height=image_height,
|
|
104
|
+
sensor_name="left",
|
|
105
|
+
save_dir="/data/calibration",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
X_cam_flg = calibrator.calibrate()
|
|
109
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from griip_sdk.calibration.hand_eye_calib.core import (
|
|
2
|
+
Routine,
|
|
3
|
+
WaypointGenerator,
|
|
4
|
+
Waypoint,
|
|
5
|
+
project_points,
|
|
6
|
+
draw_points,
|
|
7
|
+
points_in_image,
|
|
8
|
+
invert_pose,
|
|
9
|
+
generate_camera_pose_on_sphere,
|
|
10
|
+
)
|
|
11
|
+
from griip_sdk.calibration.hand_eye_calib.routines import (
|
|
12
|
+
BootstrapRoutine,
|
|
13
|
+
SphereRoutine,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from griip_sdk.calibration.hand_eye_calib.main import (
|
|
17
|
+
HandEyeCalibrationService,
|
|
18
|
+
camera_vector_to_se3,
|
|
19
|
+
se3_to_camera_vector,
|
|
20
|
+
intrinsic_dict_to_arrays,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"HandEyeCalibrationService",
|
|
25
|
+
"Routine",
|
|
26
|
+
"WaypointGenerator",
|
|
27
|
+
"Waypoint",
|
|
28
|
+
"BootstrapRoutine",
|
|
29
|
+
"SphereRoutine",
|
|
30
|
+
"project_points",
|
|
31
|
+
"draw_points",
|
|
32
|
+
"points_in_image",
|
|
33
|
+
"invert_pose",
|
|
34
|
+
"generate_camera_pose_on_sphere",
|
|
35
|
+
"camera_vector_to_se3",
|
|
36
|
+
"se3_to_camera_vector",
|
|
37
|
+
"intrinsic_dict_to_arrays",
|
|
38
|
+
]
|