luckyrobots 0.1.71__tar.gz → 0.1.72__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/PKG-INFO +1 -1
  2. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/pyproject.toml +1 -1
  3. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/client.py +103 -0
  4. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/agent_pb2.py +7 -3
  5. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/agent_pb2_grpc.py +46 -0
  6. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/scene_pb2.py +13 -3
  7. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/scene_pb2_grpc.py +88 -0
  8. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/agent.proto +29 -0
  9. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/scene.proto +34 -0
  10. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/luckyrobots.py +37 -6
  11. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/.gitignore +0 -0
  12. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/LICENSE +0 -0
  13. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/README.md +0 -0
  14. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/__init__.py +0 -0
  15. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/config/__init__.py +0 -0
  16. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/config/robots.yaml +0 -0
  17. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/engine/__init__.py +0 -0
  18. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/engine/check_updates.py +0 -0
  19. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/engine/download.py +0 -0
  20. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/engine/manager.py +0 -0
  21. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/__init__.py +0 -0
  22. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/__init__.py +0 -0
  23. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/camera_pb2.py +0 -0
  24. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/camera_pb2_grpc.py +0 -0
  25. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/common_pb2.py +0 -0
  26. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/common_pb2_grpc.py +0 -0
  27. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/hazel_rpc_pb2.py +0 -0
  28. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/hazel_rpc_pb2_grpc.py +0 -0
  29. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/media_pb2.py +0 -0
  30. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/media_pb2_grpc.py +0 -0
  31. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/mujoco_pb2.py +0 -0
  32. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/mujoco_pb2_grpc.py +0 -0
  33. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/telemetry_pb2.py +0 -0
  34. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/telemetry_pb2_grpc.py +0 -0
  35. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/viewport_pb2.py +0 -0
  36. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/generated/viewport_pb2_grpc.py +0 -0
  37. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/camera.proto +0 -0
  38. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/common.proto +0 -0
  39. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/hazel_rpc.proto +0 -0
  40. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/media.proto +0 -0
  41. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/mujoco.proto +0 -0
  42. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/telemetry.proto +0 -0
  43. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/grpc/proto/viewport.proto +0 -0
  44. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/models/__init__.py +0 -0
  45. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/models/camera.py +0 -0
  46. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/models/observation.py +0 -0
  47. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/models/randomization.py +0 -0
  48. {luckyrobots-0.1.71 → luckyrobots-0.1.72}/src/luckyrobots/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: luckyrobots
3
- Version: 0.1.71
3
+ Version: 0.1.72
4
4
  Summary: Robotics-AI Training in Hyperrealistic Game Environments
5
5
  Project-URL: Homepage, https://github.com/luckyrobots/luckyrobots
6
6
  Project-URL: Documentation, https://luckyrobots.readthedocs.io
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "luckyrobots"
7
- version = "0.1.71"
7
+ version = "0.1.72"
8
8
  description = "Robotics-AI Training in Hyperrealistic Game Environments"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -630,6 +630,109 @@ class LuckyEngineClient:
630
630
  timeout=timeout,
631
631
  )
632
632
 
633
+ def step(
634
+ self,
635
+ actions: list[float],
636
+ agent_name: str = "",
637
+ timeout_ms: int = 0,
638
+ timeout: Optional[float] = None,
639
+ ) -> ObservationResponse:
640
+ """
641
+ Synchronous RL step: apply action, wait for physics, return observation.
642
+
643
+ This is the recommended interface for RL training as it eliminates
644
+ one network round-trip compared to separate SendControl + GetObservation.
645
+
646
+ Args:
647
+ actions: Action vector to apply for this step.
648
+ agent_name: Agent name (empty = default agent).
649
+ timeout_ms: Server-side timeout for the step in milliseconds (0 = use default).
650
+ timeout: RPC timeout in seconds.
651
+
652
+ Returns:
653
+ ObservationResponse with observation after physics step.
654
+ """
655
+ timeout = timeout or self.timeout
656
+
657
+ resp = self.agent.Step(
658
+ self.pb.agent.StepRequest(
659
+ agent_name=agent_name,
660
+ actions=actions,
661
+ timeout_ms=timeout_ms,
662
+ ),
663
+ timeout=timeout,
664
+ )
665
+
666
+ if not resp.success:
667
+ raise RuntimeError(f"Step failed: {resp.message}")
668
+
669
+ # Extract observation from AgentFrame
670
+ agent_frame = resp.observation
671
+ observations = list(agent_frame.observations) if agent_frame.observations else []
672
+ actions_out = list(agent_frame.actions) if agent_frame.actions else []
673
+ timestamp_ms = getattr(agent_frame, "timestamp_ms", 0)
674
+ frame_number = getattr(agent_frame, "frame_number", 0)
675
+
676
+ # Look up cached schema for named access
677
+ cache_key = agent_name or "agent_0"
678
+ obs_names, action_names = self._schema_cache.get(cache_key, (None, None))
679
+
680
+ return ObservationResponse(
681
+ observation=observations,
682
+ actions=actions_out,
683
+ timestamp_ms=timestamp_ms,
684
+ frame_number=frame_number,
685
+ agent_name=cache_key,
686
+ observation_names=obs_names,
687
+ action_names=action_names,
688
+ )
689
+
690
+ def set_simulation_mode(
691
+ self,
692
+ mode: str = "fast",
693
+ timeout: Optional[float] = None,
694
+ ):
695
+ """
696
+ Set simulation timing mode.
697
+
698
+ Args:
699
+ mode: "realtime", "deterministic", or "fast"
700
+ - realtime: Physics runs at 1x wall-clock speed (for visualization)
701
+ - deterministic: Physics runs at fixed rate (for reproducibility)
702
+ - fast: Physics runs as fast as possible (for RL training)
703
+ timeout: RPC timeout in seconds.
704
+
705
+ Returns:
706
+ SetSimulationModeResponse with success and current mode.
707
+ """
708
+ timeout = timeout or self.timeout
709
+
710
+ mode_map = {
711
+ "realtime": 0,
712
+ "deterministic": 1,
713
+ "fast": 2,
714
+ }
715
+ mode_value = mode_map.get(mode.lower(), 2) # Default to fast
716
+
717
+ return self.scene.SetSimulationMode(
718
+ self.pb.scene.SetSimulationModeRequest(mode=mode_value),
719
+ timeout=timeout,
720
+ )
721
+
722
+ def get_simulation_mode(self, timeout: Optional[float] = None):
723
+ """
724
+ Get current simulation timing mode.
725
+
726
+ Returns:
727
+ GetSimulationModeResponse with mode field (0=realtime, 1=deterministic, 2=fast).
728
+ """
729
+ timeout = timeout or self.timeout
730
+
731
+ return self.scene.GetSimulationMode(
732
+ self.pb.scene.GetSimulationModeRequest(),
733
+ timeout=timeout,
734
+ )
735
+
633
736
  def _randomization_to_proto(self, randomization_cfg: Any):
634
737
  """Convert domain randomization config to proto message.
635
738
 
@@ -28,7 +28,7 @@ from . import mujoco_pb2 as mujoco__pb2
28
28
  from . import telemetry_pb2 as telemetry__pb2
29
29
 
30
30
 
31
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61gent.proto\x12\thazel.rpc\x1a\x0c\x63ommon.proto\x1a\x0bmedia.proto\x1a\x0cmujoco.proto\x1a\x0ftelemetry.proto\"\x81\x01\n\x0b\x41gentSchema\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x19\n\x11observation_names\x18\x02 \x03(\t\x12\x14\n\x0c\x61\x63tion_names\x18\x03 \x03(\t\x12\x18\n\x10observation_size\x18\x04 \x01(\r\x12\x13\n\x0b\x61\x63tion_size\x18\x05 \x01(\r\"+\n\x15GetAgentSchemaRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\"@\n\x16GetAgentSchemaResponse\x12&\n\x06schema\x18\x01 \x01(\x0b\x32\x16.hazel.rpc.AgentSchema\"<\n\x12StreamAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x12\n\ntarget_fps\x18\x02 \x01(\r\"\xa1\x03\n\x19\x44omainRandomizationConfig\x12\x1b\n\x13pose_position_noise\x18\x01 \x03(\x02\x12\x1e\n\x16pose_orientation_noise\x18\x02 \x01(\x02\x12\x1c\n\x14joint_position_noise\x18\x03 \x01(\x02\x12\x1c\n\x14joint_velocity_noise\x18\x04 \x01(\x02\x12\x16\n\x0e\x66riction_range\x18\x05 \x03(\x02\x12\x19\n\x11restitution_range\x18\x06 \x03(\x02\x12\x18\n\x10mass_scale_range\x18\x07 \x03(\x02\x12\x18\n\x10\x63om_offset_range\x18\x08 \x03(\x02\x12\x1c\n\x14motor_strength_range\x18\t \x03(\x02\x12\x1a\n\x12motor_offset_range\x18\n \x03(\x02\x12\x1b\n\x13push_interval_range\x18\x0b \x03(\x02\x12\x1b\n\x13push_velocity_range\x18\x0c \x03(\x02\x12\x14\n\x0cterrain_type\x18\r \x01(\t\x12\x1a\n\x12terrain_difficulty\x18\x0e \x01(\x02\"`\n\x11ResetAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x37\n\tdr_config\x18\x02 \x01(\x0b\x32$.hazel.rpc.DomainRandomizationConfig\"6\n\x12ResetAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x87\x01\n\nAgentFrame\x12\x14\n\x0ctimestamp_ms\x18\x01 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x02 \x01(\r\x12\x14\n\x0cobservations\x18\x03 \x03(\x02\x12\x0f\n\x07\x61\x63tions\x18\x04 \x03(\x02\x12\x12\n\nagent_name\x18\x05 \x01(\t\x12\x12\n\ntarget_fps\x18\x06 \x01(\r\"\x87\x01\n\x15GetCameraFrameRequest\x12!\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityIdH\x00\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x05 \x01(\tB\x0c\n\nidentifier\"_\n\x17GetViewportFrameRequest\x12\x15\n\rviewport_name\x18\x01 \x01(\t\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x04 \x01(\t\"\xfe\x01\n\x15GetObservationRequest\x12\x12\n\nrobot_name\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x1b\n\x13include_joint_state\x18\x03 \x01(\x08\x12\x1b\n\x13include_agent_frame\x18\x04 \x01(\x08\x12\x19\n\x11include_telemetry\x18\x05 \x01(\x08\x12\x31\n\x07\x63\x61meras\x18\x06 \x03(\x0b\x32 .hazel.rpc.GetCameraFrameRequest\x12\x35\n\tviewports\x18\x07 \x03(\x0b\x32\".hazel.rpc.GetViewportFrameRequest\"\xd4\x02\n\x16GetObservationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0ctimestamp_ms\x18\x03 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x04 \x01(\r\x12*\n\x0bjoint_state\x18\x05 \x01(\x0b\x32\x15.hazel.rpc.JointState\x12*\n\x0b\x61gent_frame\x18\x06 \x01(\x0b\x32\x15.hazel.rpc.AgentFrame\x12,\n\ttelemetry\x18\x07 \x01(\x0b\x32\x19.hazel.rpc.TelemetryFrame\x12\x31\n\rcamera_frames\x18\x08 \x03(\x0b\x32\x1a.hazel.rpc.NamedImageFrame\x12\x33\n\x0fviewport_frames\x18\t \x03(\x0b\x32\x1a.hazel.rpc.NamedImageFrame2\xce\x02\n\x0c\x41gentService\x12U\n\x0eGetAgentSchema\x12 .hazel.rpc.GetAgentSchemaRequest\x1a!.hazel.rpc.GetAgentSchemaResponse\x12U\n\x0eGetObservation\x12 .hazel.rpc.GetObservationRequest\x1a!.hazel.rpc.GetObservationResponse\x12\x45\n\x0bStreamAgent\x12\x1d.hazel.rpc.StreamAgentRequest\x1a\x15.hazel.rpc.AgentFrame0\x01\x12I\n\nResetAgent\x12\x1c.hazel.rpc.ResetAgentRequest\x1a\x1d.hazel.rpc.ResetAgentResponseB\x03\xf8\x01\x01\x62\x06proto3')
31
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61gent.proto\x12\thazel.rpc\x1a\x0c\x63ommon.proto\x1a\x0bmedia.proto\x1a\x0cmujoco.proto\x1a\x0ftelemetry.proto\"\x81\x01\n\x0b\x41gentSchema\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x19\n\x11observation_names\x18\x02 \x03(\t\x12\x14\n\x0c\x61\x63tion_names\x18\x03 \x03(\t\x12\x18\n\x10observation_size\x18\x04 \x01(\r\x12\x13\n\x0b\x61\x63tion_size\x18\x05 \x01(\r\"+\n\x15GetAgentSchemaRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\"@\n\x16GetAgentSchemaResponse\x12&\n\x06schema\x18\x01 \x01(\x0b\x32\x16.hazel.rpc.AgentSchema\"<\n\x12StreamAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x12\n\ntarget_fps\x18\x02 \x01(\r\"\xa1\x03\n\x19\x44omainRandomizationConfig\x12\x1b\n\x13pose_position_noise\x18\x01 \x03(\x02\x12\x1e\n\x16pose_orientation_noise\x18\x02 \x01(\x02\x12\x1c\n\x14joint_position_noise\x18\x03 \x01(\x02\x12\x1c\n\x14joint_velocity_noise\x18\x04 \x01(\x02\x12\x16\n\x0e\x66riction_range\x18\x05 \x03(\x02\x12\x19\n\x11restitution_range\x18\x06 \x03(\x02\x12\x18\n\x10mass_scale_range\x18\x07 \x03(\x02\x12\x18\n\x10\x63om_offset_range\x18\x08 \x03(\x02\x12\x1c\n\x14motor_strength_range\x18\t \x03(\x02\x12\x1a\n\x12motor_offset_range\x18\n \x03(\x02\x12\x1b\n\x13push_interval_range\x18\x0b \x03(\x02\x12\x1b\n\x13push_velocity_range\x18\x0c \x03(\x02\x12\x14\n\x0cterrain_type\x18\r \x01(\t\x12\x1a\n\x12terrain_difficulty\x18\x0e \x01(\x02\"`\n\x11ResetAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x37\n\tdr_config\x18\x02 \x01(\x0b\x32$.hazel.rpc.DomainRandomizationConfig\"6\n\x12ResetAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x87\x01\n\nAgentFrame\x12\x14\n\x0ctimestamp_ms\x18\x01 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x02 \x01(\r\x12\x14\n\x0cobservations\x18\x03 \x03(\x02\x12\x0f\n\x07\x61\x63tions\x18\x04 \x03(\x02\x12\x12\n\nagent_name\x18\x05 \x01(\t\x12\x12\n\ntarget_fps\x18\x06 \x01(\r\"\x87\x01\n\x15GetCameraFrameRequest\x12!\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityIdH\x00\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x05 \x01(\tB\x0c\n\nidentifier\"_\n\x17GetViewportFrameRequest\x12\x15\n\rviewport_name\x18\x01 \x01(\t\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x04 \x01(\t\"\xfe\x01\n\x15GetObservationRequest\x12\x12\n\nrobot_name\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x1b\n\x13include_joint_state\x18\x03 \x01(\x08\x12\x1b\n\x13include_agent_frame\x18\x04 \x01(\x08\x12\x19\n\x11include_telemetry\x18\x05 \x01(\x08\x12\x31\n\x07\x63\x61meras\x18\x06 \x03(\x0b\x32 .hazel.rpc.GetCameraFrameRequest\x12\x35\n\tviewports\x18\x07 \x03(\x0b\x32\".hazel.rpc.GetViewportFrameRequest\"\xd4\x02\n\x16GetObservationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0ctimestamp_ms\x18\x03 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x04 \x01(\r\x12*\n\x0bjoint_state\x18\x05 \x01(\x0b\x32\x15.hazel.rpc.JointState\x12*\n\x0b\x61gent_frame\x18\x06 \x01(\x0b\x32\x15.hazel.rpc.AgentFrame\x12,\n\ttelemetry\x18\x07 \x01(\x0b\x32\x19.hazel.rpc.TelemetryFrame\x12\x31\n\rcamera_frames\x18\x08 \x03(\x0b\x32\x1a.hazel.rpc.NamedImageFrame\x12\x33\n\x0fviewport_frames\x18\t \x03(\x0b\x32\x1a.hazel.rpc.NamedImageFrame\"F\n\x0bStepRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x02 \x03(\x02\x12\x12\n\ntimeout_ms\x18\x03 \x01(\r\"~\n\x0cStepResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12*\n\x0bobservation\x18\x03 \x01(\x0b\x32\x15.hazel.rpc.AgentFrame\x12 \n\x18physics_step_duration_us\x18\x04 \x01(\x04\x32\x87\x03\n\x0c\x41gentService\x12U\n\x0eGetAgentSchema\x12 .hazel.rpc.GetAgentSchemaRequest\x1a!.hazel.rpc.GetAgentSchemaResponse\x12U\n\x0eGetObservation\x12 .hazel.rpc.GetObservationRequest\x1a!.hazel.rpc.GetObservationResponse\x12\x45\n\x0bStreamAgent\x12\x1d.hazel.rpc.StreamAgentRequest\x1a\x15.hazel.rpc.AgentFrame0\x01\x12I\n\nResetAgent\x12\x1c.hazel.rpc.ResetAgentRequest\x1a\x1d.hazel.rpc.ResetAgentResponse\x12\x37\n\x04Step\x12\x16.hazel.rpc.StepRequest\x1a\x17.hazel.rpc.StepResponseB\x03\xf8\x01\x01\x62\x06proto3')
32
32
 
33
33
  _globals = globals()
34
34
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -60,6 +60,10 @@ if not _descriptor._USE_C_DESCRIPTORS:
60
60
  _globals['_GETOBSERVATIONREQUEST']._serialized_end=1591
61
61
  _globals['_GETOBSERVATIONRESPONSE']._serialized_start=1594
62
62
  _globals['_GETOBSERVATIONRESPONSE']._serialized_end=1934
63
- _globals['_AGENTSERVICE']._serialized_start=1937
64
- _globals['_AGENTSERVICE']._serialized_end=2271
63
+ _globals['_STEPREQUEST']._serialized_start=1936
64
+ _globals['_STEPREQUEST']._serialized_end=2006
65
+ _globals['_STEPRESPONSE']._serialized_start=2008
66
+ _globals['_STEPRESPONSE']._serialized_end=2134
67
+ _globals['_AGENTSERVICE']._serialized_start=2137
68
+ _globals['_AGENTSERVICE']._serialized_end=2528
65
69
  # @@protoc_insertion_point(module_scope)
@@ -55,6 +55,11 @@ class AgentServiceStub(object):
55
55
  request_serializer=agent__pb2.ResetAgentRequest.SerializeToString,
56
56
  response_deserializer=agent__pb2.ResetAgentResponse.FromString,
57
57
  _registered_method=True)
58
+ self.Step = channel.unary_unary(
59
+ '/hazel.rpc.AgentService/Step',
60
+ request_serializer=agent__pb2.StepRequest.SerializeToString,
61
+ response_deserializer=agent__pb2.StepResponse.FromString,
62
+ _registered_method=True)
58
63
 
59
64
 
60
65
  class AgentServiceServicer(object):
@@ -93,6 +98,15 @@ class AgentServiceServicer(object):
93
98
  context.set_details('Method not implemented!')
94
99
  raise NotImplementedError('Method not implemented!')
95
100
 
101
+ def Step(self, request, context):
102
+ """Synchronous RL step: apply action, wait for physics, return observation.
103
+ This is the recommended interface for RL training as it eliminates
104
+ one network round-trip compared to separate SendControl + GetObservation.
105
+ """
106
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
107
+ context.set_details('Method not implemented!')
108
+ raise NotImplementedError('Method not implemented!')
109
+
96
110
 
97
111
  def add_AgentServiceServicer_to_server(servicer, server):
98
112
  rpc_method_handlers = {
@@ -116,6 +130,11 @@ def add_AgentServiceServicer_to_server(servicer, server):
116
130
  request_deserializer=agent__pb2.ResetAgentRequest.FromString,
117
131
  response_serializer=agent__pb2.ResetAgentResponse.SerializeToString,
118
132
  ),
133
+ 'Step': grpc.unary_unary_rpc_method_handler(
134
+ servicer.Step,
135
+ request_deserializer=agent__pb2.StepRequest.FromString,
136
+ response_serializer=agent__pb2.StepResponse.SerializeToString,
137
+ ),
119
138
  }
120
139
  generic_handler = grpc.method_handlers_generic_handler(
121
140
  'hazel.rpc.AgentService', rpc_method_handlers)
@@ -235,3 +254,30 @@ class AgentService(object):
235
254
  timeout,
236
255
  metadata,
237
256
  _registered_method=True)
257
+
258
+ @staticmethod
259
+ def Step(request,
260
+ target,
261
+ options=(),
262
+ channel_credentials=None,
263
+ call_credentials=None,
264
+ insecure=False,
265
+ compression=None,
266
+ wait_for_ready=None,
267
+ timeout=None,
268
+ metadata=None):
269
+ return grpc.experimental.unary_unary(
270
+ request,
271
+ target,
272
+ '/hazel.rpc.AgentService/Step',
273
+ agent__pb2.StepRequest.SerializeToString,
274
+ agent__pb2.StepResponse.FromString,
275
+ options,
276
+ channel_credentials,
277
+ insecure,
278
+ call_credentials,
279
+ compression,
280
+ wait_for_ready,
281
+ timeout,
282
+ metadata,
283
+ _registered_method=True)
@@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default()
25
25
  from . import common_pb2 as common__pb2
26
26
 
27
27
 
28
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bscene.proto\x12\thazel.rpc\x1a\x0c\x63ommon.proto\"x\n\nEntityInfo\x12\x1f\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityId\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\'\n\ttransform\x18\x03 \x01(\x0b\x32\x14.hazel.rpc.Transform\x12\x12\n\ncomponents\x18\x04 \x03(\t\"\x15\n\x13GetSceneInfoRequest\"T\n\x14GetSceneInfoResponse\x12\x12\n\nscene_name\x18\x01 \x01(\t\x12\x12\n\nscene_path\x18\x02 \x01(\t\x12\x14\n\x0c\x65ntity_count\x18\x03 \x01(\r\"M\n\x13ListEntitiesRequest\x12\x1a\n\x12include_transforms\x18\x01 \x01(\x08\x12\x1a\n\x12include_components\x18\x02 \x01(\x08\"?\n\x14ListEntitiesResponse\x12\'\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x15.hazel.rpc.EntityInfo\"S\n\x10GetEntityRequest\x12!\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityIdH\x00\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x42\x0c\n\nidentifier\"I\n\x11GetEntityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12%\n\x06\x65ntity\x18\x02 \x01(\x0b\x32\x15.hazel.rpc.EntityInfo\"e\n\x19SetEntityTransformRequest\x12\x1f\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityId\x12\'\n\ttransform\x18\x02 \x01(\x0b\x32\x14.hazel.rpc.Transform\">\n\x1aSetEntityTransformResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t2\xdb\x02\n\x0cSceneService\x12O\n\x0cGetSceneInfo\x12\x1e.hazel.rpc.GetSceneInfoRequest\x1a\x1f.hazel.rpc.GetSceneInfoResponse\x12O\n\x0cListEntities\x12\x1e.hazel.rpc.ListEntitiesRequest\x1a\x1f.hazel.rpc.ListEntitiesResponse\x12\x46\n\tGetEntity\x12\x1b.hazel.rpc.GetEntityRequest\x1a\x1c.hazel.rpc.GetEntityResponse\x12\x61\n\x12SetEntityTransform\x12$.hazel.rpc.SetEntityTransformRequest\x1a%.hazel.rpc.SetEntityTransformResponseB\x03\xf8\x01\x01\x62\x06proto3')
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bscene.proto\x12\thazel.rpc\x1a\x0c\x63ommon.proto\"x\n\nEntityInfo\x12\x1f\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityId\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\'\n\ttransform\x18\x03 \x01(\x0b\x32\x14.hazel.rpc.Transform\x12\x12\n\ncomponents\x18\x04 \x03(\t\"\x15\n\x13GetSceneInfoRequest\"T\n\x14GetSceneInfoResponse\x12\x12\n\nscene_name\x18\x01 \x01(\t\x12\x12\n\nscene_path\x18\x02 \x01(\t\x12\x14\n\x0c\x65ntity_count\x18\x03 \x01(\r\"M\n\x13ListEntitiesRequest\x12\x1a\n\x12include_transforms\x18\x01 \x01(\x08\x12\x1a\n\x12include_components\x18\x02 \x01(\x08\"?\n\x14ListEntitiesResponse\x12\'\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x15.hazel.rpc.EntityInfo\"S\n\x10GetEntityRequest\x12!\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityIdH\x00\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x42\x0c\n\nidentifier\"I\n\x11GetEntityResponse\x12\r\n\x05\x66ound\x18\x01 \x01(\x08\x12%\n\x06\x65ntity\x18\x02 \x01(\x0b\x32\x15.hazel.rpc.EntityInfo\"e\n\x19SetEntityTransformRequest\x12\x1f\n\x02id\x18\x01 \x01(\x0b\x32\x13.hazel.rpc.EntityId\x12\'\n\ttransform\x18\x02 \x01(\x0b\x32\x14.hazel.rpc.Transform\">\n\x1aSetEntityTransformResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"C\n\x18SetSimulationModeRequest\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.hazel.rpc.SimulationMode\"n\n\x19SetSimulationModeResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12/\n\x0c\x63urrent_mode\x18\x03 \x01(\x0e\x32\x19.hazel.rpc.SimulationMode\"\x1a\n\x18GetSimulationModeRequest\"D\n\x19GetSimulationModeResponse\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.hazel.rpc.SimulationMode*k\n\x0eSimulationMode\x12\x1c\n\x18SIMULATION_MODE_REALTIME\x10\x00\x12!\n\x1dSIMULATION_MODE_DETERMINISTIC\x10\x01\x12\x18\n\x14SIMULATION_MODE_FAST\x10\x02\x32\x9b\x04\n\x0cSceneService\x12O\n\x0cGetSceneInfo\x12\x1e.hazel.rpc.GetSceneInfoRequest\x1a\x1f.hazel.rpc.GetSceneInfoResponse\x12O\n\x0cListEntities\x12\x1e.hazel.rpc.ListEntitiesRequest\x1a\x1f.hazel.rpc.ListEntitiesResponse\x12\x46\n\tGetEntity\x12\x1b.hazel.rpc.GetEntityRequest\x1a\x1c.hazel.rpc.GetEntityResponse\x12\x61\n\x12SetEntityTransform\x12$.hazel.rpc.SetEntityTransformRequest\x1a%.hazel.rpc.SetEntityTransformResponse\x12^\n\x11SetSimulationMode\x12#.hazel.rpc.SetSimulationModeRequest\x1a$.hazel.rpc.SetSimulationModeResponse\x12^\n\x11GetSimulationMode\x12#.hazel.rpc.GetSimulationModeRequest\x1a$.hazel.rpc.GetSimulationModeResponseB\x03\xf8\x01\x01\x62\x06proto3')
29
29
 
30
30
  _globals = globals()
31
31
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -33,6 +33,8 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'scene_pb2', _globals)
33
33
  if not _descriptor._USE_C_DESCRIPTORS:
34
34
  _globals['DESCRIPTOR']._loaded_options = None
35
35
  _globals['DESCRIPTOR']._serialized_options = b'\370\001\001'
36
+ _globals['_SIMULATIONMODE']._serialized_start=1021
37
+ _globals['_SIMULATIONMODE']._serialized_end=1128
36
38
  _globals['_ENTITYINFO']._serialized_start=40
37
39
  _globals['_ENTITYINFO']._serialized_end=160
38
40
  _globals['_GETSCENEINFOREQUEST']._serialized_start=162
@@ -51,6 +53,14 @@ if not _descriptor._USE_C_DESCRIPTORS:
51
53
  _globals['_SETENTITYTRANSFORMREQUEST']._serialized_end=676
52
54
  _globals['_SETENTITYTRANSFORMRESPONSE']._serialized_start=678
53
55
  _globals['_SETENTITYTRANSFORMRESPONSE']._serialized_end=740
54
- _globals['_SCENESERVICE']._serialized_start=743
55
- _globals['_SCENESERVICE']._serialized_end=1090
56
+ _globals['_SETSIMULATIONMODEREQUEST']._serialized_start=742
57
+ _globals['_SETSIMULATIONMODEREQUEST']._serialized_end=809
58
+ _globals['_SETSIMULATIONMODERESPONSE']._serialized_start=811
59
+ _globals['_SETSIMULATIONMODERESPONSE']._serialized_end=921
60
+ _globals['_GETSIMULATIONMODEREQUEST']._serialized_start=923
61
+ _globals['_GETSIMULATIONMODEREQUEST']._serialized_end=949
62
+ _globals['_GETSIMULATIONMODERESPONSE']._serialized_start=951
63
+ _globals['_GETSIMULATIONMODERESPONSE']._serialized_end=1019
64
+ _globals['_SCENESERVICE']._serialized_start=1131
65
+ _globals['_SCENESERVICE']._serialized_end=1670
56
66
  # @@protoc_insertion_point(module_scope)
@@ -55,6 +55,16 @@ class SceneServiceStub(object):
55
55
  request_serializer=scene__pb2.SetEntityTransformRequest.SerializeToString,
56
56
  response_deserializer=scene__pb2.SetEntityTransformResponse.FromString,
57
57
  _registered_method=True)
58
+ self.SetSimulationMode = channel.unary_unary(
59
+ '/hazel.rpc.SceneService/SetSimulationMode',
60
+ request_serializer=scene__pb2.SetSimulationModeRequest.SerializeToString,
61
+ response_deserializer=scene__pb2.SetSimulationModeResponse.FromString,
62
+ _registered_method=True)
63
+ self.GetSimulationMode = channel.unary_unary(
64
+ '/hazel.rpc.SceneService/GetSimulationMode',
65
+ request_serializer=scene__pb2.GetSimulationModeRequest.SerializeToString,
66
+ response_deserializer=scene__pb2.GetSimulationModeResponse.FromString,
67
+ _registered_method=True)
58
68
 
59
69
 
60
70
  class SceneServiceServicer(object):
@@ -85,6 +95,20 @@ class SceneServiceServicer(object):
85
95
  context.set_details('Method not implemented!')
86
96
  raise NotImplementedError('Method not implemented!')
87
97
 
98
+ def SetSimulationMode(self, request, context):
99
+ """Set simulation timing mode (realtime, deterministic, or fast).
100
+ """
101
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
102
+ context.set_details('Method not implemented!')
103
+ raise NotImplementedError('Method not implemented!')
104
+
105
+ def GetSimulationMode(self, request, context):
106
+ """Get current simulation timing mode.
107
+ """
108
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
109
+ context.set_details('Method not implemented!')
110
+ raise NotImplementedError('Method not implemented!')
111
+
88
112
 
89
113
  def add_SceneServiceServicer_to_server(servicer, server):
90
114
  rpc_method_handlers = {
@@ -108,6 +132,16 @@ def add_SceneServiceServicer_to_server(servicer, server):
108
132
  request_deserializer=scene__pb2.SetEntityTransformRequest.FromString,
109
133
  response_serializer=scene__pb2.SetEntityTransformResponse.SerializeToString,
110
134
  ),
135
+ 'SetSimulationMode': grpc.unary_unary_rpc_method_handler(
136
+ servicer.SetSimulationMode,
137
+ request_deserializer=scene__pb2.SetSimulationModeRequest.FromString,
138
+ response_serializer=scene__pb2.SetSimulationModeResponse.SerializeToString,
139
+ ),
140
+ 'GetSimulationMode': grpc.unary_unary_rpc_method_handler(
141
+ servicer.GetSimulationMode,
142
+ request_deserializer=scene__pb2.GetSimulationModeRequest.FromString,
143
+ response_serializer=scene__pb2.GetSimulationModeResponse.SerializeToString,
144
+ ),
111
145
  }
112
146
  generic_handler = grpc.method_handlers_generic_handler(
113
147
  'hazel.rpc.SceneService', rpc_method_handlers)
@@ -227,3 +261,57 @@ class SceneService(object):
227
261
  timeout,
228
262
  metadata,
229
263
  _registered_method=True)
264
+
265
+ @staticmethod
266
+ def SetSimulationMode(request,
267
+ target,
268
+ options=(),
269
+ channel_credentials=None,
270
+ call_credentials=None,
271
+ insecure=False,
272
+ compression=None,
273
+ wait_for_ready=None,
274
+ timeout=None,
275
+ metadata=None):
276
+ return grpc.experimental.unary_unary(
277
+ request,
278
+ target,
279
+ '/hazel.rpc.SceneService/SetSimulationMode',
280
+ scene__pb2.SetSimulationModeRequest.SerializeToString,
281
+ scene__pb2.SetSimulationModeResponse.FromString,
282
+ options,
283
+ channel_credentials,
284
+ insecure,
285
+ call_credentials,
286
+ compression,
287
+ wait_for_ready,
288
+ timeout,
289
+ metadata,
290
+ _registered_method=True)
291
+
292
+ @staticmethod
293
+ def GetSimulationMode(request,
294
+ target,
295
+ options=(),
296
+ channel_credentials=None,
297
+ call_credentials=None,
298
+ insecure=False,
299
+ compression=None,
300
+ wait_for_ready=None,
301
+ timeout=None,
302
+ metadata=None):
303
+ return grpc.experimental.unary_unary(
304
+ request,
305
+ target,
306
+ '/hazel.rpc.SceneService/GetSimulationMode',
307
+ scene__pb2.GetSimulationModeRequest.SerializeToString,
308
+ scene__pb2.GetSimulationModeResponse.FromString,
309
+ options,
310
+ channel_credentials,
311
+ insecure,
312
+ call_credentials,
313
+ compression,
314
+ wait_for_ready,
315
+ timeout,
316
+ metadata,
317
+ _registered_method=True)
@@ -167,6 +167,31 @@ message GetObservationResponse {
167
167
  repeated NamedImageFrame viewport_frames = 9;
168
168
  }
169
169
 
170
+ // =============================================================================
171
+ // Step RPC (synchronous RL step)
172
+ // =============================================================================
173
+
174
+ // Request to step the simulation with the provided action.
175
+ // This is the recommended interface for RL training - it combines
176
+ // SendControl + physics step + GetObservation into a single RPC.
177
+ message StepRequest {
178
+ // Agent logical name. Empty means default agent.
179
+ string agent_name = 1;
180
+ // Action vector to apply for this step.
181
+ repeated float actions = 2;
182
+ // Optional timeout in milliseconds. 0 means use server default.
183
+ uint32 timeout_ms = 3;
184
+ }
185
+
186
+ message StepResponse {
187
+ bool success = 1;
188
+ string message = 2;
189
+ // Observation after the physics step completed.
190
+ AgentFrame observation = 3;
191
+ // Time taken for the physics step in microseconds (for profiling).
192
+ uint64 physics_step_duration_us = 4;
193
+ }
194
+
170
195
  // Per-agent RL-style observation streaming (training / inference clients consume this).
171
196
  service AgentService {
172
197
  rpc GetAgentSchema(GetAgentSchemaRequest) returns (GetAgentSchemaResponse);
@@ -181,4 +206,8 @@ service AgentService {
181
206
  // Reset a specific agent (full reset: clear buffers, reset state, resample commands, apply MuJoCo state).
182
207
  // Useful for multi-env RL where individual agents need to be reset without resetting the entire scene.
183
208
  rpc ResetAgent(ResetAgentRequest) returns (ResetAgentResponse);
209
+ // Synchronous RL step: apply action, wait for physics, return observation.
210
+ // This is the recommended interface for RL training as it eliminates
211
+ // one network round-trip compared to separate SendControl + GetObservation.
212
+ rpc Step(StepRequest) returns (StepResponse);
184
213
  }
@@ -61,10 +61,44 @@ message SetEntityTransformResponse {
61
61
  string message = 2;
62
62
  }
63
63
 
64
+ // =============================================================================
65
+ // Simulation Mode
66
+ // =============================================================================
67
+
68
+ // Simulation timing mode controls how physics stepping relates to wall-clock time.
69
+ enum SimulationMode {
70
+ // Real-time: physics runs at 1x wall-clock speed (for visualization, games).
71
+ SIMULATION_MODE_REALTIME = 0;
72
+ // Deterministic: physics runs at fixed rate regardless of wall-clock (for reproducibility).
73
+ SIMULATION_MODE_DETERMINISTIC = 1;
74
+ // Fast: physics runs as fast as possible without real-time limiting (for RL training).
75
+ SIMULATION_MODE_FAST = 2;
76
+ }
77
+
78
+ message SetSimulationModeRequest {
79
+ SimulationMode mode = 1;
80
+ }
81
+
82
+ message SetSimulationModeResponse {
83
+ bool success = 1;
84
+ string message = 2;
85
+ SimulationMode current_mode = 3;
86
+ }
87
+
88
+ message GetSimulationModeRequest {}
89
+
90
+ message GetSimulationModeResponse {
91
+ SimulationMode mode = 1;
92
+ }
93
+
64
94
  // Scene inspection + basic editing.
65
95
  service SceneService {
66
96
  rpc GetSceneInfo(GetSceneInfoRequest) returns (GetSceneInfoResponse);
67
97
  rpc ListEntities(ListEntitiesRequest) returns (ListEntitiesResponse);
68
98
  rpc GetEntity(GetEntityRequest) returns (GetEntityResponse);
69
99
  rpc SetEntityTransform(SetEntityTransformRequest) returns (SetEntityTransformResponse);
100
+ // Set simulation timing mode (realtime, deterministic, or fast).
101
+ rpc SetSimulationMode(SetSimulationModeRequest) returns (SetSimulationModeResponse);
102
+ // Get current simulation timing mode.
103
+ rpc GetSimulationMode(GetSimulationModeRequest) returns (GetSimulationModeResponse);
70
104
  }
@@ -164,13 +164,44 @@ class LuckyRobots:
164
164
  raise RuntimeError(f"SendControl failed: {getattr(resp, 'message', '')}")
165
165
 
166
166
  def step(
167
- self, controls: Sequence[float], sleep_s: float = 0.01
167
+ self,
168
+ actions: Sequence[float],
169
+ agent_name: str = "",
168
170
  ) -> ObservationResponse:
169
- """Send controls, wait briefly for physics, then return a fresh observation."""
170
- self.send_control(controls)
171
- if sleep_s > 0:
172
- time.sleep(sleep_s)
173
- return self.get_observation()
171
+ """
172
+ Synchronous RL step: apply action, wait for physics, return observation.
173
+
174
+ This is the recommended interface for RL training. It uses the gRPC Step RPC
175
+ which combines SendControl + physics step + GetObservation into a single call,
176
+ eliminating one network round-trip.
177
+
178
+ Args:
179
+ actions: Action vector to apply for this step.
180
+ agent_name: Agent name (empty = default agent).
181
+
182
+ Returns:
183
+ ObservationResponse with observation after physics step.
184
+ """
185
+ client = self._require_client()
186
+ return client.step(actions=list(actions), agent_name=agent_name)
187
+
188
+ def set_simulation_mode(self, mode: str = "fast"):
189
+ """
190
+ Set simulation timing mode.
191
+
192
+ Args:
193
+ mode: "realtime", "deterministic", or "fast"
194
+ - realtime: Physics runs at 1x wall-clock speed (for visualization)
195
+ - deterministic: Physics runs at fixed rate (for reproducibility)
196
+ - fast: Physics runs as fast as possible (for RL training)
197
+ """
198
+ client = self._require_client()
199
+ return client.set_simulation_mode(mode=mode)
200
+
201
+ def get_simulation_mode(self):
202
+ """Get current simulation timing mode."""
203
+ client = self._require_client()
204
+ return client.get_simulation_mode()
174
205
 
175
206
  def reset(
176
207
  self,
File without changes
File without changes
File without changes