Add clutter placement - #1242
Conversation
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
2155f20 to
f25f5e4
Compare
|
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
alexmillane
left a comment
There was a problem hiding this comment.
This is a good start.
However I have a few opinions on the design:
- Output: Right now the system outputs a single
environment.yamlper placement. Generating lots of graphs in the case that we want many placements. I would suggest instead that we introduce a new type of yaml file that is an optional companion to our usual environment files. If such a file is passed to the run-time (or specified in the environment.yaml) poses are taken from this companion file. This file would be a mapping between object names and a list of poses-per-object. With this design we can get 1000s of layouts with just two files. It also separates specification of the environment from the specification of the placement. Wdyt? - Settling specification. Right now we specify which objects are to be settled via command line parameters. Which means that they're not captured in the
environment.yaml. What do you think about instead adding a new relation-type calledClutterOnwhich is specified in theenvironment.yamland captures that this object is the target of the clutter placement. This way the placement of the object travels with the environment spec, rather than being captured on a users CLI (and then immediately lost). - Before drop initialization: Right now we use a parallel system to our
ObjectPlacerto arrange the object prior to dropping them. Could we not implement this inside the object placer.ClutterOncould translate to an object being above a support surface, as low as possible, without colliding with other (ClutterOn) objects. This would leave to the objects forming a vertical collumn prior to the settling. It also would mean thatClutterOnwould produce valid results even without running throughgenerate_clutter_scene.py. They would just drop at the start of simulation.
| """Apply a fixed YAML pose at construction and on every reset.""" | ||
| if value is None: | ||
| return | ||
| assert isinstance(value, dict), "initial_pose must be a mapping" |
There was a problem hiding this comment.
Can we correct the type annotation above. At the input we say Any but then check that it's None | dict
| def _apply_initial_pose(asset: Asset, value: Any) -> None: | ||
| """Apply a fixed YAML pose at construction and on every reset.""" | ||
| if value is None: | ||
| return | ||
| assert isinstance(value, dict), "initial_pose must be a mapping" | ||
| assert not (set(value) - {"position_xyz", "rotation_xyzw"}), "Unknown initial_pose fields" | ||
| if "position_xyz" not in value or "rotation_xyzw" not in value: | ||
| declared = asset.get_initial_pose() | ||
| assert declared is None or isinstance(declared, Pose), "Partial initial_pose requires a fixed default pose" | ||
| default = declared if declared is not None else Pose.identity() | ||
| position = value.get("position_xyz", default.position_xyz) | ||
| rotation = value.get("rotation_xyzw", default.rotation_xyzw) | ||
| else: | ||
| position, rotation = value["position_xyz"], value["rotation_xyzw"] | ||
| for name, values, size in (("position_xyz", position, 3), ("rotation_xyzw", rotation, 4)): | ||
| assert isinstance(values, (list, tuple)) and len(values) == size, f"{name} needs {size} numbers" | ||
| assert all( | ||
| isinstance(v, Real) and not isinstance(v, bool) and math.isfinite(v) for v in values | ||
| ), f"{name} must contain finite numbers" | ||
| assert math.isclose(sum(v * v for v in rotation), 1.0, abs_tol=1e-4), "rotation_xyzw must be a unit quaternion" | ||
| asset.set_initial_pose(Pose(tuple(float(v) for v in position), tuple(float(v) for v in rotation))) |
There was a problem hiding this comment.
99% of this function is extracting a Pose from a dict.
Then we set it in half the final line.
Suggestion to break this function up. The first function should be
def _get_pose_from_dict(pose_dict: dict) -> Pose | None:
This
def _apply_initial_pose(pose: Pose | None):
|
|
||
|
|
||
| def scene_with_cached_poses( | ||
| spec: ArenaEnvGraphSpec, poses: Mapping[str, Pose], assets_by_node_id: Mapping[str, Asset] |
There was a problem hiding this comment.
I'm interested why the agent wants to use Mapping. Do we expect something other than a dict here?
There was a problem hiding this comment.
This is a test/example file correct?
I would suggest that this goes into isaaclab_arena_environments or isaaclab_arena_examples.
Generally we try to keep examples out of the core framework.
| def write_scene_cache(spec: ArenaEnvGraphSpec, path: Path) -> None: | ||
| """Publish a complete YAML file atomically without overwriting; requires hard-link support.""" | ||
| path.parent.mkdir(parents=True, exist_ok=True) | ||
| with tempfile.TemporaryDirectory(dir=path.parent, prefix=".clutter-") as directory: | ||
| temporary = Path(directory) / path.name | ||
| spec.write_yaml(temporary) | ||
| # A same-filesystem hard link publishes the complete file without overwriting a raced writer. | ||
| os.link(temporary, path) |
There was a problem hiding this comment.
I'm wondering why we have to go through all this effort to write a file? What's wrong with just writing a file normally?
Summary
Add offline physics-settled clutter placement
Detailed description
pre-commit --all-files. Newton and CAP/Berkeley reproduction remain unverified.