Switch arm IK from world-frame to body-frame control so that pushing the XR controller forward always moves the arm in the robot's forward direction, regardless of chassis rotation. Key changes: - dual_arm_agent: convert EEF observations to body frame before passing to XR controller; send body-frame IK targets directly (removed convert_action_world_to_root) - xr_controller: XR deltas treated as body-frame deltas (no yaw rotation needed — VR view tracks robot heading naturally) - streaming: add debug frame save for stereo alignment diagnostics - mindrobot_2i_cfg: IdealPDActuator for trunk, disabled gravity - Author headers updated to Yutang Li, SIAT Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
124 lines
4.8 KiB
Python
124 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-FileCopyrightText: Copyright (c) 2022-2025, The Isaac Lab Project Developers.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
"""XR teleoperation entry point for MindBot.
|
|
|
|
Selects the appropriate agent class based on the task's action manager:
|
|
- DualArmHeadXrAgent (23D) for tasks with head_action (e.g. Isaac-MindRobot-2i-DualArm-IK-Abs-v0)
|
|
- DualArmXrAgent (20D) for dual-arm tasks without head
|
|
- SingleArmXrAgent (8D) for single-arm tasks (e.g. Isaac-MindRobot-LeftArm-IK-Abs-v0)
|
|
|
|
Usage:
|
|
~/IsaacLab/isaaclab.sh -p scripts/environments/teleoperation/teleop_xr_agent.py \\
|
|
--task Isaac-MindRobot-2i-DualArm-IK-Abs-v0 --stream-to <PICO_IP>
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
import os
|
|
|
|
# Ensure xr_utils (next to this script) is importable
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from isaaclab.app import AppLauncher
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# -- CLI arguments --
|
|
parser = argparse.ArgumentParser(
|
|
description="Teleoperation for Isaac Lab environments with PICO XR Controller (absolute IK mode)."
|
|
)
|
|
parser.add_argument("--num_envs", type=int, default=1)
|
|
parser.add_argument("--task", type=str, default="Isaac-MindRobot-LeftArm-IK-Abs-v0")
|
|
parser.add_argument("--sensitivity", type=float, default=None)
|
|
parser.add_argument("--pos_sensitivity", type=float, default=None)
|
|
parser.add_argument("--rot_sensitivity", type=float, default=None)
|
|
parser.add_argument("--arm", type=str, default="left", choices=["left", "right"])
|
|
parser.add_argument("--base_speed", type=float, default=5.0)
|
|
parser.add_argument("--base_turn", type=float, default=2.0)
|
|
parser.add_argument("--drive_speed", type=float, default=0.5)
|
|
parser.add_argument("--drive_turn", type=float, default=1.5)
|
|
parser.add_argument("--stream-to", type=str, default=None, dest="stream_to")
|
|
parser.add_argument("--stream-port", type=int, default=12345, dest="stream_port")
|
|
parser.add_argument("--stream-bitrate", type=int, default=20_000_000, dest="stream_bitrate")
|
|
parser.add_argument("--debug-viewports", action="store_true", dest="debug_viewports", default=True)
|
|
|
|
AppLauncher.add_app_launcher_args(parser)
|
|
args_cli = parser.parse_args()
|
|
app_launcher_args = vars(args_cli)
|
|
app_launcher_args["xr"] = False
|
|
|
|
app_launcher = AppLauncher(app_launcher_args)
|
|
simulation_app = app_launcher.app
|
|
|
|
# -- Post-launch imports (require Isaac Sim runtime) --
|
|
import gymnasium as gym
|
|
from isaaclab.envs import ManagerBasedRLEnvCfg
|
|
import isaaclab_tasks # noqa: F401
|
|
import mindbot.tasks # noqa: F401
|
|
from isaaclab_tasks.utils import parse_env_cfg
|
|
|
|
from xr_teleop import SingleArmXrAgent, DualArmXrAgent, DualArmHeadXrAgent
|
|
|
|
|
|
def main() -> None:
|
|
args = args_cli
|
|
|
|
# Resolve sensitivity
|
|
pos_sens = args.pos_sensitivity or args.sensitivity or 1.0
|
|
rot_sens = args.rot_sensitivity or args.sensitivity or 0.3
|
|
|
|
# Create environment
|
|
env_cfg = parse_env_cfg(args.task, num_envs=args.num_envs)
|
|
env_cfg.env_name = args.task
|
|
if not isinstance(env_cfg, ManagerBasedRLEnvCfg):
|
|
raise ValueError(f"Teleoperation requires ManagerBasedRLEnvCfg. Got: {type(env_cfg)}")
|
|
if "Abs" not in args.task:
|
|
raise ValueError(f"Task '{args.task}' is not an absolute IK task.")
|
|
env_cfg.terminations.time_out = None
|
|
|
|
env = gym.make(args.task, cfg=env_cfg).unwrapped
|
|
|
|
# Select agent based on task capabilities
|
|
action_terms = env.action_manager._terms
|
|
is_dual_arm = "left_arm_action" in action_terms
|
|
has_head = "head_action" in action_terms
|
|
|
|
if is_dual_arm and has_head:
|
|
print(f"[INFO] DualArmHeadXrAgent (23D) | pos_sens={pos_sens} rot_sens={rot_sens}")
|
|
agent = DualArmHeadXrAgent(
|
|
env, simulation_app,
|
|
pos_sensitivity=pos_sens, rot_sensitivity=rot_sens,
|
|
base_speed=args.base_speed, base_turn=args.base_turn,
|
|
drive_speed=args.drive_speed, drive_turn=args.drive_turn,
|
|
stream_to=args.stream_to, stream_port=args.stream_port,
|
|
stream_bitrate=args.stream_bitrate,
|
|
debug_viewports=args.debug_viewports or bool(args.stream_to),
|
|
)
|
|
elif is_dual_arm:
|
|
print(f"[INFO] DualArmXrAgent (20D) | pos_sens={pos_sens} rot_sens={rot_sens}")
|
|
agent = DualArmXrAgent(
|
|
env, simulation_app,
|
|
pos_sensitivity=pos_sens, rot_sensitivity=rot_sens,
|
|
base_speed=args.base_speed, base_turn=args.base_turn,
|
|
drive_speed=args.drive_speed, drive_turn=args.drive_turn,
|
|
debug_viewports=args.debug_viewports,
|
|
)
|
|
else:
|
|
print(f"[INFO] SingleArmXrAgent (8D) | arm={args.arm} pos_sens={pos_sens} rot_sens={rot_sens}")
|
|
agent = SingleArmXrAgent(
|
|
env, simulation_app,
|
|
arm=args.arm,
|
|
pos_sensitivity=pos_sens, rot_sensitivity=rot_sens,
|
|
debug_viewports=args.debug_viewports,
|
|
)
|
|
|
|
agent.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
simulation_app.close()
|