yubi-mujoco 0.1.0__py3-none-any.whl
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.
- yubi_mujoco/__init__.py +13 -0
- yubi_mujoco/__main__.py +3 -0
- yubi_mujoco/_images.py +31 -0
- yubi_mujoco/adapter.py +107 -0
- yubi_mujoco/assets/NOTICE.md +34 -0
- yubi_mujoco/assets/SOURCE.md +42 -0
- yubi_mujoco/assets/cad_assembly.json +167 -0
- yubi_mujoco/assets/cad_manifest.json +801 -0
- yubi_mujoco/assets/colors.json +14 -0
- yubi_mujoco/assets/licenses/Apache-2.0.txt +202 -0
- yubi_mujoco/assets/licenses/CERN-OHL-W-2.0.txt +289 -0
- yubi_mujoco/assets/licenses/MIT.txt +26 -0
- yubi_mujoco/assets/mass_properties.json +108 -0
- yubi_mujoco/assets/meshes/cad_left_jaw_attachment.stl +0 -0
- yubi_mujoco/assets/meshes/cad_left_jaw_flap.stl +0 -0
- yubi_mujoco/assets/meshes/cad_left_jaw_hardware.stl +0 -0
- yubi_mujoco/assets/meshes/cad_left_jaw_pad.stl +0 -0
- yubi_mujoco/assets/meshes/cad_left_jaw_rubber.stl +0 -0
- yubi_mujoco/assets/meshes/cad_palm_camera.stl +0 -0
- yubi_mujoco/assets/meshes/cad_palm_servo.stl +0 -0
- yubi_mujoco/assets/meshes/cad_palm_structure.stl +0 -0
- yubi_mujoco/assets/meshes/cad_right_jaw_attachment.stl +0 -0
- yubi_mujoco/assets/meshes/cad_right_jaw_flap.stl +0 -0
- yubi_mujoco/assets/meshes/cad_right_jaw_hardware.stl +0 -0
- yubi_mujoco/assets/meshes/cad_right_jaw_pad.stl +0 -0
- yubi_mujoco/assets/meshes/cad_right_jaw_rubber.stl +0 -0
- yubi_mujoco/cli.py +268 -0
- yubi_mujoco/env.py +360 -0
- yubi_mujoco/geometry.py +57 -0
- yubi_mujoco/model.py +310 -0
- yubi_mujoco/scripted.py +77 -0
- yubi_mujoco-0.1.0.dist-info/METADATA +169 -0
- yubi_mujoco-0.1.0.dist-info/RECORD +41 -0
- yubi_mujoco-0.1.0.dist-info/WHEEL +5 -0
- yubi_mujoco-0.1.0.dist-info/entry_points.txt +2 -0
- yubi_mujoco-0.1.0.dist-info/licenses/LICENSE +26 -0
- yubi_mujoco-0.1.0.dist-info/licenses/src/yubi_mujoco/assets/NOTICE.md +34 -0
- yubi_mujoco-0.1.0.dist-info/licenses/src/yubi_mujoco/assets/licenses/Apache-2.0.txt +202 -0
- yubi_mujoco-0.1.0.dist-info/licenses/src/yubi_mujoco/assets/licenses/CERN-OHL-W-2.0.txt +289 -0
- yubi_mujoco-0.1.0.dist-info/licenses/src/yubi_mujoco/assets/licenses/MIT.txt +26 -0
- yubi_mujoco-0.1.0.dist-info/top_level.txt +1 -0
yubi_mujoco/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Arm-free YUBI simulation. Set MUJOCO_GL before first environment import."""
|
|
2
|
+
|
|
3
|
+
__all__ = ["YubiEnv", "SimConfig"]
|
|
4
|
+
__version__ = "0.1.0"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def __getattr__(name):
|
|
8
|
+
# Keep CLI import lightweight so headless GL selection precedes MuJoCo import.
|
|
9
|
+
if name in __all__:
|
|
10
|
+
from .env import YubiEnv, SimConfig
|
|
11
|
+
|
|
12
|
+
return {"YubiEnv": YubiEnv, "SimConfig": SimConfig}[name]
|
|
13
|
+
raise AttributeError(name)
|
yubi_mujoco/__main__.py
ADDED
yubi_mujoco/_images.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Write RGB PNG images without optional video dependencies."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import struct
|
|
5
|
+
import zlib
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def write_png(path, pixels):
|
|
11
|
+
"""Write an HWC uint8 RGB image as a lossless PNG."""
|
|
12
|
+
pixels = np.asarray(pixels)
|
|
13
|
+
if pixels.ndim != 3 or pixels.shape[2] != 3 or pixels.dtype != np.uint8:
|
|
14
|
+
raise ValueError("pixels must have shape (height, width, 3) and dtype uint8")
|
|
15
|
+
height, width, _ = pixels.shape
|
|
16
|
+
if not height or not width:
|
|
17
|
+
raise ValueError("image dimensions must be positive")
|
|
18
|
+
|
|
19
|
+
def chunk(kind, data):
|
|
20
|
+
return (
|
|
21
|
+
struct.pack(">I", len(data)) + kind + data + struct.pack(">I", zlib.crc32(kind + data))
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
header = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
|
|
25
|
+
rows = b"".join(b"\0" + row.tobytes() for row in pixels)
|
|
26
|
+
Path(path).write_bytes(
|
|
27
|
+
b"\x89PNG\r\n\x1a\n"
|
|
28
|
+
+ chunk(b"IHDR", header)
|
|
29
|
+
+ chunk(b"IDAT", zlib.compress(rows))
|
|
30
|
+
+ chunk(b"IEND", b"")
|
|
31
|
+
)
|
yubi_mujoco/adapter.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Local Policy(checkpoint_dir).infer(obs) evaluation; no model/network side effects."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import importlib.util
|
|
5
|
+
import time
|
|
6
|
+
import numpy as np
|
|
7
|
+
from .geometry import pose
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def validate_chunk(actions, min_rows=16, *, executed_rows=None):
|
|
11
|
+
if not isinstance(min_rows, int) or min_rows < 1:
|
|
12
|
+
raise ValueError("min_rows must be a positive integer")
|
|
13
|
+
a = np.asarray(actions)
|
|
14
|
+
if a.ndim != 2 or a.shape[1] != 16 or a.shape[0] < min_rows:
|
|
15
|
+
raise ValueError(f"policy must return (N,16) with N>={min_rows}; received {a.shape}")
|
|
16
|
+
if not np.issubdtype(a.dtype, np.number) or np.iscomplexobj(a) or not np.isfinite(a).all():
|
|
17
|
+
raise ValueError("policy actions must be finite numeric values")
|
|
18
|
+
if executed_rows is None:
|
|
19
|
+
executed_rows = len(a)
|
|
20
|
+
if not isinstance(executed_rows, int) or not 1 <= executed_rows <= len(a):
|
|
21
|
+
raise ValueError("executed_rows must be an integer within the returned chunk")
|
|
22
|
+
for row in a[:executed_rows]:
|
|
23
|
+
pose(row[:7])
|
|
24
|
+
pose(row[7:14])
|
|
25
|
+
return a.astype(np.float64)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class HoldPolicy:
|
|
29
|
+
def __init__(self, checkpoint_dir=None):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def infer(self, obs):
|
|
33
|
+
a = np.zeros((32, 16), dtype=np.float32)
|
|
34
|
+
a[:, 6] = a[:, 13] = 1
|
|
35
|
+
a[:, 14:16] = obs["observation.joint_states"]
|
|
36
|
+
return a
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_policy(policy_path, checkpoint_dir):
|
|
40
|
+
path = Path(policy_path).resolve()
|
|
41
|
+
if not path.is_file():
|
|
42
|
+
raise FileNotFoundError(path)
|
|
43
|
+
spec = importlib.util.spec_from_file_location("yubi_user_policy", path)
|
|
44
|
+
if spec is None or spec.loader is None:
|
|
45
|
+
raise ValueError(f"cannot import {path}")
|
|
46
|
+
module = importlib.util.module_from_spec(spec)
|
|
47
|
+
spec.loader.exec_module(module)
|
|
48
|
+
return module.Policy(str(Path(checkpoint_dir).resolve()))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def run_policy(
|
|
52
|
+
env, policy, *, adopt_rows=16, max_calls=None, on_step=None, translation_frame="body"
|
|
53
|
+
):
|
|
54
|
+
"""Measure simulation task success, not Arena real-robot scores.
|
|
55
|
+
|
|
56
|
+
Simulator pauses while infer runs, as described by the public robot contract.
|
|
57
|
+
Translation frame and row frequency are explicit caller choices.
|
|
58
|
+
No dataset-only gripper time shift is applied to live actions.
|
|
59
|
+
"""
|
|
60
|
+
if not isinstance(adopt_rows, int) or adopt_rows <= 0:
|
|
61
|
+
raise ValueError("adopt_rows must be a positive integer")
|
|
62
|
+
if max_calls is not None and (not isinstance(max_calls, int) or max_calls <= 0):
|
|
63
|
+
raise ValueError("max_calls must be a positive integer")
|
|
64
|
+
latencies = []
|
|
65
|
+
calls = 0
|
|
66
|
+
rate_limited_steps = 0
|
|
67
|
+
status = "running"
|
|
68
|
+
error = None
|
|
69
|
+
while not env._done and (max_calls is None or calls < max_calls):
|
|
70
|
+
obs = env.observe(images=True)
|
|
71
|
+
start = time.perf_counter()
|
|
72
|
+
try:
|
|
73
|
+
actions = validate_chunk(policy.infer(obs), executed_rows=adopt_rows)
|
|
74
|
+
latencies.append(time.perf_counter() - start)
|
|
75
|
+
# Fail before partially executing a chunk with illegal gripper positions.
|
|
76
|
+
for row in actions[:adopt_rows]:
|
|
77
|
+
env._motor_to_jaw(row[14:16])
|
|
78
|
+
calls += 1
|
|
79
|
+
for action in actions[:adopt_rows]:
|
|
80
|
+
_, _, terminated, truncated, info = env.step_delta(
|
|
81
|
+
action, translation_frame=translation_frame
|
|
82
|
+
)
|
|
83
|
+
rate_limited_steps += int(any(info["target_rate_limited"]))
|
|
84
|
+
if on_step:
|
|
85
|
+
on_step(env, info)
|
|
86
|
+
if terminated or truncated:
|
|
87
|
+
break
|
|
88
|
+
except (ValueError, RuntimeError, FloatingPointError) as exc:
|
|
89
|
+
status, error = "invalid_policy_output", f"{type(exc).__name__}: {exc}"
|
|
90
|
+
break
|
|
91
|
+
if status == "running":
|
|
92
|
+
status = "success" if env.info()["success"] else "horizon" if env._done else "call_limit"
|
|
93
|
+
return {
|
|
94
|
+
**env.info(),
|
|
95
|
+
"status": status,
|
|
96
|
+
"error": error,
|
|
97
|
+
"policy_calls": calls,
|
|
98
|
+
"latency_seconds": latencies,
|
|
99
|
+
"latency_p95_seconds": float(np.percentile(latencies, 95)) if latencies else None,
|
|
100
|
+
"rate_limited_steps": rate_limited_steps,
|
|
101
|
+
"action_hz": env.config.control_hz,
|
|
102
|
+
"translation_frame": translation_frame,
|
|
103
|
+
"adopt_rows": adopt_rows,
|
|
104
|
+
"scoring": "local_sim_task_success_only",
|
|
105
|
+
"camera_calibrated": False,
|
|
106
|
+
"motor_mapping_calibrated": False,
|
|
107
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# YUBI MuJoCo notices
|
|
2
|
+
|
|
3
|
+
Original simulation software: Copyright (c) 2026 YUBI simulation contributors,
|
|
4
|
+
MIT license. The root LICENSE does not relicense third-party assets.
|
|
5
|
+
|
|
6
|
+
Toyota-derived CAD and transformation/grouping metadata:
|
|
7
|
+
Copyright 2026 Toyota Motor Corporation, CERN-OHL-W-2.0.
|
|
8
|
+
Full license: licenses/CERN-OHL-W-2.0.txt.
|
|
9
|
+
|
|
10
|
+
Modification notice, 2026-10-01: tessellated the pinned robot-gripper STEP
|
|
11
|
+
assembly; partitioned 97 solid leaves into fixed palm and moving jaws;
|
|
12
|
+
converted millimeters to meters and re-expressed geometry in a canonical
|
|
13
|
+
root frame and verified hinge-local frames. No intentional solid-geometry
|
|
14
|
+
design changes. Distributed meshes are 13 disjoint material partitions;
|
|
15
|
+
redundant whole-body aggregate meshes are omitted. Approximate black/red
|
|
16
|
+
rendering colors follow the official assembled gripper reference.
|
|
17
|
+
mass_properties.json holds body mass, center-of-mass and inertia estimates
|
|
18
|
+
computed from the same STEP solids with stated material assumptions.
|
|
19
|
+
The CAD-derived meshes and transformation/grouping metadata retain
|
|
20
|
+
CERN-OHL-W-2.0. See SOURCE.md for complete source and regeneration instructions.
|
|
21
|
+
|
|
22
|
+
Published software/API/coordinate conventions were consulted from:
|
|
23
|
+
- airoa-org/yubi-sw, commit 1eccb041efc4d8966b03e4ccf4b402aa79d78be4
|
|
24
|
+
Copyright 2026 AI Robot Association
|
|
25
|
+
- airoa-org/umi-arena-evaluation, commit 8763f10a022139dbcf4293b60f797a717d8bd1ad
|
|
26
|
+
These upstream references use Apache-2.0; a copy is in licenses/Apache-2.0.txt.
|
|
27
|
+
No pretrained weights or restricted datasets are redistributed.
|
|
28
|
+
|
|
29
|
+
OpenArm MuJoCo (https://github.com/enactic/openarm_mujoco) was consulted as a
|
|
30
|
+
packaging/usability reference; its code and robot assets are not included.
|
|
31
|
+
|
|
32
|
+
This is an independent project. It is not endorsed or certified by Toyota,
|
|
33
|
+
AIRoA, or Enactic. No trademark rights are granted. See the full licenses for
|
|
34
|
+
warranty disclaimers and other terms.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Complete source and asset provenance
|
|
2
|
+
|
|
3
|
+
For a published 0.1.0 release, obtain its complete corresponding source as
|
|
4
|
+
`yubi_mujoco-0.1.0.tar.gz`, the same-version source distribution (sdist), at:
|
|
5
|
+
https://pypi.org/project/yubi-mujoco/0.1.0/#files
|
|
6
|
+
|
|
7
|
+
The sdist contains the unmodified source STEP, original hardware license and
|
|
8
|
+
commercial guidance, the CAD exporter, and all grouping/transformation data.
|
|
9
|
+
It must be published alongside the wheel. A wheel alone is not the complete
|
|
10
|
+
CAD source. Repository source is also intended at:
|
|
11
|
+
https://github.com/k1000dai/yubi_mujoco
|
|
12
|
+
|
|
13
|
+
Original hardware source: https://github.com/Toyota/yubi-hw
|
|
14
|
+
Pinned revision: dd8bd13d2fd8e5003057243576f88be333d95fc5
|
|
15
|
+
Original path: STEP/gripper/YUBI Gripper Assy_Dynamixel_ver2.STEP
|
|
16
|
+
SHA-256: 0e60bf62de970e5fae5e7cff104f547a954ef8d5d7b9dde9835b381b95f4fcc8
|
|
17
|
+
Copyright 2026 Toyota Motor Corporation. License: CERN-OHL-W-2.0.
|
|
18
|
+
See NOTICE.md for the dated modification notice and licenses/ for full terms.
|
|
19
|
+
|
|
20
|
+
## Regenerate
|
|
21
|
+
|
|
22
|
+
Extract the sdist, change into its root, and use a Python interpreter with
|
|
23
|
+
FreeCAD 1.0 native Python modules (Import, Part, and MeshPart) available:
|
|
24
|
+
|
|
25
|
+
python cad/fetch_source.py
|
|
26
|
+
/usr/bin/python3 cad/export.py
|
|
27
|
+
|
|
28
|
+
The first command verifies the bundled STEP; it downloads only that pinned
|
|
29
|
+
public source if it is absent. The exporter itself uses no network. Set
|
|
30
|
+
FREECAD_LIB for an alternate FreeCAD Python library location. FreeCAD is not
|
|
31
|
+
a runtime dependency and is installed separately, not as a pip extra.
|
|
32
|
+
|
|
33
|
+
The exporter verifies the source hash, all 97 expected solid leaves, their
|
|
34
|
+
unique body assignments, 8 mm shaft bores, and parallel-finger orientation.
|
|
35
|
+
Default tessellation: 0.12 mm linear deflection, 0.35 rad angular deflection.
|
|
36
|
+
Version-specific FreeCAD/OpenCascade tessellation may change mesh hashes.
|
|
37
|
+
|
|
38
|
+
Runtime meshes and corresponding SHA-256 hashes are listed in
|
|
39
|
+
cad_manifest.json; exact frame/pivot transforms are in cad_assembly.json.
|
|
40
|
+
Body mass estimates in mass_properties.json are regenerated by
|
|
41
|
+
cad/mass_properties.py, which needs OpenCascade's Python bindings (cadquery-ocp).
|
|
42
|
+
Detailed geometry derivation is in docs/CAD_PROVENANCE.md in the sdist.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"source_step": "cad/source/YUBI Gripper Assy_Dynamixel_ver2.STEP",
|
|
4
|
+
"cad_unit": "millimeter",
|
|
5
|
+
"mesh_unit": "meter",
|
|
6
|
+
"cad_origin_mm": [
|
|
7
|
+
854.070661629179,
|
|
8
|
+
856.5046236934521,
|
|
9
|
+
1286.5734593075358
|
|
10
|
+
],
|
|
11
|
+
"cad_to_canonical_rotation": [
|
|
12
|
+
[
|
|
13
|
+
0,
|
|
14
|
+
0,
|
|
15
|
+
1
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
1,
|
|
19
|
+
0,
|
|
20
|
+
0
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
0,
|
|
24
|
+
1,
|
|
25
|
+
0
|
|
26
|
+
]
|
|
27
|
+
],
|
|
28
|
+
"cad_to_canonical_scale": 0.001,
|
|
29
|
+
"formula": "canonical_m = 0.001 * rotation @ (cad_mm - cad_origin_mm)",
|
|
30
|
+
"canonical_axes": {
|
|
31
|
+
"x": "+CAD Z, along parallel fingers",
|
|
32
|
+
"y": "+CAD X, toward source left jaw",
|
|
33
|
+
"z": "+CAD Y, hinge axis / camera side"
|
|
34
|
+
},
|
|
35
|
+
"bodies": {
|
|
36
|
+
"palm": {
|
|
37
|
+
"origin_m": [
|
|
38
|
+
0,
|
|
39
|
+
0,
|
|
40
|
+
0
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"left_jaw": {
|
|
44
|
+
"origin_m": [
|
|
45
|
+
-0.015999999999828787,
|
|
46
|
+
0.014999999999733518,
|
|
47
|
+
0.0
|
|
48
|
+
],
|
|
49
|
+
"joint_axis": [
|
|
50
|
+
0,
|
|
51
|
+
0,
|
|
52
|
+
1
|
|
53
|
+
],
|
|
54
|
+
"opening_angle_sign": 1,
|
|
55
|
+
"cad_neutral_joint_angle_rad": 0.0
|
|
56
|
+
},
|
|
57
|
+
"right_jaw": {
|
|
58
|
+
"origin_m": [
|
|
59
|
+
-0.01600000000017144,
|
|
60
|
+
-0.014999999999733518,
|
|
61
|
+
0.0
|
|
62
|
+
],
|
|
63
|
+
"joint_axis": [
|
|
64
|
+
0,
|
|
65
|
+
0,
|
|
66
|
+
1
|
|
67
|
+
],
|
|
68
|
+
"opening_angle_sign": -1,
|
|
69
|
+
"cad_neutral_joint_angle_rad": 0.0
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"cad_neutral": "Parallel attachments point +canonical X; proven by paired flap mounting holes. This is a geometric reference, not a measured motor encoder zero.",
|
|
73
|
+
"calibration": {
|
|
74
|
+
"motor_zero_verified": false,
|
|
75
|
+
"robot_joint_limits_verified": false,
|
|
76
|
+
"nominal_glove_opening_range_rad": [
|
|
77
|
+
0.0,
|
|
78
|
+
0.94
|
|
79
|
+
],
|
|
80
|
+
"nominal_range_warning": "Glove URDF range, not measured robot travel. CAD neutral already has a nonzero pad gap. Do not call q=0 fully closed."
|
|
81
|
+
},
|
|
82
|
+
"geometric_evidence": {
|
|
83
|
+
"right_jaw": {
|
|
84
|
+
"shaft_bore_radius_mm": 4.0,
|
|
85
|
+
"shaft_bore_center_mm": [
|
|
86
|
+
839.0706616294455,
|
|
87
|
+
862.5046236934472,
|
|
88
|
+
1270.5734593073644
|
|
89
|
+
],
|
|
90
|
+
"shaft_axis_cad": [
|
|
91
|
+
0,
|
|
92
|
+
1,
|
|
93
|
+
0
|
|
94
|
+
],
|
|
95
|
+
"flap_screw_centers_mm": [
|
|
96
|
+
[
|
|
97
|
+
834.0706616294581,
|
|
98
|
+
866.5046236934472,
|
|
99
|
+
1301.5734593073664
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
834.0706616294653,
|
|
103
|
+
866.5046236934472,
|
|
104
|
+
1319.5734593073664
|
|
105
|
+
]
|
|
106
|
+
],
|
|
107
|
+
"flap_screw_spacing_mm": 18.0,
|
|
108
|
+
"finger_direction_cad": [
|
|
109
|
+
4.0421986745463475e-13,
|
|
110
|
+
0.0,
|
|
111
|
+
1.0
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"left_jaw": {
|
|
115
|
+
"shaft_bore_radius_mm": 4.0,
|
|
116
|
+
"shaft_bore_center_mm": [
|
|
117
|
+
869.0706616289125,
|
|
118
|
+
862.504623693457,
|
|
119
|
+
1270.573459307707
|
|
120
|
+
],
|
|
121
|
+
"shaft_axis_cad": [
|
|
122
|
+
0,
|
|
123
|
+
1,
|
|
124
|
+
0
|
|
125
|
+
],
|
|
126
|
+
"flap_screw_centers_mm": [
|
|
127
|
+
[
|
|
128
|
+
874.0706616289125,
|
|
129
|
+
866.504623693457,
|
|
130
|
+
1301.573459307707
|
|
131
|
+
],
|
|
132
|
+
[
|
|
133
|
+
874.0706616289125,
|
|
134
|
+
866.504623693457,
|
|
135
|
+
1319.573459307707
|
|
136
|
+
]
|
|
137
|
+
],
|
|
138
|
+
"flap_screw_spacing_mm": 18.0,
|
|
139
|
+
"finger_direction_cad": [
|
|
140
|
+
0.0,
|
|
141
|
+
0.0,
|
|
142
|
+
1.0
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"optional_material_meshes": {
|
|
147
|
+
"palm": [
|
|
148
|
+
"cad_palm_camera.stl",
|
|
149
|
+
"cad_palm_servo.stl",
|
|
150
|
+
"cad_palm_structure.stl"
|
|
151
|
+
],
|
|
152
|
+
"left_jaw": [
|
|
153
|
+
"cad_left_jaw_attachment.stl",
|
|
154
|
+
"cad_left_jaw_flap.stl",
|
|
155
|
+
"cad_left_jaw_hardware.stl",
|
|
156
|
+
"cad_left_jaw_pad.stl",
|
|
157
|
+
"cad_left_jaw_rubber.stl"
|
|
158
|
+
],
|
|
159
|
+
"right_jaw": [
|
|
160
|
+
"cad_right_jaw_attachment.stl",
|
|
161
|
+
"cad_right_jaw_flap.stl",
|
|
162
|
+
"cad_right_jaw_hardware.stl",
|
|
163
|
+
"cad_right_jaw_pad.stl",
|
|
164
|
+
"cad_right_jaw_rubber.stl"
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
}
|