Skip to content

Commit ac43c49

Browse files
authored
Merge pull request #50 from AGH-CEAI/feature/cut_xacro_dependency
Cut xacro dependency & download URDF assets from ClearML
2 parents 7559a4d + 45a8224 commit ac43c49

File tree

6 files changed

+200
-40
lines changed

6 files changed

+200
-40
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- [PR-49](https://github.com/AGH-CEAI/aegis_gym/pull/49) - Utility script for uploading the URDF assets to the ClearML server as a dataset.
1213
- [PR-48](https://github.com/AGH-CEAI/aegis_gym/pull/48) - Added real robot control via gRPC in RSL-RL Grasp env.
1314
- [PR-46](https://github.com/AGH-CEAI/aegis_gym/pull/46) - Added TCP-to-object Grasp environment.
1415
- [PR-40](https://github.com/AGH-CEAI/aegis_gym/pull/40) - Added scene and tool cameras setup for Grasp environment.
@@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2728

2829
### Changed
2930

31+
- [PR-49](https://github.com/AGH-CEAI/aegis_gym/pull/49) - The `AegisGrasp`'s rsl_rl robot config accepts ID to download URDF dataset from ClearML (see [aegis_ros PR-95](https://github.com/AGH-CEAI/aegis_ros/pull/95)).
3032
- [PR-42](https://github.com/AGH-CEAI/aegis_gym/pull/42) - Extracted Grasp environment configs to a new file.
3133
- [PR-42](https://github.com/AGH-CEAI/aegis_gym/pull/42) - Ported Grasp environment to use `rsl-rl-lib==3.3.0`.
3234
- [PR-38](https://github.com/AGH-CEAI/aegis_gym/pull/38) - Changed `ur_base` frame to the `world` frame.
@@ -44,6 +46,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4446

4547
### Removed
4648

49+
- [PR-49](https://github.com/AGH-CEAI/aegis_gym/pull/49) - Removed automatic URDF generation with `xacro`.
50+
4751
### Fixed
4852

4953
- [PR-46](https://github.com/AGH-CEAI/aegis_gym/pull/46) - Fixed model sorting and typos.

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,35 @@ poetry run pytest -v -s
3737
```bash
3838
python3 ./test/sb3_run_train.py
3939
```
40+
41+
---
42+
## URDF model for simulator
43+
44+
### Uploading
45+
1. Generate standalone URDF model with [aegis_ros/aegis_descrption]() launch command:
46+
```bash
47+
ros2 launch aegis_description generate_standalone_urdf.launch.py disable_cell:=true
48+
```
49+
Which will generate the whole URDF file with 3D models in a default `~/ceai_ws/aegis_urdf` directory.
50+
51+
2. Run the [`utils/upload_urdf_to_clearml.py`](./utils/upload_urdf_to_clearml.py) script with the following options:
52+
```bash
53+
python3 utils/upload_urdf_to_clearml.py ~/ceai_ws/aegis_urdf --name AegisURDFModel --project AEGIS_GRASP --desc "Aegis simulator assets"
54+
```
55+
> [!WARNING]
56+
> **To update the dataset** make sure to add an additional option: `--parent "PREVIOUS_DATASET_ID"`
57+
58+
3. Check the ClearML server's datasets.
59+
60+
### Usage
61+
62+
In the robot's config set the `urdf_model_id` param to the ClearML's dataset ID.
63+
64+
> [!IMPORTANT]
65+
> In case of failure to obtain the model, the code will try to load URDF model from `~/ceai_ws/aegid_urdf` directory.
66+
67+
68+
4069
---
4170
## Development notes
4271

aegis_gym/rsl/envs/manipulator.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import time
2+
import warnings
3+
from pathlib import Path
14
from typing import Literal
25

36
import torch as th
47
import genesis as gs
8+
from clearml import Dataset
59
from genesis.utils.geom import (
610
transform_quat_by_quat,
711
xyz_to_quat,
812
)
913

10-
from .utils import generate_aegis_urdf
11-
1214

1315
class Manipulator:
1416
def __init__(
@@ -25,10 +27,22 @@ def __init__(
2527
self._num_envs = num_envs
2628
self._args = args
2729

30+
if show_cell:
31+
self._urdf_model_id = args["urdf_model_id"]["cell"]
32+
else:
33+
self._urdf_model_id = args["urdf_model_id"]["no_cell"]
34+
35+
if self._urdf_model_id:
36+
print(
37+
f"[GraspEnv::Manipulator] URDF ClearML dataset ID: {self._urdf_model_id}"
38+
)
39+
self._urdf_path = self._resolve_aegis_urdf()
40+
print(f"[GraspEnv::Manipulator] URDF path: {self._urdf_path}")
41+
2842
# == Genesis configurations ==
2943
material: gs.materials.Rigid = gs.materials.Rigid()
3044
morph: gs.morphs.URDF = gs.morphs.URDF(
31-
file=generate_aegis_urdf(show_cell),
45+
file=self._urdf_path,
3246
fixed=True,
3347
pos=(0.0, 0.0, 0.0),
3448
quat=(1.0, 0.0, 0.0, 0.0),
@@ -49,6 +63,41 @@ def __init__(
4963

5064
self._init()
5165

66+
def _resolve_aegis_urdf(self) -> Path:
67+
default_path = Path("~/ceai_ws/aegis_urdf/aegis.urdf").expanduser().resolve()
68+
69+
if self._urdf_model_id is not None:
70+
try:
71+
dataset = Dataset.get(dataset_id=self._urdf_model_id)
72+
local_path = Path(dataset.get_local_copy())
73+
except ValueError:
74+
warnings.warn(
75+
"Failed to obtain the dataset: `{e}`. Fallbacking to the default path..."
76+
)
77+
return default_path
78+
79+
urdf_files = list(local_path.rglob("*.urdf"))
80+
if not urdf_files:
81+
raise FileNotFoundError(
82+
f"No URDF file in dataset {self._urdf_model_id}"
83+
)
84+
if len(urdf_files) > 1:
85+
raise RuntimeError(
86+
f"Found {len(urdf_files)} URDF files in dataset {self._urdf_model_id}, expected just one"
87+
)
88+
return str(urdf_files[0])
89+
90+
warnings.warn(
91+
"There is no given ClearML dataset ID for the URDF assets! Trying to read the default directory in 5s.."
92+
)
93+
time.sleep(5.0)
94+
95+
if not default_path.exists():
96+
raise FileNotFoundError(
97+
f"Couldn't resolve the path to the URDF file: Default file '{default_path}' doesn't exist!"
98+
)
99+
return default_path
100+
52101
def set_pd_gains(self):
53102
# set control gains
54103
self._robot_entity.set_dofs_kp(

aegis_gym/rsl/envs/utils.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

aegis_gym/rsl/grasp_cfgs.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,9 @@ def get_task_cfgs():
145145
"default_arm_dof": [0.0, -2.09, 2.09, -1.57, -1.57, 0.0],
146146
"default_gripper_dof": [0.025, 0.025],
147147
"ik_method": "dls_ik",
148+
"urdf_model_id": {
149+
"cell": "4ae9243a9e294db998d3d6e0b5a0539b",
150+
"no_cell": "3b30eed8cea6423a99d9bad3343740ed",
151+
},
148152
}
149153
return env_cfg, reward_scales, robot_cfg

utils/upload_urdf_to_clearml.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Upload robot simulator assets to ClearML Dataset.
4+
Handles URDF + STL/DAE models as a versioned dataset.
5+
"""
6+
7+
import argparse
8+
import logging
9+
from pathlib import Path
10+
from typing import Optional
11+
12+
from clearml import Dataset
13+
14+
logging.basicConfig(level=logging.INFO)
15+
logger = logging.getLogger(__name__)
16+
17+
18+
def upload_robot_assets(
19+
robot_folder_path: str,
20+
dataset_name: str = "robot_simulator_assets",
21+
dataset_project: str = "DeepRL",
22+
parent: str = None,
23+
output_storage: Optional[str] = None, # None = use default ClearML file server
24+
description: Optional[str] = None,
25+
):
26+
"""
27+
Upload robot simulator folder to ClearML Dataset.
28+
29+
Args:
30+
robot_folder_path: Path to folder containing URDF + models (STL/DAE)
31+
dataset_name: Name of the dataset (version auto-incremented)
32+
dataset_project: Project name in ClearML
33+
output_storage: Target storage (None=default fileserver, or "/path", "s3://bucket", etc.)
34+
description: Optional dataset description
35+
"""
36+
37+
folder_path = Path(robot_folder_path).resolve()
38+
39+
if not folder_path.exists():
40+
raise FileNotFoundError(f"Folder not found: {robot_folder_path}")
41+
42+
logger.info(f"> Creating dataset: {dataset_project}/{dataset_name}")
43+
if parent:
44+
logger.info(f"> Parent DatasetID: {parent}")
45+
logger.info(f"> Source folder: {folder_path}")
46+
logger.info(f"> Files to upload: {len(list(folder_path.rglob('*')))}")
47+
48+
# Create dataset (automatically increments version if exists)
49+
parent_list = [parent] if parent else None
50+
dataset = Dataset.create(
51+
dataset_name=dataset_name,
52+
dataset_project=dataset_project,
53+
parent_datasets=parent_list,
54+
description=description or "Robot simulator assets",
55+
)
56+
57+
logger.info(f"> Dataset created: {dataset.id}")
58+
59+
# Add all files from folder (preserves structure)
60+
logger.info("> Adding files to dataset...")
61+
num_files = dataset.add_files(path=str(folder_path), recursive=True, verbose=True)
62+
63+
logger.info(f"> Added {num_files} files")
64+
65+
# Upload to storage
66+
logger.info("> Uploading to ClearML server...")
67+
dataset.upload(
68+
show_progress=True,
69+
output_url=output_storage, # Uses default if None
70+
)
71+
72+
logger.info("> Upload complete")
73+
74+
# Finalize (lock version, prevents further modifications)
75+
logger.info("> Finalizing dataset...")
76+
dataset.finalize()
77+
78+
logger.info("\n> SUCCESS!")
79+
logger.info(f"> Dataset ID: {dataset.id}")
80+
logger.info(f"> Project: {dataset_project}")
81+
logger.info(f"> Name: {dataset_name}")
82+
logger.info(f"> Use in code: Dataset.get(dataset_id='{dataset.id}')")
83+
84+
return dataset.id
85+
86+
87+
if __name__ == "__main__":
88+
parser = argparse.ArgumentParser(description="Upload robot assets to ClearML")
89+
parser.add_argument("folder", help="Path to robot model folder")
90+
parser.add_argument("--name", default="AegisURDFModel", help="Dataset name")
91+
parser.add_argument("--project", default="AEGIS_GRASP", help="ClearML project")
92+
parser.add_argument(
93+
"--parent",
94+
default=None,
95+
help="Already existing dataset ID which will be the parent of this version.",
96+
)
97+
parser.add_argument(
98+
"--storage", default=None, help="Storage URL (s3://, /path, etc.)"
99+
)
100+
parser.add_argument("--desc", default=None, help="Dataset description")
101+
102+
args = parser.parse_args()
103+
104+
upload_robot_assets(
105+
robot_folder_path=args.folder,
106+
dataset_name=args.name,
107+
dataset_project=args.project,
108+
parent=args.parent,
109+
output_storage=args.storage,
110+
description=args.desc,
111+
)

0 commit comments

Comments
 (0)