Some checks failed
nightly / Test against latest dependencies (py3.10) (push) Has been cancelled
nightly / Test against latest dependencies (py3.13) (push) Has been cancelled
tests / tests (3.13, locked) (push) Has been cancelled
tests / tests (3.13, unlocked) (push) Has been cancelled
tests / pyright (3.10) (push) Has been cancelled
tests / lint-format (push) Has been cancelled
tests / tests (3.10, locked) (push) Has been cancelled
tests / tests (3.11, locked) (push) Has been cancelled
tests / tests (3.12, locked) (push) Has been cancelled
tests / pyright (3.11) (push) Has been cancelled
tests / pyright (3.12) (push) Has been cancelled
tests / pyright (3.13) (push) Has been cancelled
tests / ty-check (3.10) (push) Has been cancelled
tests / ty-check (3.11) (push) Has been cancelled
tests / ty-check (3.12) (push) Has been cancelled
tests / ty-check (3.13) (push) Has been cancelled
tests / stubs (push) Has been cancelled
tests / smoke-test (push) Has been cancelled
Docker / check_paths (push) Has been cancelled
docs / build (push) Has been cancelled
Docker / build (push) Has been cancelled
Upstream: https://github.com/michaelgillett/mjlab Upstream-Commit: c19f713c415a699a79d71cd96aa13c3104a05047 Upstream-Branch: main
195 lines
5.7 KiB
Python
195 lines
5.7 KiB
Python
"""Interactive IK control demo.
|
|
|
|
Drag the 3D transform control in the viser viewer to move the YAM end-effector.
|
|
|
|
Run with:
|
|
MJLAB_WARP_QUIET=1 uv run scripts/demos/differential_ik.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from types import SimpleNamespace
|
|
|
|
import torch
|
|
import viser
|
|
|
|
from mjlab.asset_zoo.robots.i2rt_yam.yam_constants import get_yam_robot_cfg
|
|
from mjlab.entity import Entity, EntityCfg
|
|
from mjlab.envs.mdp.actions import DifferentialIKAction, DifferentialIKActionCfg
|
|
from mjlab.sim.sim import MujocoCfg, Simulation, SimulationCfg
|
|
from mjlab.utils.lab_api.math import quat_from_matrix
|
|
from mjlab.viewer.viser import ViserMujocoScene
|
|
|
|
DEMO_INIT_STATE = EntityCfg.InitialStateCfg(
|
|
pos=(0.0, 0.0, 0.01),
|
|
joint_pos={
|
|
"joint2": 0.6,
|
|
"joint3": 0.6,
|
|
"joint4": 0.0,
|
|
"left_finger": 0.037,
|
|
"right_finger": -0.037,
|
|
},
|
|
joint_vel={".*": 0.0},
|
|
)
|
|
|
|
IK_ITERATIONS = 10
|
|
|
|
|
|
def main() -> None:
|
|
device = "cuda:0" if torch.cuda.is_available() else "cpu"
|
|
|
|
robot_cfg = get_yam_robot_cfg()
|
|
robot_cfg.init_state = DEMO_INIT_STATE
|
|
entity = Entity(robot_cfg)
|
|
model = entity.compile()
|
|
sim_cfg = SimulationCfg(mujoco=MujocoCfg(gravity=(0, 0, -9.81)))
|
|
sim = Simulation(num_envs=1, cfg=sim_cfg, model=model, device=device)
|
|
entity.initialize(model, sim.model, sim.data, device)
|
|
entity.write_joint_position_to_sim(entity.data.default_joint_pos, joint_ids=None)
|
|
sim.forward()
|
|
|
|
env = SimpleNamespace(num_envs=1, device=device, scene={"robot": entity}, sim=sim)
|
|
ik_cfg = DifferentialIKActionCfg(
|
|
entity_name="robot",
|
|
actuator_names=("joint.*",),
|
|
frame_name="grasp_site",
|
|
frame_type="site",
|
|
posture_weight=0.02,
|
|
joint_limit_weight=1e-1,
|
|
damping=1e-1,
|
|
use_relative_mode=False,
|
|
)
|
|
ik_action: DifferentialIKAction = ik_cfg.build(env) # type: ignore[arg-type]
|
|
joint_ids = ik_action._joint_ids
|
|
|
|
grip_ids, _ = entity.find_joints("left_finger")
|
|
grip_joint_ids = torch.tensor(grip_ids, device=device, dtype=torch.long)
|
|
grip_open = torch.tensor([[0.037]], device=device)
|
|
|
|
server = viser.ViserServer(label="IK Control Demo")
|
|
scene = ViserMujocoScene(server, sim.mj_model, num_envs=1)
|
|
scene.create_scene_gui(
|
|
camera_distance=0.1,
|
|
camera_azimuth=135.0,
|
|
camera_elevation=30.0,
|
|
)
|
|
|
|
site_id = ik_action._frame_id
|
|
pos = sim.data.site_xpos[0, site_id].cpu().numpy()
|
|
xmat = sim.data.site_xmat[0, site_id]
|
|
quat = quat_from_matrix(xmat).cpu().numpy()
|
|
|
|
transform_ctrl = server.scene.add_transform_controls(
|
|
"/ik_target",
|
|
position=(float(pos[0]), float(pos[1]), float(pos[2])),
|
|
wxyz=(float(quat[0]), float(quat[1]), float(quat[2]), float(quat[3])),
|
|
scale=0.12,
|
|
)
|
|
|
|
needs_reset = [False]
|
|
|
|
with server.gui.add_folder("IK Control"):
|
|
reset_button = server.gui.add_button("Reset")
|
|
reset_button.on_click(lambda _: needs_reset.__setitem__(0, True))
|
|
iterations_slider = server.gui.add_slider(
|
|
"IK Iterations",
|
|
min=1,
|
|
max=50,
|
|
step=1,
|
|
initial_value=IK_ITERATIONS,
|
|
)
|
|
|
|
with server.gui.add_folder("IK Weights"):
|
|
damping_slider = server.gui.add_slider(
|
|
"Damping (λ)",
|
|
min=1e-2,
|
|
max=1.0,
|
|
step=1e-3,
|
|
initial_value=ik_cfg.damping,
|
|
)
|
|
pos_w_slider = server.gui.add_slider(
|
|
"Position Weight",
|
|
min=0.0,
|
|
max=10.0,
|
|
step=0.1,
|
|
initial_value=ik_cfg.position_weight,
|
|
)
|
|
ori_w_slider = server.gui.add_slider(
|
|
"Orientation Weight",
|
|
min=0.0,
|
|
max=10.0,
|
|
step=0.1,
|
|
initial_value=ik_cfg.orientation_weight,
|
|
)
|
|
jlim_w_slider = server.gui.add_slider(
|
|
"Joint Limit Weight",
|
|
min=0.0,
|
|
max=1.0,
|
|
step=0.01,
|
|
initial_value=ik_cfg.joint_limit_weight,
|
|
)
|
|
posture_w_slider = server.gui.add_slider(
|
|
"Posture Weight",
|
|
min=0.0,
|
|
max=1.0,
|
|
step=0.01,
|
|
initial_value=ik_cfg.posture_weight,
|
|
)
|
|
|
|
print("=" * 60)
|
|
print("IK Control Demo")
|
|
print(" Open the viser URL printed above")
|
|
print(" Drag the 3D transform control to move the end-effector")
|
|
print("=" * 60)
|
|
|
|
target_action = torch.zeros(1, 7, device=device)
|
|
|
|
def _reset() -> None:
|
|
entity.write_joint_position_to_sim(entity.data.default_joint_pos, joint_ids=None)
|
|
sim.forward()
|
|
ik_action.reset()
|
|
p = sim.data.site_xpos[0, site_id].cpu().numpy()
|
|
q = quat_from_matrix(sim.data.site_xmat[0, site_id]).cpu().numpy()
|
|
transform_ctrl.position = (float(p[0]), float(p[1]), float(p[2]))
|
|
transform_ctrl.wxyz = (float(q[0]), float(q[1]), float(q[2]), float(q[3]))
|
|
|
|
try:
|
|
while True:
|
|
if needs_reset[0]:
|
|
needs_reset[0] = False
|
|
_reset()
|
|
|
|
ik_cfg.damping = max(damping_slider.value, 1e-2)
|
|
ik_cfg.position_weight = max(pos_w_slider.value, 0.0)
|
|
ik_cfg.orientation_weight = max(ori_w_slider.value, 0.0)
|
|
ik_cfg.joint_limit_weight = max(jlim_w_slider.value, 0.0)
|
|
ik_cfg.posture_weight = max(posture_w_slider.value, 0.0)
|
|
|
|
p = transform_ctrl.position
|
|
w = transform_ctrl.wxyz
|
|
target_action[0, :3] = torch.tensor([p[0], p[1], p[2]], device=device)
|
|
target_action[0, 3:] = torch.tensor([w[0], w[1], w[2], w[3]], device=device)
|
|
ik_action.process_actions(target_action)
|
|
|
|
n_iter = int(iterations_slider.value)
|
|
for _ in range(n_iter):
|
|
dq = ik_action.compute_dq()
|
|
q = entity.data.joint_pos[:, joint_ids] + dq
|
|
entity.write_joint_position_to_sim(q, joint_ids=joint_ids)
|
|
entity.write_joint_position_to_sim(grip_open, joint_ids=grip_joint_ids)
|
|
sim.forward()
|
|
|
|
scene.update(sim.data)
|
|
if scene.needs_update:
|
|
scene.refresh_visualization()
|
|
|
|
time.sleep(1.0 / 30.0)
|
|
except KeyboardInterrupt:
|
|
print("\nShutting down...")
|
|
server.stop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|