Add EnvGraphSpec to ArenaEnv method#724
Conversation
There was a problem hiding this comment.
Code Review
Thanks for adding the to_arena_env() method and the comprehensive conversion utilities! This is a substantial addition that bridges the gap between declarative graph specs and runtime environment instances. Below are my observations:
✅ Strengths
-
Clean API Design: The
to_arena_env()method onArenaEnvGraphSpecprovides a natural, discoverable entry point. TheTYPE_CHECKINGguard for the import keeps the module dependency graph clean. -
Extensible Task Resolution: The task factories mechanism allows custom task types while still supporting class name and module discovery fallbacks. The resolution order (import path → class name → module stem) is intuitive.
-
Well-Structured Utilities: The conversion logic is broken into focused helper functions (
_select_state_spec,_instantiate_node_assets,_apply_state_spatial_constraints, etc.), making the code readable and testable. -
Good Test Coverage: The new test case exercises sequential task composition and validates the full conversion pipeline through a realistic YAML fixture.
🔍 Suggestions & Questions
1. Consider documenting the task_args aliasing behavior
The _align_task_kwargs function does clever fuzzy matching of YAML keys to constructor parameters (lines 357-390). While useful for YAML ergonomics, this implicit behavior could surprise users when debugging. Consider either:
- Adding a note in docstrings about which aliases are supported, or
- Logging/warning when a fuzzy match is used
2. _discover_task_classes does package-wide scanning
If the direct module candidates fail, the fallback scans all modules under isaaclab_arena.tasks. This could become slow as the task library grows. Is caching viable here, or is this path rarely hit in practice?
3. Potential edge case in _match_task_arg_to_parameter
The substring matching (normalized_key in _normalize_identifier(name)) could yield unexpected results for short keys. For example, a task arg "x" would match any parameter containing the letter "x". The narrowing to required params helps, but it might be worth documenting or tightening this.
4. The IN spatial constraint raises NotImplementedError
Noticed that ArenaEnvGraphSpatialConstraintType.IN is defined but raises at conversion time. The TODO comment references May 21—is there a tracking issue for this, or should one be created?
5. Minor import ordering
In arena_env_graph_spec.py, the diff shows yaml import moved below from collections.abc which deviates from the typical stdlib-first ordering in this codebase. Not blocking, just a style nit.
🧪 Testing
The new test test_arena_env_graph_conversion_builds_sequential_pick_and_place_task is well-structured. One suggestion: consider adding a negative test for the IN constraint NotImplementedError to lock in the expected behavior until solver support lands.
Overall, this is a solid PR that adds valuable functionality. The suggestions above are minor refinements. Happy to discuss any of these points!
Update (53a0a44)
Thanks for the follow-up commit! This update addresses several important areas:
✅ What's Improved
-
API Simplification: Removed the
task_factoriesparameter fromto_arena_env()andbuild_arena_env_from_graph_spec(). This simplifies the public API—users now rely on task class discovery rather than custom factories. -
Validation Before Instantiation: Added comprehensive pre-conversion validation via
_validate_graph_spec_for_conversion():_validate_spatial_constraint_shapes()ensures proper child node arity for each constraint type_validate_task_arg_node_references()catches dangling node references in task args early_validate_initial_state_supported_for_conversion()prevents unsupportedINconstraints from reaching the runtime
-
Expanded Test Coverage: Three new validation tests added:
test_arena_env_graph_conversion_validates_references_before_instantiationtest_arena_env_graph_conversion_validates_relationship_shape_before_instantiationtest_arena_env_graph_conversion_validates_task_arg_node_references_before_instantiation
-
Code Consolidation:
- Unified
_normalize_task_class_nameand_normalize_task_module_nameinto single_normalize_task_name() - Cleaned up the test data by consolidating
two_pick_and_place_maple_table_env_graph.yamlinto the existing test fixture - Tightened error messages for better debugging
- Unified
-
TYPE_CHECKING Guards: Expanded use of
TYPE_CHECKINGimports in the conversion utils to keep the module lean at runtime.
🔍 Minor Observations
- The validation now catches my earlier suggestion #4 (the
INconstraint issue) with a clear assertion before hitting the runtimeNotImplementedError. - Good use of early-exit validation pattern—fail fast with descriptive errors rather than cryptic runtime failures during asset instantiation.
🧪 Test Fixture Updates
The pick_and_place_maple_table_env_graph.yaml now includes the second task and third state spec inline, which makes the sequential task test self-contained. The removed two_pick_and_place_maple_table_env_graph.yaml was redundant.
No blocking concerns. This is a nice cleanup that improves both usability and debuggability. 👍
Update (f28226a): Reviewed 4 new commits (03410c8 → f28226a).
Changes Since Last Review
These commits are a significant cleanup and simplification pass:
-
API Simplification: Removed
state_spec_idparameter fromto_arena_env()— now always uses the first state spec. This is cleaner but removes flexibility; the TODO notes a future aggregation approach. -
Refactored
is_unary()in Relations: Movedis_unary()fromRelationBaseto individual subclasses (UnaryRelation,IsAnchor,RandomAroundSolution,RotateAroundSolution).⚠️ Note: If external code subclassesRelationBasedirectly without implementingis_unary(), it will now fail withAttributeError. Since these relations are likely internal, this is probably fine, but worth a note in release/migration docs if public. -
Removed Helper Indirection:
_select_state_spec(),_select_embodiment(), and_relation_params_for_constraint()are gone — logic is inlined or moved upstream. The upstream contract now expectsconstraint.paramsto arrive in constructor-ready form (no more Side enum conversion or position_xyz unpacking). -
Hoisted Imports: Several imports moved from inside functions to module-level. Trades laziness for clarity; should be fine given these modules are already in use when conversion runs.
-
Extensive Docstring Improvements:
arena_env_graph_task_conversion_utils.pyandgraph_spec_utils.pynow have detailed docstrings with examples. Nice for discoverability. -
Test Adjustment: Removed
test_arena_env_graph_conversion_validates_selected_initial_state_before_instantiationsince thestate_spec_idparam no longer exists.
Assessment
No new bugs spotted. The simplification is reasonable — removing state_spec_id reduces API surface while the TODO signals the intended future direction. The is_unary() change is the most notable from a compatibility standpoint but appears safe for internal usage.
✅ New commits look good.
Update (4d10198): Reviewed incremental changes.
Changes
- Inlined helper functions: Removed
_child_asset(),_add_relation(),_set_initial_pose()— logic now directly calls asset methods. Cleaner code, relies on upstream validation to catch issues early. - Single-pass
OBJECT_REFERENCEinstantiation: Consolidated into main loop with documented upstream contract (nodes must be ordered parent-first). Avoids second iteration. - Module-level imports: Hoisted
isaaclab_arena.tasks,SequentialTaskBase,TaskBaseimports. - Minor: Removed unused
ArenaEnvGraphSpatialConstraintSpecimport and redundanttuple()wrapping.
Assessment
Continuation of the cleanup theme. No new issues introduced. ✅
Update (b0ae704): Reviewed incremental changes.
Changes
This commit is a major API simplification that removes the fuzzy/loose matching behavior:
-
Removed fuzzy
task_argsmatching —_align_task_kwargs()and related helpers are gone. Task arg keys must now exactly match constructor parameter names. -
Removed task type loose resolution —
_normalize_task_name(),_task_module_candidates(), and module-stem matching removed. Tasktype:in YAML must now be the exact class name (e.g.,PickAndPlaceTask) or a qualified import path. -
Removed auto-injection of
instance_name— upstream must now emitinstance_nameexplicitly innode.paramswhen needed. -
Removed
assert_task_arg_node_references_exist()validation — node reference validation now happens at runtime rather than spec construction. -
Test fixtures updated to use exact names (
pick_up_objectinstead ofobject,PickAndPlaceTaskinstead ofpick_and_place).
Assessment
This directly addresses my earlier suggestions (#1-#3 from the original review) by removing the complexity entirely rather than documenting it. The strict upstream contract approach is cleaner and more predictable.
No new issues. ✅
Update (ced545a): Reviewed incremental changes.
Changes
This commit is a refactoring to break circular imports by extracting types into a dedicated module:
-
New
arena_env_graph_types.pymodule: Contains all the pure-data schema types (enums and dataclasses) that were previously inarena_env_graph_spec.py:- Enums:
ArenaEnvGraphNodeType,ArenaEnvGraphSpatialConstraintType,ArenaEnvGraphTaskConstraintType - Dataclasses:
ArenaEnvGraphNodeSpec,ArenaEnvGraphObjectReferenceNodeSpec,ArenaEnvGraphSpatialConstraintSpec,ArenaEnvGraphTaskConstraintSpec,ArenaEnvGraphStateSpec,ArenaEnvGraphTaskSpec
- Enums:
-
arena_env_graph_spec.pyslimmed down: Now imports/re-exports types via__all__for backward compatibility. Thebuild_arena_env_from_graph_specimport is hoisted to module level (removing the lazy import into_arena_env()). -
Import path updates:
arena_env_graph_conversion_utils.py,arena_env_graph_task_conversion_utils.py, andgraph_spec_utils.pynow import types from the newarena_env_graph_typesmodule. -
Test fixture adjustments: Node indices updated in validation tests due to node reordering (background now at index 1, object_reference at index 2). Task type updated to exact name
PickAndPlaceTask.
Assessment
Clean separation of concerns:
- Types module: Pure data (no behavior, no dependencies on conversion logic)
- Spec module: Parsing, validation, and conversion entry point
This breaks the potential circular import chain (conversion_utils → spec → conversion_utils) while maintaining backward compatibility via re-exports. No new issues. ✅
Update (f020c7f): Reviewed 1 new commit (ced545a → f020c7f).
Changes
This is a substantial infrastructure commit that introduces relation and task registries with discovery-time registration:
1. New Registry Infrastructure
ObjectRelationLibraryRegistryandTaskRegistryadded toregistries.py- Both extend the base
Registrypattern with typed accessor methods ensure_assets_registered()now importsrelations.relationsandtasksmodules to trigger decorator registration
2. Relation Registration System
@register_object_relationdecorator inregister.py- All relation classes (
NextTo,On,IsAnchor,AtPosition,PositionLimits,RandomAroundSolution,RotateAroundSolution) now have anameclass attribute and are registered is_unary()method added as@staticmethodon each relation class for proper polymorphic dispatch
3. Task Registration System
@register_taskdecorator inregister.py- All task classes (
PickAndPlaceTask,LiftObjectTask,NoTask, etc.) are now decorated with@register_task - Registry keys use class
__name__directly (e.g.,"PickAndPlaceTask")
4. Graph Spec Validation Enhancement
assert_spatial_constraint_shapes()added tograph_spec_utils.py— validates that each spatial constraint has the correct parent/child arity based on the relation'sis_unary()methodrelation_class_for_spatial_constraint_type()helper resolves enum to relation class via the new registry- Validation is now called in
ArenaEnvGraphSpec.validate()and invoked automatically infrom_dict()
5. Relation Solver Interface Extraction
- New
relation_solver_interface.pymodule — extracts placement orchestration logic fromArenaEnvBuilder._solve_relations() solve_and_apply_relation_placement()is the new public API entry pointPooledObjectPlacerpropertypool_sizeadded for introspection
6. Lightwheel Timeout/Retry Wrapper
- New
lightwheel_utils.pymodule withacquire_lightwheel_asset()helper - Wraps Lightwheel SDK calls with configurable timeout, retry attempts, and delay between retries
- Applied to
RobocasaKitchenBackgroundand severalobject_library.pyassets
7. Documentation Updates
- Static apple workflow docs significantly expanded with dual-viewport monitoring, pre-trained model downloads, and single-GPU training config
- Links updated to pinned GR00T commit SHAs
- gh-pages.yml simplified to only build from
mainbranch
Assessment
Strengths:
- The registry approach enables the graph-spec-to-env conversion to resolve task/relation types at runtime without import-time coupling
assert_spatial_constraint_shapes()provides excellent fail-fast validation — catches arity errors at YAML load time- Lightwheel retry wrapper is a pragmatic addition for flaky network calls
- Documentation improvements are substantial and user-friendly
Minor Observations:
- The
_assets_registeredflag is now set before imports (line 281) to handle re-entrant decorator calls — good defensive programming, but the rollback-on-exception pattern adds complexity. Consider adding a comment explaining when this can actually trigger. relation_class_for_spatial_constraint_type()returnsNoneforAT_POSEandIN— the caller must handle these special cases. The TODO notes are helpful here.
Tests:
- New tests for
TaskRegistry,relation_solver_interface, and Lightwheel retry are comprehensive test_arena_env_graph_spec.pyupdated to validate the newrelation_class_for_spatial_constraint_type()behavior
No blocking issues. This is excellent infrastructure work that sets up clean extensibility for future task and relation types. ✅
Update (48f0be3): Reviewed incremental changes (4b56480 → 48f0be3).
Changes
This is a substantial commit adding several important features:
1. CompositeTaskBase — Order-Independent Composite Tasks
A new base class CompositeTaskBase is introduced alongside the existing SequentialTaskBase:
- Order-independent completion: Unlike
SequentialTaskBasewhich requires subtasks to complete in sequence (state machine),CompositeTaskBaseallows subtasks to complete in any order SequentialTaskBasenow inherits fromCompositeTaskBase, overriding onlycomposite_task_success_funcandreset_subtask_success_stateto add ordering logic- Both support
desired_subtask_success_statewithNoneentries for "don't care" positions - Comprehensive test coverage in
test_composite_task_base.pyandtest_composite_open_door.py
2. MetricsManager — Manager-Based Metrics Infrastructure
A significant refactor of the metrics system:
- New
MetricsManagerclass inmetrics_manager.py— follows the IsaacLab manager pattern - New
MetricTermCfgconfigclass — decouples metric configuration from recorder terms MetricBase.get_metric_term_cfg()replacescompute_metric_from_recording()— metrics now return a config rather than computing directlyIsaacLabArenaManagerBasedRLEnvsplit into env + cfg modules:isaaclab_arena_manager_based_env.py→ env class withload_managers()andcompute_metrics()isaaclab_arena_manager_based_env_cfg.py→ config class withArenaPhysicsCfg
- All metrics (
SuccessRateMetric,ObjectMovedRateMetric,RevoluteJointMovedRateMetric,SubtaskSuccessRateMetric) updated to the new pattern
3. Task Registry Infrastructure Completion
task_library.pyadded — mirrorsobject_library.pypattern for lazy task class registration- All task classes now decorated with
@register_task TaskRegistry().get_task_by_name()enables graph-spec task resolution by class name- New test
test_task_registry.pyvalidates all expected tasks are discoverable
4. Graph Spec Enhancements
ArenaEnvGraphSpec.validate()extracted as public method — allows post-mutation validation- Parent node ordering validation — nodes must be ordered parent-before-child (matches instantiation order)
assert_spatial_constraint_shapes()now validates arity at parse time- Tests expanded to cover validation after mutation scenarios
5. Task Conversion Utilities
- New
arena_env_graph_task_conversion_utils.py— dedicated module for task spec → task instance conversion find_node_ref_params_in_signature()introspects task__init__forAsset/AffordanceBasetyped params_resolve_node_refs_in_task_args()swaps node-id strings for live assets at instantiation time- Comprehensive test coverage in
test_arena_env_graph_task_conversion.py
6. Minor Fixes & Cleanup
Poserotation field:rotation_wxyz→rotation_xyzwincompile_env_notebook.pyPickAndPlaceTask.force_threshold: Default changed from 1.0 → 0.1- Import path fixes:
IsaacLabArenaManagerBasedRLEnvCfgimports updated across codebase - Removed
compute_metrics()function frommetrics.py— replaced by manager method relation_class_for_spatial_constraint_type()now exposed ingraph_spec_utils.py- Sequential task tests refactored to use direct mocks rather than full env simulation
Assessment
Strengths:
- The
CompositeTaskBase/SequentialTaskBaseinheritance hierarchy is elegant — shared logic in base, ordering constraint in subclass - The metrics refactor brings Arena into alignment with IsaacLab's manager-based architecture
- Task registry enables fully declarative task instantiation from YAML specs
- Excellent test coverage for the new composite/sequential success logic, including edge cases with
Noneindesired_subtask_success_state
Minor Observations:
- The
force_thresholddefault change (1.0 → 0.1) could affect existing pick-and-place evaluations — may warrant a note - The
rotation_wxyz→rotation_xyzwfix in the notebook suggests there may be other places using the wrong convention (worth a grep to verify)
No blocking issues. This is a substantial and well-structured addition to the Arena infrastructure. ✅
Update (bcb3b2e): Reviewed incremental changes (48f0be3 → bcb3b2e).
Changes
This commit is a final refactoring pass with significant code reorganization:
1. Module Extraction — Types and Conversion Utilities Split
arena_env_graph_types.py— Now a standalone module containing all pure-data schema classes. The docstring explicitly states this is to break circular imports between spec and conversion modules.arena_env_graph_task_conversion_utils.py— New module dedicated to task-spec → live-task resolution. Containsbuild_task_from_specs(),find_node_ref_params_in_signature(), and the node-id resolution logic.arena_env_graph_conversion_utils.py— Slimmed down to focus on env/scene/asset instantiation; task conversion delegated to the new module.
2. Relation is_unary() as @staticmethod
All relation classes now declare is_unary() as a @staticmethod returning a literal bool:
UnaryRelation,IsAnchor,RandomAroundSolution,RotateAroundSolution→return TrueRelation(binary base) →return False
This enables assert_spatial_constraint_shapes() to call relation_class.is_unary() without instantiating the class.
3. Enhanced Validation in graph_spec_utils.py
assert_references_exist()now enforces parent-before-child ordering — nodes must be declared in topological order so the conversion single-pass instantiation works.assert_spatial_constraint_shapes()validates parent/child arity for each constraint using the newis_unary()dispatch, with special-case handling forAT_POSEandIN.
4. Lazy Import in relations.py
RotateAroundSolution.get_rotation_xyzw() now imports torch inside the method body instead of at module level. This keeps the schema modules torch-free for lightweight consumers.
5. Comprehensive Test Additions
test_arena_env_graph_conversion.py— End-to-end subprocess test for SimulationApp-requiring conversion. Deliberately isolated fromtest_arena_env_graph_spec.pyto avoidpxrimport conflicts.test_arena_env_graph_task_conversion.py— Unit tests for the new task conversion utilities: signature introspection, node-id resolution, sequential task wrapping.test_arena_env_graph_spec.py— Two new tests for post-mutationvalidate()calls, ensuring programmatically modified specs can be re-validated.
6. Documentation Updates
step_2_teleoperation.rstandstep_3_policy_training.rst— Simplified language instruction from verbose ("Pick up the apple from the shelf...") to concise ("move the apple to the plate").g1_static_apple_config.yamlandg1_static_apple_gr00t_closedloop_config.yaml— Updated to match the new instruction.
Assessment
This is a clean wrap-up commit that:
- Breaks circular imports with proper module separation
- Makes
is_unary()a class-level (not instance-level) property - Adds defensive validation for node ordering
- Includes comprehensive test coverage for the new task conversion logic
- Aligns documentation/config with simpler language instructions
No blocking issues. ✅
There was a problem hiding this comment.
Thanks @xyao-nv for the draft. Overall looks ok, but isaaclab_arena/environments/arena_env_graph_conversion_utils.py is a long file and could potentially be clean up to make it simpler. Especially in terms of spec validation, many checks should happen at the spec construction step, not at the spec->arena env conversion step.
|
Changes made:
|
alexmillane
left a comment
There was a problem hiding this comment.
A couple of nits, but my main comment is that we should add registries for tasks and constraints to avoid some stuff in here that makes the graph decoding not extensible out-of-source.
f020c7f to
4b56480
Compare
alexmillane
left a comment
There was a problem hiding this comment.
Could of nits.
LGTM!
4b56480 to
48f0be3
Compare
48f0be3 to
bcb3b2e
Compare
Greptile SummaryThis PR adds
Confidence Score: 2/5Not safe to merge: moving PoseRange to TYPE_CHECKING breaks RandomAroundSolution.to_pose_range_centered_at at runtime. The PoseRange import in relations.py was moved into the TYPE_CHECKING block, but the class is instantiated as return PoseRange(...) inside RandomAroundSolution.to_pose_range_centered_at. At runtime TYPE_CHECKING is False, so PoseRange is never defined and every call to that method raises NameError. The rest of the conversion pipeline is well-structured, but this regression in a commonly-used relation utility needs to be fixed before merging. isaaclab_arena/relations/relations.py — the PoseRange import regression. isaaclab_arena/environments/arena_env_graph_conversion_utils.py — missing zero-embodiment guard in _partition_nodes_into_embodiment_and_scene. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["ArenaEnvGraphSpec.from_yaml / from_dict"] --> B["validate()"]
B --> C["assert_unique_ids"]
B --> D["assert_references_exist + parent-before-child ordering"]
B --> E["assert_spatial_constraint_shapes (new)"]
F["ArenaEnvGraphSpec.to_arena_env()"] --> G["build_arena_env_from_graph_spec"]
G --> H["_instantiate_assets_from_nodes"]
G --> I["_attach_spatial_constraints_to_assets"]
G --> J["_partition_nodes_into_embodiment_and_scene"]
J --> J1{"EMBODIMENT count"}
J1 -->|"2 or more"| J2["AssertionError"]
J1 -->|"0 - silent bug"| J3["embodiment = None"]
J1 -->|"exactly 1"| J4["OK"]
G --> K["build_task_from_specs"]
K --> L{"task count"}
L -->|"0"| M["None"]
L -->|"1"| N["single task instance"]
L -->|"more than 1"| O["SequentialTaskBase"]
G --> P["IsaacLabArenaEnvironment"]
|
| if TYPE_CHECKING: | ||
| from isaaclab_arena.assets.object_base import ObjectBase | ||
| from isaaclab_arena.utils.pose import PoseRange |
There was a problem hiding this comment.
Runtime
NameError on to_pose_range_centered_at: PoseRange was moved to the TYPE_CHECKING block, but it is used as a constructor in the function body (return PoseRange(...)). Because TYPE_CHECKING is False at runtime, PoseRange is never bound as a name, so any call to this method will raise NameError: name 'PoseRange' is not defined. The from __future__ import annotations only makes the -> PoseRange return annotation lazy — it does not protect the runtime constructor call.
| if TYPE_CHECKING: | |
| from isaaclab_arena.assets.object_base import ObjectBase | |
| from isaaclab_arena.utils.pose import PoseRange | |
| from isaaclab_arena.utils.pose import PoseRange | |
| if TYPE_CHECKING: | |
| from isaaclab_arena.assets.object_base import ObjectBase |
| scene_assets.append(assets_by_node_id[node_spec.id]) | ||
| else: | ||
| raise ValueError(f"Unsupported node type: {node_spec.type}") | ||
| return embodiment, scene_assets |
There was a problem hiding this comment.
Missing assertion for zero-embodiment graph specs: the docstring states "Asserts exactly one EMBODIMENT node," but the code only asserts at-most-one. When no
EMBODIMENT node is present, embodiment remains None and is silently passed to IsaacLabArenaEnvironment, which will fail downstream with an opaque error.
| return embodiment, scene_assets | |
| assert embodiment is not None, "Exactly one embodiment node is required to convert a graph spec to an IsaacLabArenaEnvironment" | |
| return embodiment, scene_assets |
| else: | ||
| # Asset-typed param: resolve the single node id to its live asset. | ||
| # e.g. "pick_up_object": "cube" -> "pick_up_object": <Object: cube> | ||
| resolved_task_kwargs[param_name] = _lookup_asset_by_node_id( | ||
| raw_param_value, assets_by_node_id, task_class, param_name | ||
| ) |
There was a problem hiding this comment.
Explicit
null task-arg value for an optional node-ref param is not handled: if a YAML spec contains optional_object: null for an Asset | None-typed parameter, raw_param_value is None, param_name in raw_task_args is True, and _lookup_asset_by_node_id(None, ...) raises AssertionError: unknown node id None instead of passing None through.
| else: | |
| # Asset-typed param: resolve the single node id to its live asset. | |
| # e.g. "pick_up_object": "cube" -> "pick_up_object": <Object: cube> | |
| resolved_task_kwargs[param_name] = _lookup_asset_by_node_id( | |
| raw_param_value, assets_by_node_id, task_class, param_name | |
| ) | |
| else: | |
| # Asset-typed param: resolve the single node id to its live asset. | |
| # e.g. "pick_up_object": "cube" -> "pick_up_object": <Object: cube> | |
| # None is a valid explicit value for optional (Asset | None) params. | |
| if raw_param_value is None: | |
| resolved_task_kwargs[param_name] = None | |
| else: | |
| resolved_task_kwargs[param_name] = _lookup_asset_by_node_id( | |
| raw_param_value, assets_by_node_id, task_class, param_name | |
| ) |
Summary
Convert EnvGraph to ArenaEnv
Detailed description
ArenaEnvGraphSpec.to_arena_env()so YAML graph specs can be converted intoIsaacLabArenaEnvironment.