Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions embodichain/gen_sim/action_agent_pipeline/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

__all__ = [
"ACTION_AGENT_CONFIG_DEFAULTS",
"CONVEX_HULL_DEFAULTS",
"DEFAULT_MAX_EPISODES",
"DEFAULT_MAX_EPISODE_STEPS",
"DEFAULT_SURFACE_RELEASE_CLEARANCE",
Expand Down Expand Up @@ -58,8 +59,33 @@ def generation_defaults_section(name: str) -> dict[str, Any]:
return section


def _load_convex_hull_defaults() -> dict[str, Any]:
physics = generation_defaults_section("physics")
convex_hulls = physics.get("convex_hulls")
if not isinstance(convex_hulls, dict):
raise ValueError("Generation default physics.convex_hulls must be a mapping.")
value_paths = (
("target",),
("container",),
("moved",),
("extra_rigid",),
("table",),
)
for value_path in value_paths:
value: Any = convex_hulls
for key in value_path:
value = value.get(key) if isinstance(value, dict) else None
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
dotted_path = ".".join(("physics", "convex_hulls", *value_path))
raise ValueError(
f"Generation default {dotted_path} must be a positive integer."
)
return convex_hulls


_TASK_DEFAULTS = generation_defaults_section("task")
_GEOMETRY_DEFAULTS = generation_defaults_section("geometry")
CONVEX_HULL_DEFAULTS = _load_convex_hull_defaults()

DEFAULT_TASK_NAME = str(_TASK_DEFAULTS["default_name"])
DEFAULT_MAX_EPISODES = int(_TASK_DEFAULTS["max_episodes"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import warnings

from embodichain.gen_sim.action_agent_pipeline.defaults import (
CONVEX_HULL_DEFAULTS,
DEFAULT_MAX_EPISODES,
DEFAULT_MAX_EPISODE_STEPS,
DEFAULT_SURFACE_RELEASE_CLEARANCE,
Expand Down Expand Up @@ -843,7 +844,7 @@ def _build_arrangement_line_bundle(
max_convex_hull_num=(
_moved_rigid_object_max_convex_hull_num(obj)
if obj.source_uid in moved_source_uids
else 1
else CONVEX_HULL_DEFAULTS["extra_rigid"]
),
mesh_normalizer=mesh_normalizer,
)
Expand Down Expand Up @@ -1074,7 +1075,7 @@ def _build_stacking_bundle(
else (
_container_rigid_object_max_convex_hull_num(obj)
if obj.source_uid == spec.anchor_source_uid
else 1
else CONVEX_HULL_DEFAULTS["extra_rigid"]
)
),
mesh_normalizer=mesh_normalizer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ physics:
static_friction: 0.95
dynamic_friction: 0.9
restitution: 0.01
max_convex_hull_num: 1
rigid_object:
mass: 0.2
static_friction: 0.95
Expand All @@ -115,6 +114,7 @@ physics:
container: 24
moved: 16
extra_rigid: 1
table: 1

grasp:
affordance_stabilization_steps: 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from typing import Any

from embodichain.gen_sim.action_agent_pipeline.defaults import (
CONVEX_HULL_DEFAULTS,
DEFAULT_TASK_NAME,
generation_defaults_section,
)
Expand Down Expand Up @@ -81,22 +82,12 @@
_VISUAL_MATERIAL_DEFAULTS = generation_defaults_section("visual_material")
_BACKGROUND_DEFAULTS = _PHYSICS_DEFAULTS["background"]
_RIGID_OBJECT_DEFAULTS = _PHYSICS_DEFAULTS["rigid_object"]
_CONVEX_HULL_DEFAULTS = _PHYSICS_DEFAULTS["convex_hulls"]
_TABLE_VISUAL_MATERIAL_DEFAULTS = _VISUAL_MATERIAL_DEFAULTS["table"]
_BACKGROUND_MAX_CONVEX_HULL_NUM = int(_BACKGROUND_DEFAULTS["max_convex_hull_num"])
_TARGET_MAX_CONVEX_HULL_NUM = int(_CONVEX_HULL_DEFAULTS["target"])
_CONTAINER_MAX_CONVEX_HULL_NUM = int(_CONVEX_HULL_DEFAULTS["container"])
_MOVED_MAX_CONVEX_HULL_NUM = int(_CONVEX_HULL_DEFAULTS["moved"])
_EXTRA_RIGID_MAX_CONVEX_HULL_NUM = int(_CONVEX_HULL_DEFAULTS["extra_rigid"])
_ROBOT_VIEW_LABEL = "robot_view"
_AUDIENCE_VIEW_LABEL = "audience_view"
_AUDIENCE_VIEW_Z_ROTATION_DEGREES = 180.0

_BACKGROUND_ATTRS = {
key: float(value)
for key, value in _BACKGROUND_DEFAULTS.items()
if key != "max_convex_hull_num"
}
_BACKGROUND_ATTRS = {key: float(value) for key, value in _BACKGROUND_DEFAULTS.items()}

_RIGID_OBJECT_ATTRS = {
key: (
Expand Down Expand Up @@ -705,10 +696,7 @@ def _make_background_config(
"body_type": "kinematic",
"init_pos": _clean_vector3(obj.config.get("init_pos", [0.0, 0.0, 0.0])),
"init_rot": _clean_vector3(obj.config.get("init_rot", [0.0, 0.0, 0.0])),
"max_convex_hull_num": _role_limited_max_convex_hull_num(
obj,
_BACKGROUND_MAX_CONVEX_HULL_NUM,
),
"max_convex_hull_num": CONVEX_HULL_DEFAULTS["table"],
}


Expand All @@ -732,10 +720,7 @@ def _make_extra_background_config(
"body_type": str(obj.config.get("body_type", "static")),
"init_pos": _clean_vector3(obj.config.get("init_pos", [0.0, 0.0, 0.0])),
"init_rot": _clean_vector3(obj.config.get("init_rot", [0.0, 0.0, 0.0])),
"max_convex_hull_num": _role_limited_max_convex_hull_num(
obj,
_BACKGROUND_MAX_CONVEX_HULL_NUM,
),
"max_convex_hull_num": CONVEX_HULL_DEFAULTS["table"],
}
return config

Expand All @@ -753,7 +738,7 @@ def _make_target_object_config(
obj,
runtime_uid,
target_scale,
max_convex_hull_num=_TARGET_MAX_CONVEX_HULL_NUM,
max_convex_hull_num=CONVEX_HULL_DEFAULTS["target"],
mesh_fpath=replacement.mesh_path if replacement else None,
mesh_normalizer=mesh_normalizer,
)
Expand All @@ -773,10 +758,7 @@ def _make_container_object_config(
obj,
runtime_uid,
body_scale,
max_convex_hull_num=_role_limited_max_convex_hull_num(
obj,
_CONTAINER_MAX_CONVEX_HULL_NUM,
),
max_convex_hull_num=CONVEX_HULL_DEFAULTS["container"],
mesh_normalizer=mesh_normalizer,
)

Expand Down Expand Up @@ -850,10 +832,7 @@ def _make_extra_rigid_object_config(
obj,
runtime_uid or _normalize_runtime_uid(obj.source_uid),
body_scale,
max_convex_hull_num=_role_limited_max_convex_hull_num(
obj,
_EXTRA_RIGID_MAX_CONVEX_HULL_NUM,
),
max_convex_hull_num=CONVEX_HULL_DEFAULTS["extra_rigid"],
mesh_normalizer=mesh_normalizer,
)
config["body_type"] = "dynamic"
Expand All @@ -869,19 +848,12 @@ def _make_relative_rigid_object_config(
max_convex_hull_num: int,
mesh_normalizer: GlbGeometryNormalizer,
) -> dict[str, Any]:
if max_convex_hull_num == _TARGET_MAX_CONVEX_HULL_NUM:
resolved_max_convex_hull_num = max_convex_hull_num
else:
resolved_max_convex_hull_num = _role_limited_max_convex_hull_num(
obj,
max_convex_hull_num,
)
config = _make_rigid_object_config(
scene_dir,
obj,
runtime_uid,
body_scale,
max_convex_hull_num=resolved_max_convex_hull_num,
max_convex_hull_num=max_convex_hull_num,
mesh_normalizer=mesh_normalizer,
)
config["body_type"] = "dynamic"
Expand Down Expand Up @@ -919,24 +891,14 @@ def _make_rigid_object_config(
return config


def _role_limited_max_convex_hull_num(
obj: _SceneObject,
role_max_convex_hull_num: int,
) -> int:
source_max_convex_hull_num = obj.config.get("max_convex_hull_num")
if source_max_convex_hull_num is None:
return role_max_convex_hull_num
return max(1, min(int(source_max_convex_hull_num), role_max_convex_hull_num))


def _moved_rigid_object_max_convex_hull_num(obj: _SceneObject) -> int:
def _moved_rigid_object_max_convex_hull_num(_obj: _SceneObject) -> int:
"""Return the configured convex-decomposition limit for a moved object."""
return _role_limited_max_convex_hull_num(obj, _MOVED_MAX_CONVEX_HULL_NUM)
return CONVEX_HULL_DEFAULTS["moved"]


def _container_rigid_object_max_convex_hull_num(obj: _SceneObject) -> int:
def _container_rigid_object_max_convex_hull_num(_obj: _SceneObject) -> int:
"""Return the configured convex-decomposition limit for a container."""
return _role_limited_max_convex_hull_num(obj, _CONTAINER_MAX_CONVEX_HULL_NUM)
return CONVEX_HULL_DEFAULTS["container"]


def _relative_rigid_object_max_convex_hull_num(
Expand All @@ -948,15 +910,14 @@ def _relative_rigid_object_max_convex_hull_num(
placement.relation == "inside"
and runtime_uid == placement.reference_runtime_uid
):
return _CONTAINER_MAX_CONVEX_HULL_NUM
task_uids = {
uid
for placement in spec.placements
for uid in (placement.moved_runtime_uid, placement.reference_runtime_uid)
}
if runtime_uid in task_uids:
return _TARGET_MAX_CONVEX_HULL_NUM
return _EXTRA_RIGID_MAX_CONVEX_HULL_NUM
return CONVEX_HULL_DEFAULTS["container"]
if any(runtime_uid == placement.moved_runtime_uid for placement in spec.placements):
return CONVEX_HULL_DEFAULTS["moved"]
if any(
runtime_uid == placement.reference_runtime_uid for placement in spec.placements
):
return CONVEX_HULL_DEFAULTS["target"]
return CONVEX_HULL_DEFAULTS["extra_rigid"]


def _relative_static_background_max_convex_hull_num(
Expand All @@ -968,8 +929,8 @@ def _relative_static_background_max_convex_hull_num(
placement.relation == "inside"
and runtime_uid == placement.reference_runtime_uid
):
return _CONTAINER_MAX_CONVEX_HULL_NUM
return _BACKGROUND_MAX_CONVEX_HULL_NUM
return CONVEX_HULL_DEFAULTS["container"]
return CONVEX_HULL_DEFAULTS["table"]


def _make_shape_config(
Expand Down
90 changes: 85 additions & 5 deletions tests/gen_sim/action_agent_pipeline/test_generation_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
from __future__ import annotations

from pathlib import Path
from types import SimpleNamespace

import pytest

from embodichain.gen_sim.action_agent_pipeline.defaults import (
ACTION_AGENT_CONFIG_DEFAULTS,
CONVEX_HULL_DEFAULTS,
DEFAULT_MAX_EPISODES,
DEFAULT_MAX_EPISODE_STEPS,
DEFAULT_SURFACE_RELEASE_CLEARANCE,
Expand All @@ -30,8 +32,12 @@
generation_defaults_section,
)
from embodichain.gen_sim.action_agent_pipeline.generation.config_blocks import (
_container_rigid_object_max_convex_hull_num,
_make_background_config,
_make_extra_rigid_object_config,
_make_target_object_config,
_moved_rigid_object_max_convex_hull_num,
_relative_rigid_object_max_convex_hull_num,
)
from embodichain.gen_sim.action_agent_pipeline.generation.config_types import (
_SceneObject,
Expand Down Expand Up @@ -79,7 +85,14 @@ def test_generation_defaults_expose_required_hyperparameter_sections() -> None:
}

assert expected_sections <= ACTION_AGENT_CONFIG_DEFAULTS.keys()
assert "moved" in ACTION_AGENT_CONFIG_DEFAULTS["physics"]["convex_hulls"]
convex_hulls = ACTION_AGENT_CONFIG_DEFAULTS["physics"]["convex_hulls"]
assert {
"target",
"container",
"moved",
"extra_rigid",
"table",
} == set(convex_hulls)


def test_affordance_stabilization_steps_is_a_non_negative_integer() -> None:
Expand Down Expand Up @@ -111,21 +124,88 @@ def test_generated_rigid_object_uses_complete_physics_defaults(
assert {
key: config["attrs"][key] for key in _EXPECTED_RIGID_OBJECT_PHYSICS
} == _EXPECTED_RIGID_OBJECT_PHYSICS
assert config["max_convex_hull_num"] == CONVEX_HULL_DEFAULTS["target"]


def test_generation_defaults_section_rejects_unknown_section() -> None:
with pytest.raises(ValueError, match="must be a mapping"):
generation_defaults_section("missing")


def test_moved_convex_hull_limit_is_loaded_from_defaults() -> None:
moved_default = ACTION_AGENT_CONFIG_DEFAULTS["physics"]["convex_hulls"]["moved"]
def test_convex_hull_limits_are_loaded_from_yaml() -> None:
convex_hulls = ACTION_AGENT_CONFIG_DEFAULTS["physics"]["convex_hulls"]

assert CONVEX_HULL_DEFAULTS["target"] == convex_hulls["target"]
assert CONVEX_HULL_DEFAULTS["container"] == convex_hulls["container"]
assert CONVEX_HULL_DEFAULTS["moved"] == convex_hulls["moved"]
assert CONVEX_HULL_DEFAULTS["extra_rigid"] == convex_hulls["extra_rigid"]
assert CONVEX_HULL_DEFAULTS["table"] == convex_hulls["table"]


def test_scene_role_limits_override_source_convex_hull_limit(tmp_path: Path) -> None:
moved_object = _SceneObject("cube", "rigid_object", {})
source_limited_object = _SceneObject(
"cup",
"rigid_object",
{"max_convex_hull_num": 4},
)
table = _SceneObject(
"table",
"background",
{"shape": {"shape_type": "Box"}, "max_convex_hull_num": 4},
)
extra = _SceneObject(
"spoon",
"rigid_object",
{"shape": {"shape_type": "Box"}, "max_convex_hull_num": 4},
)
normalizer = GlbGeometryNormalizer(output_dir=tmp_path / "normalized")
table_config = _make_background_config(tmp_path, table, normalizer)
extra_config = _make_extra_rigid_object_config(
tmp_path,
extra,
[1.0, 1.0, 1.0],
normalizer,
)

assert _moved_rigid_object_max_convex_hull_num(moved_object) == moved_default
assert _moved_rigid_object_max_convex_hull_num(source_limited_object) == 4
assert _moved_rigid_object_max_convex_hull_num(moved_object) == (
CONVEX_HULL_DEFAULTS["moved"]
)
assert _moved_rigid_object_max_convex_hull_num(source_limited_object) == (
CONVEX_HULL_DEFAULTS["moved"]
)
assert _container_rigid_object_max_convex_hull_num(source_limited_object) == (
CONVEX_HULL_DEFAULTS["container"]
)
assert table_config["max_convex_hull_num"] == CONVEX_HULL_DEFAULTS["table"]
assert extra_config["max_convex_hull_num"] == CONVEX_HULL_DEFAULTS["extra_rigid"]


def test_relative_object_roles_use_distinct_yaml_limits() -> None:
spec = SimpleNamespace(
placements=(
SimpleNamespace(
relation="inside",
moved_runtime_uid="apple",
reference_runtime_uid="basket",
),
SimpleNamespace(
relation="on",
moved_runtime_uid="cup",
reference_runtime_uid="pad",
),
)
)

assert _relative_rigid_object_max_convex_hull_num("basket", spec) == (
CONVEX_HULL_DEFAULTS["container"]
)
assert _relative_rigid_object_max_convex_hull_num("apple", spec) == (
CONVEX_HULL_DEFAULTS["moved"]
)
assert _relative_rigid_object_max_convex_hull_num("pad", spec) == (
CONVEX_HULL_DEFAULTS["target"]
)
assert _relative_rigid_object_max_convex_hull_num("spoon", spec) == (
CONVEX_HULL_DEFAULTS["extra_rigid"]
)
Loading