sscbirrt-assets 1.0.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.
Files changed (41) hide show
  1. sscbirrt_assets-1.0.0/.gitignore +1 -0
  2. sscbirrt_assets-1.0.0/LICENSE +21 -0
  3. sscbirrt_assets-1.0.0/PKG-INFO +42 -0
  4. sscbirrt_assets-1.0.0/README.md +25 -0
  5. sscbirrt_assets-1.0.0/fetch.py +112 -0
  6. sscbirrt_assets-1.0.0/hatch_build.py +23 -0
  7. sscbirrt_assets-1.0.0/manifest.json +37 -0
  8. sscbirrt_assets-1.0.0/pyproject.toml +45 -0
  9. sscbirrt_assets-1.0.0/src/sscbirrt_assets/__init__.py +44 -0
  10. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/2f85.xml +191 -0
  11. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/LICENSE +23 -0
  12. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/base.stl +0 -0
  13. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/base_mount.stl +0 -0
  14. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/coupler.stl +0 -0
  15. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/driver.stl +0 -0
  16. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/follower.stl +0 -0
  17. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/pad.stl +0 -0
  18. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/silicone_pad.stl +0 -0
  19. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/robotiq_2f85/assets/spring_link.stl +0 -0
  20. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/LICENSE +26 -0
  21. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/base_0.obj +15306 -0
  22. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/base_1.obj +17646 -0
  23. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/forearm_0.obj +45520 -0
  24. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/forearm_1.obj +2159 -0
  25. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/forearm_2.obj +25602 -0
  26. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/forearm_3.obj +29961 -0
  27. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/shoulder_0.obj +79709 -0
  28. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/shoulder_1.obj +15676 -0
  29. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/shoulder_2.obj +65873 -0
  30. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/upperarm_0.obj +4342 -0
  31. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/upperarm_1.obj +29711 -0
  32. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/upperarm_2.obj +99001 -0
  33. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/upperarm_3.obj +144582 -0
  34. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist1_0.obj +7761 -0
  35. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist1_1.obj +66213 -0
  36. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist1_2.obj +44960 -0
  37. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist2_0.obj +23375 -0
  38. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist2_1.obj +57886 -0
  39. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist2_2.obj +57883 -0
  40. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/assets/wrist3.obj +5893 -0
  41. sscbirrt_assets-1.0.0/src/sscbirrt_assets/menagerie/universal_robots_ur5e/ur5e.xml +136 -0
@@ -0,0 +1 @@
1
+ src/sscbirrt_assets/menagerie/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Siddhartha Srinivasa <siddhartha.srinivasa@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.5
2
+ Name: sscbirrt-assets
3
+ Version: 1.0.0
4
+ Summary: The MuJoCo Menagerie UR5e and Robotiq 2F-85 models, packaged for the sscbirrt demos
5
+ Project-URL: Homepage, https://github.com/personalrobotics/sscbirrt
6
+ Project-URL: Source, https://github.com/personalrobotics/sscbirrt/tree/main/assets
7
+ Project-URL: Upstream, https://github.com/google-deepmind/mujoco_menagerie
8
+ Author-email: Siddhartha Srinivasa <siddhartha.srinivasa@gmail.com>
9
+ License-Expression: MIT AND BSD-3-Clause AND BSD-2-Clause
10
+ License-File: LICENSE
11
+ Keywords: mujoco,robot-models,robotiq,sscbirrt,ur5e
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ # sscbirrt-assets
19
+
20
+ The [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie) UR5e and Robotiq 2F-85 models, packaged so that `pip install "sscbirrt[demo]"` can run the [sscbirrt](https://github.com/personalrobotics/sscbirrt) demos without cloning the Menagerie.
21
+
22
+ ```python
23
+ import sscbirrt_assets
24
+
25
+ sscbirrt_assets.ur5e_xml() # .../menagerie/universal_robots_ur5e/ur5e.xml
26
+ sscbirrt_assets.robotiq_2f85_xml() # .../menagerie/robotiq_2f85/2f85.xml
27
+ sscbirrt_assets.menagerie_path() # a directory laid out like a Menagerie clone
28
+ ```
29
+
30
+ The functions return real file paths, since MJCF loaders resolve meshes relative to the XML.
31
+
32
+ ## Provenance and licenses
33
+
34
+ Each file is copied unmodified from Menagerie at the commit in `sscbirrt_assets.MENAGERIE_COMMIT` and checked against the SHA-256 in `manifest.json`. Only the MJCF, the meshes and the LICENSE of each model ship.
35
+
36
+ - UR5e: BSD-3-Clause, Copyright 2018 ROS Industrial Consortium
37
+ - Robotiq 2F-85: BSD-2-Clause, Copyright (c) 2013, ROS-Industrial
38
+ - The Python code in this package: MIT
39
+
40
+ ## Building
41
+
42
+ `fetch.py` places the files. The build hook calls it, so `uv build assets` works on its own. Pass `python assets/fetch.py --from <clone>` to copy from a local Menagerie clone instead of downloading. Either way the hashes are checked. After moving the pin, regenerate the manifest with `--write-manifest --from <clone>`.
@@ -0,0 +1,25 @@
1
+ # sscbirrt-assets
2
+
3
+ The [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie) UR5e and Robotiq 2F-85 models, packaged so that `pip install "sscbirrt[demo]"` can run the [sscbirrt](https://github.com/personalrobotics/sscbirrt) demos without cloning the Menagerie.
4
+
5
+ ```python
6
+ import sscbirrt_assets
7
+
8
+ sscbirrt_assets.ur5e_xml() # .../menagerie/universal_robots_ur5e/ur5e.xml
9
+ sscbirrt_assets.robotiq_2f85_xml() # .../menagerie/robotiq_2f85/2f85.xml
10
+ sscbirrt_assets.menagerie_path() # a directory laid out like a Menagerie clone
11
+ ```
12
+
13
+ The functions return real file paths, since MJCF loaders resolve meshes relative to the XML.
14
+
15
+ ## Provenance and licenses
16
+
17
+ Each file is copied unmodified from Menagerie at the commit in `sscbirrt_assets.MENAGERIE_COMMIT` and checked against the SHA-256 in `manifest.json`. Only the MJCF, the meshes and the LICENSE of each model ship.
18
+
19
+ - UR5e: BSD-3-Clause, Copyright 2018 ROS Industrial Consortium
20
+ - Robotiq 2F-85: BSD-2-Clause, Copyright (c) 2013, ROS-Industrial
21
+ - The Python code in this package: MIT
22
+
23
+ ## Building
24
+
25
+ `fetch.py` places the files. The build hook calls it, so `uv build assets` works on its own. Pass `python assets/fetch.py --from <clone>` to copy from a local Menagerie clone instead of downloading. Either way the hashes are checked. After moving the pin, regenerate the manifest with `--write-manifest --from <clone>`.
@@ -0,0 +1,112 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Siddhartha Srinivasa
3
+
4
+ """Fetch the menagerie files sscbirrt-assets ships, pinned by commit and checked by SHA-256.
5
+
6
+ The files are not committed; they are copied into ``src/sscbirrt_assets/menagerie/`` at build time
7
+ (``hatch_build.py`` calls :func:`fetch`). ``manifest.json`` is the contract: the upstream commit and
8
+ the SHA-256 of every file. Any mismatch aborts the build.
9
+
10
+ python assets/fetch.py # download from GitHub at the pinned commit
11
+ python assets/fetch.py --from ~/mujoco_menagerie # copy from a local clone (hashes still checked)
12
+ python assets/fetch.py --write-manifest --from ~/mujoco_menagerie # regenerate the manifest
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import hashlib
19
+ import json
20
+ import shutil
21
+ import subprocess
22
+ import sys
23
+ import urllib.request
24
+ from pathlib import Path
25
+
26
+ HERE = Path(__file__).resolve().parent
27
+ MANIFEST = HERE / "manifest.json"
28
+ DEST = HERE / "src" / "sscbirrt_assets" / "menagerie"
29
+ RAW_URL = "https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/{commit}/{path}"
30
+
31
+ # What the manifest covers: each model's MJCF, its meshes, and its LICENSE. Not the preview .png,
32
+ # the scene.xml, or the README/CHANGELOG.
33
+ MODELS = {
34
+ "universal_robots_ur5e": ["ur5e.xml", "LICENSE", "assets/*.obj"],
35
+ "robotiq_2f85": ["2f85.xml", "LICENSE", "assets/*.stl"],
36
+ }
37
+
38
+
39
+ def _sha256(data: bytes) -> str:
40
+ return hashlib.sha256(data).hexdigest()
41
+
42
+
43
+ def _read_source(path: str, commit: str, local: Path | None) -> bytes:
44
+ if local is not None:
45
+ return (local / path).read_bytes()
46
+ with urllib.request.urlopen(RAW_URL.format(commit=commit, path=path), timeout=60) as response:
47
+ return response.read()
48
+
49
+
50
+ def fetch(local: Path | None = None) -> Path:
51
+ """Place every manifest file under DEST, verified; skip files already present with the right hash."""
52
+ manifest = json.loads(MANIFEST.read_text())
53
+ commit = manifest["commit"]
54
+ for path, digest in manifest["files"].items():
55
+ target = DEST / path
56
+ if target.is_file() and _sha256(target.read_bytes()) == digest:
57
+ continue
58
+ data = _read_source(path, commit, local)
59
+ if _sha256(data) != digest:
60
+ raise RuntimeError(f"{path}: SHA-256 mismatch against manifest (menagerie commit {commit})")
61
+ target.parent.mkdir(parents=True, exist_ok=True)
62
+ target.write_bytes(data)
63
+ # Nothing outside the manifest ships: remove stale files from an earlier manifest.
64
+ wanted = {DEST / p for p in manifest["files"]}
65
+ for existing in DEST.rglob("*"):
66
+ if existing.is_file() and existing not in wanted:
67
+ existing.unlink()
68
+ return DEST
69
+
70
+
71
+ def write_manifest(local: Path) -> None:
72
+ """Regenerate manifest.json from a local clone at the commit it is checked out at."""
73
+ status = subprocess.run(
74
+ ["git", "-C", str(local), "status", "--porcelain", "--", *MODELS], capture_output=True, text=True, check=True
75
+ ).stdout
76
+ tracked_changes = [line for line in status.splitlines() if not line.startswith("??")]
77
+ if tracked_changes:
78
+ raise RuntimeError(f"{local} has local changes to the shipped models:\n" + "\n".join(tracked_changes))
79
+ commit = subprocess.run(
80
+ ["git", "-C", str(local), "rev-parse", "HEAD"], capture_output=True, text=True, check=True
81
+ ).stdout.strip()
82
+ files = {}
83
+ for model, patterns in MODELS.items():
84
+ for pattern in patterns:
85
+ for f in sorted((local / model).glob(pattern)):
86
+ rel = f.relative_to(local).as_posix()
87
+ files[rel] = _sha256(f.read_bytes())
88
+ MANIFEST.write_text(json.dumps({"commit": commit, "files": files}, indent=2) + "\n")
89
+ print(f"wrote {MANIFEST} ({len(files)} files, commit {commit})")
90
+
91
+
92
+ def main() -> int:
93
+ parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
94
+ parser.add_argument("--from", dest="local", type=Path, help="copy from a local menagerie clone")
95
+ parser.add_argument("--write-manifest", action="store_true", help="regenerate manifest.json from --from")
96
+ parser.add_argument("--clean", action="store_true", help="delete the fetched files")
97
+ args = parser.parse_args()
98
+ if args.clean:
99
+ shutil.rmtree(DEST, ignore_errors=True)
100
+ return 0
101
+ if args.write_manifest:
102
+ if args.local is None:
103
+ parser.error("--write-manifest needs --from")
104
+ write_manifest(args.local.expanduser())
105
+ return 0
106
+ dest = fetch(args.local.expanduser() if args.local else None)
107
+ print(f"assets ready in {dest}")
108
+ return 0
109
+
110
+
111
+ if __name__ == "__main__":
112
+ sys.exit(main())
@@ -0,0 +1,23 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Siddhartha Srinivasa
3
+
4
+ """Build hook: place and verify the menagerie files before hatchling packs them."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
12
+
13
+
14
+ class FetchMenagerie(BuildHookInterface):
15
+ PLUGIN_NAME = "fetch-menagerie"
16
+
17
+ def initialize(self, version, build_data):
18
+ sys.path.insert(0, str(Path(self.root)))
19
+ try:
20
+ from fetch import fetch
21
+ finally:
22
+ sys.path.pop(0)
23
+ fetch()
@@ -0,0 +1,37 @@
1
+ {
2
+ "commit": "feadf76d42f8a2162426f7d226a3b539556b3bf5",
3
+ "files": {
4
+ "universal_robots_ur5e/ur5e.xml": "ffd0a7b336d3c2d27c35a15cc0d4aaa95196769aca190ac8d6522d3318c345a0",
5
+ "universal_robots_ur5e/LICENSE": "5ec71ccf66c8d03261448f2441a586765b97f2248c860a1ae19689fb1c45cee6",
6
+ "universal_robots_ur5e/assets/base_0.obj": "9a3b21f1524baef6af8a52d7af90b6e94b79f7e3c2b04a0f6581f74d22291b0b",
7
+ "universal_robots_ur5e/assets/base_1.obj": "cf7d54691a2c1eeda280955ebd6bd83c2dbecfc46fb53c10198f05d107511b50",
8
+ "universal_robots_ur5e/assets/forearm_0.obj": "64c049a55149c2ab355594d52302b53d3e34da3677ceaa1917c427cde7743146",
9
+ "universal_robots_ur5e/assets/forearm_1.obj": "a96fe203015cf532b193f414cd01268c7c36795e51a837dd33afd122334f81f2",
10
+ "universal_robots_ur5e/assets/forearm_2.obj": "05b436505fc595aeab26675590c2b56b88a65b2d52d6352096fed9f07cb4c4ca",
11
+ "universal_robots_ur5e/assets/forearm_3.obj": "fa31af19cfad7a765b62e8a2d5931cb014b88de84195a7850b4e5ee902112e9a",
12
+ "universal_robots_ur5e/assets/shoulder_0.obj": "477387a7e7312782a6040484a3c2c2fcee33f40109f419076e31b6f6e1692c68",
13
+ "universal_robots_ur5e/assets/shoulder_1.obj": "8537bba02804640ddf6e260f79fe66e895b3229ed3d44298a95e8e027eb5d435",
14
+ "universal_robots_ur5e/assets/shoulder_2.obj": "291714518416bb8f0bafbf123867db011ecbf5146a05245ca5893ee8c57d4e21",
15
+ "universal_robots_ur5e/assets/upperarm_0.obj": "14143d4f1175a7dbacc8e2f2f98f1cac5a2b2e238cd46cd03654d31f2610eb53",
16
+ "universal_robots_ur5e/assets/upperarm_1.obj": "272899e927f4a32b7c25e34560ec70654c0346cc8ed1c616c0555f3f53b32d14",
17
+ "universal_robots_ur5e/assets/upperarm_2.obj": "86c05350d60eb0e7e28a81b544267478b7db997baba41cef19088ebbeffecfa1",
18
+ "universal_robots_ur5e/assets/upperarm_3.obj": "b257dab441e6fab64ea03b4b87029d8a96554ad1a513cce944d56acba0ad948a",
19
+ "universal_robots_ur5e/assets/wrist1_0.obj": "7ef0c59b5c5d56a9902435c9a058b27d2a38c85fe2bae02d00a9284dd1f4d519",
20
+ "universal_robots_ur5e/assets/wrist1_1.obj": "ee6d5c562e6becef94109b0543329080d285d1aef521a01ed98e18a5bf1e8684",
21
+ "universal_robots_ur5e/assets/wrist1_2.obj": "88c66dc64f15391287147d50add37729a3f41dadd80b1f859cf2cb6d45daf32a",
22
+ "universal_robots_ur5e/assets/wrist2_0.obj": "2ac2e2e39d8f7005bc3b3ab26cd61634be2e14930074615da5318b9ee7c569a8",
23
+ "universal_robots_ur5e/assets/wrist2_1.obj": "a721d06dc61d3b3a8ec58c035c9e3358ed30d6991a72e077d8abf3c3c203b633",
24
+ "universal_robots_ur5e/assets/wrist2_2.obj": "6df4335c8bd5e881fda2c224f770115ffe9ddd0cd321ebe3c0a4a310796f0987",
25
+ "universal_robots_ur5e/assets/wrist3.obj": "0e51748f9c93bd66604360a5cf7e2e8a6e754550be64076333ff4fc19ec90c61",
26
+ "robotiq_2f85/2f85.xml": "e07bba81975002dbae8b1e9984b0a2088ecca55f52cf395d099650d15db2bc9d",
27
+ "robotiq_2f85/LICENSE": "3d5edd18d1e22a17666e9bb84270171ed5ac90b192d24e0177fd171708ea2316",
28
+ "robotiq_2f85/assets/base.stl": "1c7b9f2bd92d705fc4e897c94e905973a3c05f406845e942229433deb7041453",
29
+ "robotiq_2f85/assets/base_mount.stl": "87971dfdd6e5311b5a723e7b73c4701ebf5c2364df671500f30dfc1c53c8e61a",
30
+ "robotiq_2f85/assets/coupler.stl": "d5ee95e62f8415bf5b6e503c831a958f5fc1990bf9b2865329ec38a28932727c",
31
+ "robotiq_2f85/assets/driver.stl": "bbd6e868b1778bead60d2516c7ede581d7ad4e431744b1828757d6ea5129c112",
32
+ "robotiq_2f85/assets/follower.stl": "633793332c081641ce200df27a40643cc293b29956e3cb5cb29cb811c33ef1c7",
33
+ "robotiq_2f85/assets/pad.stl": "4af1fa8d9bb285abecbf5dfc88e74cb30f059a7a074c2030dc35e9ee4316019d",
34
+ "robotiq_2f85/assets/silicone_pad.stl": "ca20c0fa61e6d3ce7b04bed25360e048e31dfd88ace04187aa6d9e8f4ca3fd0f",
35
+ "robotiq_2f85/assets/spring_link.stl": "4fb74a8a0d76c0e471cf19fd48bc676fd5b19123798e7a39eb6aa56869354283"
36
+ }
37
+ }
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "sscbirrt-assets"
3
+ version = "1.0.0"
4
+ description = "The MuJoCo Menagerie UR5e and Robotiq 2F-85 models, packaged for the sscbirrt demos"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ # The Python code is MIT; the model files keep their upstream licenses.
8
+ license = "MIT AND BSD-3-Clause AND BSD-2-Clause"
9
+ license-files = [
10
+ "LICENSE",
11
+ "src/sscbirrt_assets/menagerie/universal_robots_ur5e/LICENSE",
12
+ "src/sscbirrt_assets/menagerie/robotiq_2f85/LICENSE",
13
+ ]
14
+ keywords = ["mujoco", "ur5e", "robotiq", "robot-models", "sscbirrt"]
15
+ classifiers = [
16
+ "Intended Audience :: Science/Research",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Scientific/Engineering",
19
+ ]
20
+ authors = [
21
+ {name = "Siddhartha Srinivasa", email = "siddhartha.srinivasa@gmail.com"},
22
+ ]
23
+ dependencies = []
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/personalrobotics/sscbirrt"
27
+ Source = "https://github.com/personalrobotics/sscbirrt/tree/main/assets"
28
+ Upstream = "https://github.com/google-deepmind/mujoco_menagerie"
29
+
30
+ [build-system]
31
+ requires = ["hatchling>=1.27"]
32
+ build-backend = "hatchling.build"
33
+
34
+ # The menagerie files are fetched at build time (hatch_build.py -> fetch.py, pinned and hash-checked) and
35
+ # gitignored, so they are declared as artifacts: hatchling otherwise drops VCS-ignored files.
36
+ [tool.hatch.build.hooks.custom]
37
+ path = "hatch_build.py"
38
+
39
+ [tool.hatch.build.targets.wheel]
40
+ packages = ["src/sscbirrt_assets"]
41
+ artifacts = ["src/sscbirrt_assets/menagerie/**"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ include = ["src/", "fetch.py", "hatch_build.py", "manifest.json", "README.md", "LICENSE"]
45
+ artifacts = ["src/sscbirrt_assets/menagerie/**"]
@@ -0,0 +1,44 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Siddhartha Srinivasa
3
+
4
+ """Robot models for the sscbirrt demos: the MuJoCo Menagerie UR5e and Robotiq 2F-85.
5
+
6
+ The files are copied unmodified from google-deepmind/mujoco_menagerie at the commit in
7
+ :data:`MENAGERIE_COMMIT`, each under its own LICENSE (UR5e: BSD-3-Clause; 2F-85: BSD-2-Clause).
8
+ The functions return real file paths because loaders such as ``ssik.Manipulator.from_mjcf`` and
9
+ ``mujoco.MjSpec.from_file`` resolve the meshes relative to the MJCF.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+
16
+ __all__ = ["MENAGERIE_COMMIT", "menagerie_path", "robotiq_2f85_xml", "ur5e_xml"]
17
+
18
+ MENAGERIE_COMMIT = "feadf76d42f8a2162426f7d226a3b539556b3bf5"
19
+
20
+ _ROOT = Path(__file__).resolve().parent / "menagerie"
21
+
22
+
23
+ def menagerie_path() -> Path:
24
+ """A directory laid out like a menagerie clone, holding only the shipped models."""
25
+ return _ROOT
26
+
27
+
28
+ def _existing(relative: str) -> Path:
29
+ path = _ROOT / relative
30
+ if not path.is_file():
31
+ raise FileNotFoundError(
32
+ f"{path} is missing. In a source checkout, run `python assets/fetch.py` to place the models."
33
+ )
34
+ return path
35
+
36
+
37
+ def ur5e_xml() -> Path:
38
+ """The Menagerie UR5e MJCF (``universal_robots_ur5e/ur5e.xml``)."""
39
+ return _existing("universal_robots_ur5e/ur5e.xml")
40
+
41
+
42
+ def robotiq_2f85_xml() -> Path:
43
+ """The Menagerie Robotiq 2F-85 MJCF (``robotiq_2f85/2f85.xml``)."""
44
+ return _existing("robotiq_2f85/2f85.xml")
@@ -0,0 +1,191 @@
1
+ <mujoco model="robotiq_2f85">
2
+ <compiler angle="radian" meshdir="assets" autolimits="true"/>
3
+
4
+ <option cone="elliptic" impratio="10"/>
5
+
6
+ <asset>
7
+ <material name="metal" rgba="0.58 0.58 0.58 1"/>
8
+ <material name="silicone" rgba="0.1882 0.1882 0.1882 1"/>
9
+ <material name="gray" rgba="0.4627 0.4627 0.4627 1"/>
10
+ <material name="black" rgba="0.149 0.149 0.149 1"/>
11
+
12
+ <mesh class="2f85" file="base_mount.stl"/>
13
+ <mesh class="2f85" file="base.stl"/>
14
+ <mesh class="2f85" file="driver.stl"/>
15
+ <mesh class="2f85" file="coupler.stl"/>
16
+ <mesh class="2f85" file="follower.stl"/>
17
+ <mesh class="2f85" file="pad.stl"/>
18
+ <mesh class="2f85" file="silicone_pad.stl"/>
19
+ <mesh class="2f85" file="spring_link.stl"/>
20
+ </asset>
21
+
22
+ <default>
23
+ <default class="2f85">
24
+ <mesh scale="0.001 0.001 0.001"/>
25
+ <general biastype="affine"/>
26
+
27
+ <joint axis="1 0 0"/>
28
+ <default class="driver">
29
+ <joint range="0 0.8" armature="0.005" damping="0.1" solimplimit="0.95 0.99 0.001" solreflimit="0.005 1"/>
30
+ </default>
31
+ <default class="follower">
32
+ <joint range="-0.872664 0.872664" armature="0.001" pos="0 -0.018 0.0065" solimplimit="0.95 0.99 0.001" solreflimit="0.005 1"/>
33
+ </default>
34
+ <default class="spring_link">
35
+ <joint range="-0.29670597283 0.8" armature="0.001" stiffness="0.05" springref="2.62" damping="0.00125"/>
36
+ </default>
37
+ <default class="coupler">
38
+ <joint range="-1.57 0" armature="0.001" solimplimit="0.95 0.99 0.001" solreflimit="0.005 1"/>
39
+ </default>
40
+
41
+ <default class="visual">
42
+ <geom type="mesh" contype="0" conaffinity="0" group="2"/>
43
+ </default>
44
+ <default class="collision">
45
+ <geom type="mesh" group="3"/>
46
+ <default class="pad_box1">
47
+ <geom mass="0" type="box" pos="0 -0.0026 0.028125" size="0.011 0.004 0.009375" friction="0.7"
48
+ solimp="0.95 0.99 0.001" solref="0.004 1" priority="1" rgba="0.55 0.55 0.55 1"/>
49
+ </default>
50
+ <default class="pad_box2">
51
+ <geom mass="0" type="box" pos="0 -0.0026 0.009375" size="0.011 0.004 0.009375" friction="0.6"
52
+ solimp="0.95 0.99 0.001" solref="0.004 1" priority="1" rgba="0.45 0.45 0.45 1"/>
53
+ </default>
54
+ </default>
55
+ </default>
56
+ </default>
57
+
58
+ <worldbody>
59
+ <body name="base_mount" pos="0 0 0.007" childclass="2f85">
60
+ <geom class="visual" mesh="base_mount" material="black"/>
61
+ <geom class="collision" mesh="base_mount"/>
62
+ <body name="base" pos="0 0 0.0038" quat="1 0 0 -1">
63
+ <inertial mass="0.777441" pos="0 -2.70394e-05 0.0354675" quat="1 -0.00152849 0 0"
64
+ diaginertia="0.000260285 0.000225381 0.000152708"/>
65
+ <geom class="visual" mesh="base" material="black"/>
66
+ <geom class="collision" mesh="base"/>
67
+ <site name="pinch" pos="0 0 0.145" type="sphere" group="5" rgba="0.9 0.9 0.9 1" size="0.005"/>
68
+ <!-- Right-hand side 4-bar linkage -->
69
+ <body name="right_driver" pos="0 0.0306011 0.054904">
70
+ <inertial mass="0.00899563" pos="2.96931e-12 0.0177547 0.00107314" quat="0.681301 0.732003 0 0"
71
+ diaginertia="1.72352e-06 1.60906e-06 3.22006e-07"/>
72
+ <joint name="right_driver_joint" class="driver"/>
73
+ <geom class="visual" mesh="driver" material="gray"/>
74
+ <geom class="collision" mesh="driver"/>
75
+ <body name="right_coupler" pos="0 0.0315 -0.0041">
76
+ <inertial mass="0.0140974" pos="0 0.00301209 0.0232175" quat="0.705636 -0.0455904 0.0455904 0.705636"
77
+ diaginertia="4.16206e-06 3.52216e-06 8.88131e-07"/>
78
+ <joint name="right_coupler_joint" class="coupler"/>
79
+ <geom class="visual" mesh="coupler" material="black"/>
80
+ <geom class="collision" mesh="coupler"/>
81
+ </body>
82
+ </body>
83
+ <body name="right_spring_link" pos="0 0.0132 0.0609">
84
+ <inertial mass="0.0221642" pos="-8.65005e-09 0.0181624 0.0212658" quat="0.663403 -0.244737 0.244737 0.663403"
85
+ diaginertia="8.96853e-06 6.71733e-06 2.63931e-06"/>
86
+ <joint name="right_spring_link_joint" class="spring_link"/>
87
+ <geom class="visual" mesh="spring_link" material="black"/>
88
+ <geom class="collision" mesh="spring_link"/>
89
+ <body name="right_follower" pos="0 0.055 0.0375">
90
+ <inertial mass="0.0125222" pos="0 -0.011046 0.0124786" quat="1 0.1664 0 0"
91
+ diaginertia="2.67415e-06 2.4559e-06 6.02031e-07"/>
92
+ <joint name="right_follower_joint" class="follower"/>
93
+ <geom class="visual" mesh="follower" material="black"/>
94
+ <geom class="collision" mesh="follower"/>
95
+ <body name="right_pad" pos="0 -0.0189 0.01352">
96
+ <geom class="pad_box1" name="right_pad1"/>
97
+ <geom class="pad_box2" name="right_pad2"/>
98
+ <inertial mass="0.0035" pos="0 -0.0025 0.0185" quat="0.707107 0 0 0.707107"
99
+ diaginertia="4.73958e-07 3.64583e-07 1.23958e-07"/>
100
+ <geom class="visual" mesh="pad"/>
101
+ <body name="right_silicone_pad">
102
+ <geom class="visual" mesh="silicone_pad" material="black"/>
103
+ </body>
104
+ </body>
105
+ </body>
106
+ </body>
107
+ <!-- Left-hand side 4-bar linkage -->
108
+ <body name="left_driver" pos="0 -0.0306011 0.054904" quat="0 0 0 1">
109
+ <inertial mass="0.00899563" pos="0 0.0177547 0.00107314" quat="0.681301 0.732003 0 0"
110
+ diaginertia="1.72352e-06 1.60906e-06 3.22006e-07"/>
111
+ <joint name="left_driver_joint" class="driver"/>
112
+ <geom class="visual" mesh="driver" material="gray"/>
113
+ <geom class="collision" mesh="driver"/>
114
+ <body name="left_coupler" pos="0 0.0315 -0.0041">
115
+ <inertial mass="0.0140974" pos="0 0.00301209 0.0232175" quat="0.705636 -0.0455904 0.0455904 0.705636"
116
+ diaginertia="4.16206e-06 3.52216e-06 8.88131e-07"/>
117
+ <joint name="left_coupler_joint" class="coupler"/>
118
+ <geom class="visual" mesh="coupler" material="black"/>
119
+ <geom class="collision" mesh="coupler"/>
120
+ </body>
121
+ </body>
122
+ <body name="left_spring_link" pos="0 -0.0132 0.0609" quat="0 0 0 1">
123
+ <inertial mass="0.0221642" pos="-8.65005e-09 0.0181624 0.0212658" quat="0.663403 -0.244737 0.244737 0.663403"
124
+ diaginertia="8.96853e-06 6.71733e-06 2.63931e-06"/>
125
+ <joint name="left_spring_link_joint" class="spring_link"/>
126
+ <geom class="visual" mesh="spring_link" material="black"/>
127
+ <geom class="collision" mesh="spring_link"/>
128
+ <body name="left_follower" pos="0 0.055 0.0375">
129
+ <inertial mass="0.0125222" pos="0 -0.011046 0.0124786" quat="1 0.1664 0 0"
130
+ diaginertia="2.67415e-06 2.4559e-06 6.02031e-07"/>
131
+ <joint name="left_follower_joint" class="follower"/>
132
+ <geom class="visual" mesh="follower" material="black"/>
133
+ <geom class="collision" mesh="follower"/>
134
+ <body name="left_pad" pos="0 -0.0189 0.01352">
135
+ <geom class="pad_box1" name="left_pad1"/>
136
+ <geom class="pad_box2" name="left_pad2"/>
137
+ <inertial mass="0.0035" pos="0 -0.0025 0.0185" quat="1 0 0 1"
138
+ diaginertia="4.73958e-07 3.64583e-07 1.23958e-07"/>
139
+ <geom class="visual" mesh="pad"/>
140
+ <body name="left_silicone_pad">
141
+ <geom class="visual" mesh="silicone_pad" material="black"/>
142
+ </body>
143
+ </body>
144
+ </body>
145
+ </body>
146
+ </body>
147
+ </body>
148
+ </worldbody>
149
+
150
+ <contact>
151
+ <exclude body1="base" body2="left_driver"/>
152
+ <exclude body1="base" body2="right_driver"/>
153
+ <exclude body1="base" body2="left_spring_link"/>
154
+ <exclude body1="base" body2="right_spring_link"/>
155
+ <exclude body1="right_coupler" body2="right_follower"/>
156
+ <exclude body1="left_coupler" body2="left_follower"/>
157
+ </contact>
158
+
159
+ <!--
160
+ This adds stability to the model by having a tendon that distributes the forces between both
161
+ joints, such that the equality constraint doesn't have to do that much work in order to equalize
162
+ both joints. Since both joints share the same sign, we split the force between both equally by
163
+ setting coef=0.5
164
+ -->
165
+ <tendon>
166
+ <fixed name="split">
167
+ <joint joint="right_driver_joint" coef="0.5"/>
168
+ <joint joint="left_driver_joint" coef="0.5"/>
169
+ </fixed>
170
+ </tendon>
171
+
172
+ <equality>
173
+ <connect anchor="0 0 0" body1="right_follower" body2="right_coupler" solimp="0.95 0.99 0.001" solref="0.005 1"/>
174
+ <connect anchor="0 0 0" body1="left_follower" body2="left_coupler" solimp="0.95 0.99 0.001" solref="0.005 1"/>
175
+ <joint joint1="right_driver_joint" joint2="left_driver_joint" polycoef="0 1 0 0 0" solimp="0.95 0.99 0.001"
176
+ solref="0.005 1"/>
177
+ </equality>
178
+
179
+ <!--
180
+ The general actuator below is a customized position actuator (with some damping) where
181
+ gainprm[0] != kp (see http://mujoco.org/book/modeling.html#position).
182
+ The reason why gainprm[0] != kp is because the control input range has to be re-scaled to
183
+ [0, 255]. The joint range is currently set at [0, 0.8], the control range is [0, 255] and
184
+ kp = 100. Tau = Kp * scale * control_input - Kp * error, max(Kp * scale * control_input) = 0.8,
185
+ hence scale = 0.8 * 100 / 255
186
+ -->
187
+ <actuator>
188
+ <general class="2f85" name="fingers_actuator" tendon="split" forcerange="-5 5" ctrlrange="0 255"
189
+ gainprm="0.3137255 0 0" biasprm="0 -100 -10"/>
190
+ </actuator>
191
+ </mujoco>
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013, ROS-Industrial
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ Redistributions in binary form must reproduce the above copyright notice, this
11
+ list of conditions and the following disclaimer in the documentation and/or
12
+ other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,26 @@
1
+ Copyright 2018 ROS Industrial Consortium
2
+
3
+ Redistribution and use in source and binary forms, with or without modification,
4
+ are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this
7
+ list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation and/or
11
+ other materials provided with the distribution.
12
+
13
+ 3. Neither the name of the copyright holder nor the names of its contributors
14
+ may be used to endorse or promote products derived from this software without
15
+ specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
21
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.