Georg commited on
Commit
ec12dc0
·
1 Parent(s): 4f6eb67

Add scene object extraction and update state broadcasting in mujoco_server.py

Browse files

- Introduced a new function `_get_scene_objects()` to extract position and orientation data for scene objects, specifically for UR5 scenes.
- Enhanced the `broadcast_state()` function to include scene object information in the state broadcast to connected clients.
- Updated `protocol_types.py` to define a new `SceneObject` type and included a list of scene objects in the `StateData` structure.
- Modified `README.md` to document the new `scene_objects` field, detailing its structure and usage.

Files changed (3) hide show
  1. README.md +4 -0
  2. mujoco_server.py +60 -2
  3. protocol_types.py +8 -0
README.md CHANGED
@@ -646,6 +646,10 @@ For robot arm (UR5):
646
  - Locomotion robots: Use `vx`, `vy`, `vyaw` (other fields are 0)
647
  - UR5: Use `vx`/`vy`/`vz` for Cartesian translation, `vrx`/`vry`/`vrz` for rotation, `j1`-`j6` for joint velocities, and `gripper` for gripper control
648
  - `connected_clients`: List of connected external client IDs (e.g., `["trainer_v1", "monitor"]`)
 
 
 
 
649
 
650
  *Locomotion observation fields (inside `observation`):*
651
  - `position`: Robot base position in world coordinates (x, y, z) in meters
 
646
  - Locomotion robots: Use `vx`, `vy`, `vyaw` (other fields are 0)
647
  - UR5: Use `vx`/`vy`/`vz` for Cartesian translation, `vrx`/`vry`/`vrz` for rotation, `j1`-`j6` for joint velocities, and `gripper` for gripper control
648
  - `connected_clients`: List of connected external client IDs (e.g., `["trainer_v1", "monitor"]`)
649
+ - `scene_objects`: List of scene objects with their positions and orientations. Each object has:
650
+ - `name`: Object identifier (e.g., "t_object", "t_target", "box")
651
+ - `position`: Object position in meters (x, y, z)
652
+ - `orientation`: Object orientation as quaternion (w, x, y, z)
653
 
654
  *Locomotion observation fields (inside `observation`):*
655
  - `position`: Robot base position in world coordinates (x, y, z) in meters
mujoco_server.py CHANGED
@@ -554,6 +554,54 @@ def switch_robot(robot_type, scene_name=None):
554
  env = init_g1()
555
 
556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  def broadcast_state():
558
  """Broadcast robot state to all connected WebSocket clients."""
559
  with mujoco_lock:
@@ -577,6 +625,9 @@ def broadcast_state():
577
  with external_ws_clients_lock:
578
  connected_clients = [info.get("identity", "client") for ws, info in list(client_metadata.items()) if ws in external_ws_clients]
579
 
 
 
 
580
  # UR5 has different state structure
581
  if current_robot in ("ur5", "ur5_t_push"):
582
  ee_pos = env.get_end_effector_pos()
@@ -592,8 +643,13 @@ def broadcast_state():
592
  nova_client = getattr(env, '_nova_client', None)
593
  nova_state_streaming = getattr(env, '_use_nova_state_stream', False)
594
  nova_ik = getattr(env, '_use_nova_ik', False)
595
- # Show connected if client exists (even if first state not received yet)
596
- nova_connected = nova_client is not None
 
 
 
 
 
597
 
598
  nova_available = False
599
  nova_enabled_pref = False
@@ -623,6 +679,7 @@ def broadcast_state():
623
  'steps': int(steps),
624
  'reward': reward_value,
625
  'teleop_action': teleop_snapshot,
 
626
  'nova_api': {
627
  'connected': nova_connected,
628
  'state_streaming': nova_state_streaming,
@@ -650,6 +707,7 @@ def broadcast_state():
650
  'steps': int(steps),
651
  'reward': reward_value,
652
  'teleop_action': teleop_snapshot,
 
653
  'connected_clients': connected_clients
654
  }
655
  })
 
554
  env = init_g1()
555
 
556
 
557
+ def _get_scene_objects():
558
+ """Extract scene objects (position and orientation) from the environment."""
559
+ if env is None:
560
+ return []
561
+
562
+ scene_objects = []
563
+
564
+ # For UR5 scenes, extract T-shape objects if they exist
565
+ if current_robot in ("ur5", "ur5_t_push"):
566
+ # Get t_object body if it exists
567
+ t_object_id = getattr(env, 't_object_body_id', -1)
568
+ if t_object_id >= 0:
569
+ pos = env.data.xpos[t_object_id]
570
+ quat = env._get_body_quat(t_object_id) if hasattr(env, '_get_body_quat') else np.array([1.0, 0.0, 0.0, 0.0])
571
+ scene_objects.append({
572
+ 'name': 't_object',
573
+ 'position': {'x': float(pos[0]), 'y': float(pos[1]), 'z': float(pos[2])},
574
+ 'orientation': {'w': float(quat[0]), 'x': float(quat[1]), 'y': float(quat[2]), 'z': float(quat[3])}
575
+ })
576
+
577
+ # Get t_target body if it exists
578
+ t_target_id = getattr(env, 't_target_body_id', -1)
579
+ if t_target_id >= 0:
580
+ pos = env.data.xpos[t_target_id]
581
+ quat = env._get_body_quat(t_target_id) if hasattr(env, '_get_body_quat') else np.array([1.0, 0.0, 0.0, 0.0])
582
+ scene_objects.append({
583
+ 'name': 't_target',
584
+ 'position': {'x': float(pos[0]), 'y': float(pos[1]), 'z': float(pos[2])},
585
+ 'orientation': {'w': float(quat[0]), 'x': float(quat[1]), 'y': float(quat[2]), 'z': float(quat[3])}
586
+ })
587
+
588
+ # Get box body if it exists
589
+ try:
590
+ box_id = mujoco.mj_name2id(env.model, mujoco.mjtObj.mjOBJ_BODY, "box")
591
+ if box_id >= 0:
592
+ pos = env.data.xpos[box_id]
593
+ quat = env._get_body_quat(box_id) if hasattr(env, '_get_body_quat') else np.array([1.0, 0.0, 0.0, 0.0])
594
+ scene_objects.append({
595
+ 'name': 'box',
596
+ 'position': {'x': float(pos[0]), 'y': float(pos[1]), 'z': float(pos[2])},
597
+ 'orientation': {'w': float(quat[0]), 'x': float(quat[1]), 'y': float(quat[2]), 'z': float(quat[3])}
598
+ })
599
+ except:
600
+ pass
601
+
602
+ return scene_objects
603
+
604
+
605
  def broadcast_state():
606
  """Broadcast robot state to all connected WebSocket clients."""
607
  with mujoco_lock:
 
625
  with external_ws_clients_lock:
626
  connected_clients = [info.get("identity", "client") for ws, info in list(client_metadata.items()) if ws in external_ws_clients]
627
 
628
+ # Get scene objects
629
+ scene_objects = _get_scene_objects()
630
+
631
  # UR5 has different state structure
632
  if current_robot in ("ur5", "ur5_t_push"):
633
  ee_pos = env.get_end_effector_pos()
 
643
  nova_client = getattr(env, '_nova_client', None)
644
  nova_state_streaming = getattr(env, '_use_nova_state_stream', False)
645
  nova_ik = getattr(env, '_use_nova_ik', False)
646
+ # Check if actually connected (WebSocket active and receiving data)
647
+ nova_connected = False
648
+ if nova_client is not None and hasattr(nova_client, 'is_state_stream_connected'):
649
+ nova_connected = nova_client.is_state_stream_connected()
650
+ elif nova_client is not None and not nova_state_streaming:
651
+ # If not using state streaming, consider connected if client exists
652
+ nova_connected = True
653
 
654
  nova_available = False
655
  nova_enabled_pref = False
 
679
  'steps': int(steps),
680
  'reward': reward_value,
681
  'teleop_action': teleop_snapshot,
682
+ 'scene_objects': scene_objects,
683
  'nova_api': {
684
  'connected': nova_connected,
685
  'state_streaming': nova_state_streaming,
 
707
  'steps': int(steps),
708
  'reward': reward_value,
709
  'teleop_action': teleop_snapshot,
710
+ 'scene_objects': scene_objects,
711
  'connected_clients': connected_clients
712
  }
713
  })
protocol_types.py CHANGED
@@ -325,6 +325,13 @@ class EulerAngles(TypedDict):
325
  yaw: float
326
 
327
 
 
 
 
 
 
 
 
328
  class LocomotionObservation(TypedDict):
329
  """Observation data for locomotion robots (G1, Spot)."""
330
  position: Position
@@ -359,6 +366,7 @@ class StateData(TypedDict, total=False):
359
  reward: float
360
  teleop_action: ActionData # Current action/velocity commands
361
  connected_clients: List[str] # List of connected client IDs
 
362
 
363
  # UR5-specific fields
364
  control_mode: ControlMode
 
325
  yaw: float
326
 
327
 
328
+ class SceneObject(TypedDict):
329
+ """Scene object information (position and orientation)."""
330
+ name: str
331
+ position: Position
332
+ orientation: Quaternion
333
+
334
+
335
  class LocomotionObservation(TypedDict):
336
  """Observation data for locomotion robots (G1, Spot)."""
337
  position: Position
 
366
  reward: float
367
  teleop_action: ActionData # Current action/velocity commands
368
  connected_clients: List[str] # List of connected client IDs
369
+ scene_objects: List[SceneObject] # Scene objects (position and orientation)
370
 
371
  # UR5-specific fields
372
  control_mode: ControlMode