Skip to content

Introduce PlacementAsset#915

Open
zhx06 wants to merge 12 commits into
mainfrom
zxiao/feature/robo_placement_prototype
Open

Introduce PlacementAsset#915
zhx06 wants to merge 12 commits into
mainfrom
zxiao/feature/robo_placement_prototype

Conversation

@zhx06

@zhx06 zhx06 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Unify object and robot relation placement

Detailed description

  • Enables robot embodiments to use the existing relation solver.
  • Adds PlacementEntity and generalizes the placement pipeline from objects to entities.
  • Preserves existing object placement while adding single-root, bounding-box-based robot placement.

@zhx06 zhx06 changed the title introduce PlacementEntity Introduce PlacementEntity Jul 15, 2026
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces PlacementEntity as a shared base class for ObjectBase and EmbodimentBase, unifying the placement pipeline so robot embodiments can participate in the existing relation solver alongside scene objects. The key design is that _solve_relations() is called before get_scene_cfg(), allowing the embodiment's lazy scene-config update to pick up the solved pose.

  • New PlacementEntity base class consolidates initial_pose, relations, collision_mode, and the abstract bounding-box/mesh interface, removing duplication between ObjectBase and EmbodimentBase.
  • scene_entity_names indirection decouples the placement entity's logical name from its Isaac Lab scene name (e.g. "droid""robot"), with a new _apply_static_spawn_pose path for mixed pools where some entities cannot store per-environment poses.
  • Behavior hardening: layouts with missing non-anchor positions now raise AssertionError instead of printing a warning and silently skipping, matching the updated tests.

Confidence Score: 4/5

Safe to merge; the core placement ordering (solve before get_scene_cfg) is correct and well-tested. One method override has a narrower runtime type than its declared signature.

The placement pipeline is well-structured and the ordering guarantee (solve before scene build) correctly handles the embodiment's lazy config update. The one concrete defect is that EmbodimentBase.set_initial_pose asserts isinstance(pose, Pose) but keeps the broader Pose | PoseRange | PosePerEnv signature from its parent, so any caller using the PlacementEntity interface with a PoseRange or PosePerEnv argument would get a silent runtime AssertionError on embodiments. The current placement pipeline avoids this via supports_per_env_initial_pose(), but the type contract is broken and future callers could hit it unexpectedly.

isaaclab_arena/embodiments/embodiment_base.py — the set_initial_pose override

Important Files Changed

Filename Overview
isaaclab_arena/relations/placement_entity.py New base class centralising placement state (initial_pose, relations, collision_mode) that previously lived in ObjectBase; well-structured with clean abstract interface.
isaaclab_arena/embodiments/embodiment_base.py Adds bounding-box and placement interface to EmbodimentBase; set_initial_pose asserts a narrower runtime type than its declared signature, violating the PlacementEntity contract.
isaaclab_arena/assets/object_base.py Migrates shared placement state to PlacementEntity; adds set_placement_initial_pose that correctly patches object_cfg.init_state without triggering a new reset event.
isaaclab_arena/environments/relation_solver_interface.py Adds scene_entity_names indirection for robot/embodiment name mapping; new _apply_static_spawn_pose path handles mixed entity pools correctly; scene_entity_names assertion is stricter than needed (exact equality vs subset).
isaaclab_arena/environments/arena_env_builder.py Correctly adds embodiment relations to the placement pool and builds the scene_entity_names map; _solve_relations is called before get_scene_cfg, ensuring the solved pose is picked up by the embodiment's lazy scene-config update.
isaaclab_arena/relations/placement_events.py Adds get_pose_from_layout helper and new place_entities_from_layouts reset event; write_layout_to_sim now validates all non-anchor entities are present (hard failure) rather than silently skipping missing positions.
isaaclab_arena/assets/dummy_embodiment.py New test-only PlacementEntity for embodiment solver tests; correctly implements all abstract methods and returns False for supports_per_env_initial_pose.
isaaclab_arena/tests/test_relation_solver_embodiment.py New test file covering single-env embodiment placement and expected assertion for batched placement without per-env pose support.
isaaclab_arena/tests/test_relation_solver_interface.py Updated tests correctly reflect behavior changes: missing-position layouts now raise instead of warn, and new tests cover unique-name and complete-map assertions.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Builder as ArenaEnvBuilder
    participant RSI as relation_solver_interface
    participant Pool as PooledObjectPlacer
    participant PE as PlacementEntity
    participant Events as placement_events

    Builder->>RSI: solve_and_apply_relation_placement(entities, scene_entity_names)
    RSI->>Pool: sample_with_replacement(num_envs)
    Pool-->>RSI: layouts[]
    RSI->>PE: set_placement_initial_pose(pose) [env 0 seed]
    Note over PE: ObjectBase patches object_cfg.init_state<br/>EmbodimentBase updates self.initial_pose only
    RSI-->>Builder: EventTermCfg (reset event)
    Builder->>PE: embodiment.get_scene_cfg()
    Note over PE: Lazily applies self.initial_pose<br/>to scene_config.robot.init_state
    Note over Builder: Scene built with solved poses
    loop On every reset
        Events->>Events: solve_and_place_objects / place_entities_from_layouts
        Events->>PE: write_root_pose_to_sim(pose, env_ids)
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Builder as ArenaEnvBuilder
    participant RSI as relation_solver_interface
    participant Pool as PooledObjectPlacer
    participant PE as PlacementEntity
    participant Events as placement_events

    Builder->>RSI: solve_and_apply_relation_placement(entities, scene_entity_names)
    RSI->>Pool: sample_with_replacement(num_envs)
    Pool-->>RSI: layouts[]
    RSI->>PE: set_placement_initial_pose(pose) [env 0 seed]
    Note over PE: ObjectBase patches object_cfg.init_state<br/>EmbodimentBase updates self.initial_pose only
    RSI-->>Builder: EventTermCfg (reset event)
    Builder->>PE: embodiment.get_scene_cfg()
    Note over PE: Lazily applies self.initial_pose<br/>to scene_config.robot.init_state
    Note over Builder: Scene built with solved poses
    loop On every reset
        Events->>Events: solve_and_place_objects / place_entities_from_layouts
        Events->>PE: write_root_pose_to_sim(pose, env_ids)
    end
Loading

Reviews (1): Last reviewed commit: "introduce PlacementEntity" | Re-trigger Greptile

Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment thread isaaclab_arena/environments/relation_solver_interface.py Outdated

Args:
objects: Objects with spatial predicates that should be relation-solved.
objects: Entities with spatial predicates that should be relation-solved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, rename objects to entities?

Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
placement_entities = list(self.arena_env.scene.get_objects_with_relations())
scene_entity_names = {entity.name: entity.name for entity in placement_entities}
embodiment = self.arena_env.embodiment
if embodiment is not None and embodiment.get_relations():

@xyao-nv xyao-nv Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are modeling single-arm manipulator on a stand as a 3D bbox, does it mean it will put the robot far away from the table that may be even hard to reach? E.g. the stand footprint of droid is pretty large.
How's nextTo(Robot) being defined here? Using a boxy AABB edge?
On(Robot)? a footprint on the top face of the robot?

Shall we have some restrictions/definitions of embodiment-related relations?

Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment thread isaaclab_arena/tests/dummy_embodiment.py
Comment thread isaaclab_arena/tests/dummy_object.py

def set_initial_pose(self, pose: Pose | PoseRange | PosePerEnv) -> None:
"""Set the embodiment root pose."""
assert isinstance(pose, Pose), "Embodiments require one root Pose"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pose: Pose | PoseRange | PosePerEnv seems to contradict "assert isinstance(pose, Pose)"?

quarters = quaternion_to_90_deg_z_quarters(self.initial_pose.rotation_xyzw)
return bounding_box.rotated_90_around_z(quarters).translated(self.initial_pose.position_xyz)

def get_collision_mesh(self) -> trimesh.Trimesh | None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this a default implementation on PlacementEntity, then remove the redundant no-op implementations from EmbodimentBase and ObjectBase?

raise RuntimeError("scene_config must be populated with a `robot` before calling `set_robot_initial_pose`.")
scene_config.robot.init_state.pos = pose.position_xyz
scene_config.robot.init_state.rot = pose.rotation_xyzw
assert scene_config is not None, "scene_config.robot must be populated before setting the root pose"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assert message does not match the actual assert

raise RuntimeError("scene_config must be populated with a `robot` before calling `set_robot_initial_pose`.")
scene_config.robot.init_state.pos = pose.position_xyz
scene_config.robot.init_state.rot = pose.rotation_xyzw
assert scene_config is not None, "scene_config.robot must be populated before setting the root pose"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert scene_config is not None, "scene_config.robot must be populated before setting the root pose"
assert scene_config is not None, "scene_config must be populated before setting the root pose"

scene_config.robot.init_state.rot = pose.rotation_xyzw
assert scene_config is not None, "scene_config.robot must be populated before setting the root pose"
robot = scene_config.robot
assert robot is not None, "scene_config.robot must be populated before setting the root pose"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, can directly do assert scene_config.robot is not None, "scene_config.robot must be populated before setting the root pose",as the declared variable is not directly used

Comment thread isaaclab_arena/relations/placement_entity.py Outdated

@alexmillane alexmillane left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level review #1

Comment thread isaaclab_arena/assets/object_base.py Outdated
Comment on lines +90 to +95
def set_placement_initial_pose(self, pose: Pose) -> None:
"""Set a solved root pose without configuring an independent reset."""
self.initial_pose = pose
if self.object_cfg is not None:
self.object_cfg.init_state.pos = pose.position_xyz
self.object_cfg.init_state.rot = pose.rotation_xyzw

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why's this required?

Are there places where we want to set a pose that doesn't reset?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is designed for robots that does not have object_cfg

Comment thread isaaclab_arena/embodiments/embodiment_base.py
Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment thread isaaclab_arena/environments/arena_env_builder.py Outdated
Comment thread isaaclab_arena/environments/arena_env_builder.py Outdated
Comment thread isaaclab_arena/environments/relation_solver_interface.py Outdated
Comment on lines 156 to 170
if all(obj.supports_per_env_initial_pose() for obj in objects):
_apply_static_initial_poses(
objects=objects,
placement_pool=placement_pool,
anchor_objects_set=anchor_objects_set,
num_envs=num_envs,
)
return None
return _apply_static_spawn_pose(
objects=objects,
placement_pool=placement_pool,
anchor_objects_set=anchor_objects_set,
num_envs=num_envs,
scene_entity_names=scene_entity_names,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference in behaviour between these two branches?

Like, how does the generated scene differ? In the second case, do we, for example, lose different poses between the environments? That would be a big change in behaviour... Probably something that we don't want to do.

Could you explain what's going on here. I would add such an explanation as a (lengthy) comment. However, let's agree here on what's going on and what should happen first.

@zhx06 zhx06 Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both branches preserve per-environment variation. The reason is that objects and embodiments currently use different reset mechanisms.

The first branch handles object-only scenes, where each object’s reset event stores its per-environment pose.

The second handles scenes containing an embodiment. Robot configs store only one construction pose, so a reset event writes each environment’s saved layout at reset.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we need to handle object vs embodiment differently at relation_solver_interface.py level, it feels like we are missing some abstraction layers at PlacementAsset/EmbodimentBase class so we can unify the interface with ObjectBase. The root cause appears to be embodiment's rejecting PosePerEnv. Is this something we can extend the EmbodimentBase to support, so we provide one unified the per-env & reset pose behavior for object and embodiment?

@zhx06 zhx06 Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. The new commit unify these 2 classes. One remaining thing is how to deal with the stand of the driod.

Comment thread isaaclab_arena/environments/relation_solver_interface.py Outdated
Comment on lines +197 to +217
def _apply_static_spawn_pose(
objects: list[PlacementEntity],
placement_pool: PooledObjectPlacer,
anchor_objects_set: set[PlacementEntity],
num_envs: int,
scene_entity_names: Mapping[str, str],
) -> EventTermCfg:
"""Return a reset event that restores one fixed layout per environment."""
from isaaclab.managers import EventTermCfg

layouts = placement_pool.sample_with_replacement(num_envs)
_set_placement_initial_poses(objects, anchor_objects_set, layouts[0])
return EventTermCfg(
func=place_entities_from_layouts,
mode="reset",
params={
"objects": objects,
"layouts": layouts,
"scene_entity_names": scene_entity_names,
},
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok this is related to my comment above. Seems like we're introducing new behaviour that is degraded from what was there in some circumstances.

It seems like now, we may end up with the same poses per environment, rather than the varied poses we had before. That is a big change in behaviour no?

Is that coming because we can't set the robot pose per-env?

@zhx06 zhx06 Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first layout is used only to initialize the single-pose scene configuration during construction. The poses will be applied separately for each environment.

from isaaclab_arena.utils.bounding_box import AxisAlignedBoundingBox


class PlacementEntity(Asset, ABC):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I commented above, I'm not sure we should introduce the term Entity, because then we'll have three overlapping things Assets Objects and Entities.

This class is always intended to be combined with something deriving from Asset no? In that case this class is a "mixin" to add functionality for placement no? What do you think about calling this class Placeable. That indicates that this class adds place-ability to another object?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with the naming. Entitiy is quite vague as well. Placeable would be a good fit, however already taken by one of our affordances IsaacLab-Arena/isaaclab_arena/affordances/placeable.py
Arguable Placeable would be a better fit for this use case here as the current Placeable affordance is just checking for orientation via place_upright()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed the class to be PlacementAsset.

@zhx06
zhx06 force-pushed the zxiao/feature/robo_placement_prototype branch 2 times, most recently from bfdfee6 to 62d990d Compare July 22, 2026 03:10
@zhx06
zhx06 force-pushed the zxiao/feature/robo_placement_prototype branch from 62d990d to cc32f4e Compare July 22, 2026 23:51
@zhx06 zhx06 changed the title Introduce PlacementEntity Introduce PlacementAssets Jul 23, 2026
@zhx06 zhx06 changed the title Introduce PlacementAssets Introduce PlacementAsset Jul 23, 2026
Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment thread isaaclab_arena/tests/dummy_object.py
Comment thread isaaclab_arena/tests/test_relation_solver_embodiment.py
@zhx06
zhx06 force-pushed the zxiao/feature/robo_placement_prototype branch 3 times, most recently from f856cda to ce534d7 Compare July 24, 2026 03:40

@alexmillane alexmillane left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty much ready to go. A few small comments and questions about pose-setting.

Comment thread isaaclab_arena/assets/asset.py Outdated
Comment thread isaaclab_arena/assets/object_base.py Outdated
Comment thread isaaclab_arena/assets/object_base.py Outdated
Comment on lines +72 to +93
def set_spawn_pose(self, pose: Pose) -> None:
"""Set the scene-construction pose without rebuilding the reset event."""
assert self.object_cfg is not None, "object_cfg must be initialized before setting the spawn pose"
self._set_pose_state(pose)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we return this to a single function with a flag for create_reset_event=True

Right now, these three names: set_initial_pose, _set_pose_state, and set_spawn_pose are all basically synonyms. One cannot read the names and know what the difference is.

If we have a single function called set_initial_pose with create_reset_event=True, it's super clear to the reader what is going on.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. We now have set_initial_pose(pose, create_reset_event=True). _set_pose_state, and set_spawn_pose are gone and set_initial_pose is the public interface.

Comment on lines +101 to +110
def set_initial_pose(self, pose: Pose | PosePerEnv) -> None:
"""Store the requested base pose(s), lifted by the stand-height offset to match the spawned base."""
if isinstance(pose, PosePerEnv):
super().set_initial_pose(PosePerEnv(poses=[p.translate(self._robot_base_offset) for p in pose.poses]))
else:
super().set_initial_pose(pose.translate(self._robot_base_offset))

def set_spawn_pose(self, pose: Pose) -> None:
"""Set the scene-construction base pose, lifted by the stand-height offset like ``set_initial_pose``."""
super().set_spawn_pose(pose.translate(self._robot_base_offset))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct that this function just calls the underlying function, except that it first translates the pose by the self._robot_base_offset?

I think it probably makes sense we eventually put the stand into the USD with the robot to skip this backflipping here.

For the time being, let's go with this but:

def _translate_pose(pose: Pose | PosePerEnv):
        if isinstance(pose, PosePerEnv):
            translated_pose = PosePerEnv(poses=[p.translate(self._robot_base_offset) for p in pose.poses])
        else:
            translated_pose = pose.translate(self._robot_base_offset)
        return translated_pose

def set_initial_pose(self, pose: Pose | PosePerEnv)
    pose = _translate_pose(pose)
    super().

@zhx06 zhx06 Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. We now have _translate_pose and set_initial_pose for droid in droid.py, and add a TODO for adding stand into the USD.

Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment thread isaaclab_arena/embodiments/embodiment_base.py Outdated
Comment on lines +85 to +87
* **False** — assigns one fixed layout per environment. Object-only
scenes use per-object reset events; scenes with an embodiment use one
coordinated reset event.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Object-only scenes use per-object reset events; scenes with an embodiment use one coordinated reset event."

Is there any way we can unify this behaviour?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, their behaviors are unified now. Each non-anchor asset stores its per-env pose and owns its own reset event.

Comment thread isaaclab_arena/environments/relation_solver_interface.py Outdated
Comment on lines +98 to +99
def get_collision_mesh(self) -> trimesh.Trimesh | None:
"""Return this asset's collision mesh, or ``None`` if it has none."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an @AbstractMethod?

@zhx06 zhx06 Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not. None here means "No collision mesh, fall back to the AABB." It is supposed to stay concrete.

Comment on lines +54 to +68
def get_initial_pose(self) -> Pose | PoseRange | PosePerEnv | None:
"""Return the configured root pose."""
return self.initial_pose

def set_initial_pose(self, pose: Pose | PoseRange | PosePerEnv) -> None:
"""Set the configured root pose.

Accepts a fixed ``Pose``, a ``PoseRange`` (randomized on reset), or a ``PosePerEnv``
(a distinct pose per environment); the interpretation is subclass-specific.
"""
self.initial_pose = pose

def set_spawn_pose(self, pose: Pose) -> None:
"""Set the root pose used when constructing the scene."""
self.set_initial_pose(pose)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions appear to me to be duplicated (overridden) in the classes that this class is mixed into.

Is there a reason for that? It would be amazing if we could centralize the treatment of poses here.

Also there appears to me to be a bit of duplication between ObjectBase and EmbodimentBase. Another reason to centralize the implementation here.

Is there anything preventing us from doing that?

@zhx06 zhx06 Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there was some duplicated code here. In the newest commit, all pose-related functions share the same interface in PlaceableAsset. ObjectBase/EmbodimentBase only override the two different methods according to the actual needs.

zhx06 added 10 commits July 24, 2026 08:39
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
@zhx06
zhx06 force-pushed the zxiao/feature/robo_placement_prototype branch from ee9f659 to dedf07a Compare July 24, 2026 16:38
zhx06 added 2 commits July 24, 2026 10:12
Signed-off-by: zhx06 <zihaox@nvidia.com>
Signed-off-by: zhx06 <zihaox@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants