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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions examples/run_hang_lifebuoy_teleop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Teleop script for Hang Lifebuoy environment.
"""

import genesis as gs
import torch
from gs_agent.wrappers.teleop_wrapper import KeyboardWrapper
from gs_env.sim.envs.config.registry import EnvArgsRegistry
from gs_env.sim.envs.manipulation.hang_lifebuoy_env import HangLifebuoyEnv


def main() -> None:
"""Run teleop for hang lifebuoy task."""
print("Initializing Hang Lifebuoy Teleop System...")

# Initialize Genesis
gs.init(
seed=0,
precision="32",
logging_level="info",
backend=gs.cpu, # type: ignore
Copy link
Owner

@yun-long yun-long Sep 11, 2025

Choose a reason for hiding this comment

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

i am not sure if we need type: ignore it here.

)

# Create teleop wrapper first (without environment)
print("Creating teleop wrapper...")
teleop_wrapper = KeyboardWrapper(
env=None,
device=torch.device("cpu"),
movement_speed=0.01, # Position movement speed
rotation_speed=0.05, # Rotation speed
trajectory_filename_prefix="hang_lifebuoy_",
)

# Start teleop wrapper (keyboard listener) FIRST, before creating Genesis scene
teleop_wrapper.start() # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

no need type: ignore here.


# Create task environment AFTER teleop wrapper is running
env = HangLifebuoyEnv(
args=EnvArgsRegistry["hang_lifebuoy_default"],
device=torch.device("cpu"),
)
teleop_wrapper.set_environment(env)

print("\n" + "=" * 50)
print("Hang Lifebuoy TELEOP SYSTEM READY")
print("=" * 50)
print("📝 TRAJECTORY RECORDING INSTRUCTIONS:")
print(" 1. Press 'r' to start recording (anytime)")
print(" 2. Move robot with arrow keys, n/m, space")
print(" 3. Press 'r' again to stop recording and save")
print(" 💡 Recording works from any state now!")
print("=" * 50)

# Run the main control loop in the main thread (Genesis viewer requires this)
try:
step_count = 0
while teleop_wrapper.running:
# Step the teleop wrapper (this processes input and steps environment)
teleop_wrapper.step(torch.tensor([]))
step_count += 1

# Check for quit command
if (
hasattr(teleop_wrapper, "last_command")
and teleop_wrapper.last_command
and hasattr(teleop_wrapper.last_command, "quit_teleop")
and teleop_wrapper.last_command.quit_teleop
):
print("Quit command received, exiting...")
break

# Safety check - exit after 1 hour of running
if step_count > 180000: # 1 hour at 50Hz
print("Maximum runtime reached, exiting...")
break

except KeyboardInterrupt:
print("\n👋 Teleop interrupted by user")

finally:
# Cleanup
teleop_wrapper.stop()
print("✅ Teleop session ended")


if __name__ == "__main__":
main()
88 changes: 88 additions & 0 deletions examples/run_put_bowl_teleop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Teleop script for Put Bowl Inside Microwave environment.
"""

import genesis as gs
import torch
from gs_agent.wrappers.teleop_wrapper import KeyboardWrapper
from gs_env.sim.envs.config.registry import EnvArgsRegistry
from gs_env.sim.envs.manipulation.put_bowl_inside_microwave_env import PutBowlInsideMicrowaveEnv


def main() -> None:
"""Run teleop for put bowl inside microwave task."""
print("Initializing Put Bowl Inside Microwave Teleop System...")

# Initialize Genesis
gs.init(
seed=0,
precision="32",
logging_level="info",
backend=gs.cpu, # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

same

)

# Create teleop wrapper first (without environment)
print("Creating teleop wrapper...")
teleop_wrapper = KeyboardWrapper(
env=None,
device=torch.device("cpu"),
movement_speed=0.01, # Position movement speed
rotation_speed=0.05, # Rotation speed
trajectory_filename_prefix="put_bowl_microwave_",
)

# Start teleop wrapper (keyboard listener) FIRST, before creating Genesis scene
teleop_wrapper.start() # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

also here.


# Create task environment AFTER teleop wrapper is running
env = PutBowlInsideMicrowaveEnv(
args=EnvArgsRegistry["put_bowl_inside_microwave_default"],
device=torch.device("cpu"),
)
teleop_wrapper.set_environment(env)

print("\n" + "=" * 50)
print("Put Bowl Inside Microwave TELEOP SYSTEM READY")
print("=" * 50)
print("📝 TRAJECTORY RECORDING INSTRUCTIONS:")
print(" 1. Press 'r' to start recording (anytime)")
print(" 2. Move robot with arrow keys, n/m, space")
print(" 3. Press 'r' again to stop recording and save")
print(" 💡 Recording works from any state now!")
print("=" * 50)

# Run the main control loop in the main thread (Genesis viewer requires this)
try:
step_count = 0
while teleop_wrapper.running:
# Step the teleop wrapper (this processes input and steps environment)
teleop_wrapper.step(torch.tensor([]))
step_count += 1

# Check for quit command
if (
hasattr(teleop_wrapper, "last_command")
and teleop_wrapper.last_command
and hasattr(teleop_wrapper.last_command, "quit_teleop")
and teleop_wrapper.last_command.quit_teleop
):
print("Quit command received, exiting...")
break

# Safety check - exit after 1 hour of running
if step_count > 180000: # 1 hour at 50Hz
print("Maximum runtime reached, exiting...")
break

except KeyboardInterrupt:
print("\n👋 Teleop interrupted by user")

finally:
# Cleanup
teleop_wrapper.stop()
print("✅ Teleop session ended")


if __name__ == "__main__":
main()
88 changes: 88 additions & 0 deletions examples/run_sweep_table_teleop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Teleop script for Sweep Table environment.
"""

import genesis as gs
import torch
from gs_agent.wrappers.teleop_wrapper import KeyboardWrapper
from gs_env.sim.envs.config.registry import EnvArgsRegistry
from gs_env.sim.envs.manipulation.sweep_table_env import SweepTableEnv


def main() -> None:
"""Run teleop for sweep table task."""
print("Initializing Sweep Table Teleop System...")

# Initialize Genesis
gs.init(
seed=0,
precision="32",
logging_level="info",
backend=gs.cpu, # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

same

)

# Create teleop wrapper first (without environment)
print("Creating teleop wrapper...")
teleop_wrapper = KeyboardWrapper(
env=None,
device=torch.device("cpu"),
movement_speed=0.01, # Position movement speed
rotation_speed=0.05, # Rotation speed
trajectory_filename_prefix="sweep_table_",
)

# Start teleop wrapper (keyboard listener) FIRST, before creating Genesis scene
teleop_wrapper.start() # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

same


# Create task environment AFTER teleop wrapper is running
env = SweepTableEnv(
args=EnvArgsRegistry["sweep_table_default"],
device=torch.device("cpu"),
)
teleop_wrapper.set_environment(env)

print("\n" + "=" * 50)
print("Sweep Table TELEOP SYSTEM READY")
print("=" * 50)
print("📝 TRAJECTORY RECORDING INSTRUCTIONS:")
print(" 1. Press 'r' to start recording (anytime)")
print(" 2. Move robot with arrow keys, n/m, space")
print(" 3. Press 'r' again to stop recording and save")
print(" 💡 Recording works from any state now!")
print("=" * 50)

# Run the main control loop in the main thread (Genesis viewer requires this)
try:
step_count = 0
while teleop_wrapper.running:
# Step the teleop wrapper (this processes input and steps environment)
teleop_wrapper.step(torch.tensor([]))
step_count += 1

# Check for quit command
if (
hasattr(teleop_wrapper, "last_command")
and teleop_wrapper.last_command
and hasattr(teleop_wrapper.last_command, "quit_teleop")
and teleop_wrapper.last_command.quit_teleop
):
print("Quit command received, exiting...")
break

# Safety check - exit after 1 hour of running
if step_count > 180000: # 1 hour at 50Hz
print("Maximum runtime reached, exiting...")
break

except KeyboardInterrupt:
print("\n👋 Teleop interrupted by user")

finally:
# Cleanup
teleop_wrapper.stop()
print("✅ Teleop session ended")


if __name__ == "__main__":
main()
23 changes: 14 additions & 9 deletions src/agent/gs_agent/wrappers/teleop_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,22 +207,27 @@ def get_observations(self) -> torch.Tensor:

def _convert_observation_to_dict(self) -> dict[str, Any]:
"""Convert tensor observation to dictionary format for teleop compatibility."""

# Get cube position
cube_pos = np.array(self._env.entities["cube"].get_pos())
cube_quat = np.array(self._env.entities["cube"].get_quat())

# Create observation dictionary (for teleop compatibility)
observation = {
"ee_pose": self._env.entities["robot"].ee_pose,
# "end_effector_pos": robot_obs["end_effector_pos"],
# "end_effector_quat": robot_obs["end_effector_quat"],
"cube_pos": cube_pos,
"cube_quat": cube_quat,
"rgb_images": {}, # No cameras in this simple setup
"depth_images": {}, # No depth sensors in this simple setup
}

# Add all object positions dynamically (excluding robot, plane, table, ee_frame)
excluded_entities = {"robot", "plane", "table", "ee_frame"}
for entity_name, entity in self._env.entities.items():
if entity_name not in excluded_entities:
try:
obj_pos = np.array(entity.get_pos())
obj_quat = np.array(entity.get_quat())
observation[f"{entity_name}_pos"] = obj_pos
observation[f"{entity_name}_quat"] = obj_quat
except Exception as e:
# Skip entities that don't have get_pos/get_quat methods
print(f"Warning: Could not get pose for entity '{entity_name}': {e}")
continue
Copy link
Owner

Choose a reason for hiding this comment

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

in stead of using try and except here, why don't we try to make sure the entity has get_pos and get_quat()


return observation

def _process_input(self) -> KeyboardCommand:
Expand Down
15 changes: 15 additions & 0 deletions src/env/gs_env/sim/envs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .manipulation import (
GoalReachingEnv,
HangLifebuoyEnv,
PickCubeEnv,
PutBowlInsideMicrowaveEnv,
SweepTableEnv,
)

__all__ = [
"GoalReachingEnv",
"HangLifebuoyEnv",
"PickCubeEnv",
"PutBowlInsideMicrowaveEnv",
"SweepTableEnv",
Copy link
Collaborator

@YilingQiao YilingQiao Sep 11, 2025

Choose a reason for hiding this comment

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

also pick_cube_env

]
33 changes: 33 additions & 0 deletions src/env/gs_env/sim/envs/config/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,36 @@
reward_args={},
img_resolution=(480, 270),
)


EnvArgsRegistry["put_bowl_inside_microwave_default"] = EnvArgs(
gs_init_args=GenesisInitArgsRegistry["default"],
scene_args=SceneArgsRegistry["flat_scene_default"],
robot_args=RobotArgsRegistry["franka_teleop"],
objects_args=[], # Objects are created directly in the environment
sensors_args=[],
reward_args={},
img_resolution=(480, 270),
)


EnvArgsRegistry["hang_lifebuoy_default"] = EnvArgs(
gs_init_args=GenesisInitArgsRegistry["default"],
scene_args=SceneArgsRegistry["flat_scene_default"],
robot_args=RobotArgsRegistry["franka_teleop"],
objects_args=[], # Objects are created directly in the environment
sensors_args=[],
reward_args={},
img_resolution=(480, 270),
)


EnvArgsRegistry["sweep_table_default"] = EnvArgs(
gs_init_args=GenesisInitArgsRegistry["default"],
scene_args=SceneArgsRegistry["flat_scene_default"],
robot_args=RobotArgsRegistry["franka_teleop"],
objects_args=[], # Objects are created directly in the environment
sensors_args=[],
reward_args={},
img_resolution=(480, 270),
)
8 changes: 8 additions & 0 deletions src/env/gs_env/sim/envs/manipulation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from .goal_reaching_env import GoalReachingEnv
from .hang_lifebuoy_env import HangLifebuoyEnv
from .pick_cube_env import PickCubeEnv
from .put_bowl_inside_microwave_env import PutBowlInsideMicrowaveEnv
from .sweep_table_env import SweepTableEnv

__all__ = [
"GoalReachingEnv",
"HangLifebuoyEnv",
"PickCubeEnv",
"PutBowlInsideMicrowaveEnv",
"SweepTableEnv",
]
Loading