🎭 Mocap Control¶
Mocap (Motion Capture) bodies are special bodies in MuJoCo that can be controlled by directly setting their pose.
What is a Mocap Body?¶
- A body in MuJoCo with
body_mocapid != -1 - Its pose can be set directly without being affected by forces/dynamics
- Often used with equality constraints (WELD/CONNECT) to implement grasping/dragging
- Typical uses: anchor points, virtual hands, tool attachment points
Finding Mocap Bodies¶
Under the Euler system, OrcaGymEulerEnv does not directly expose a public method to list mocap names.
You can verify whether a given mocap body exists via env.data.mocap_pos(name)
(it raises KeyError if the name does not exist), or traverse via env.model.get_body_names()
(mocap bodies usually carry suffixes like Anchor).
# Read the pose of a known mocap body via env.data (raises if the name does not exist)
mocap_pos = env.data.mocap_pos("ActorManipulator_Anchor") # (3,)
mocap_quat = env.data.mocap_quat("ActorManipulator_Anchor") # (4,)
Note: the UI-grasp dedicated mocap body name is
ORCA_MANIPULATOR_<uuid>_Anchorin default levels, andActorManipulator_Anchorin legacy levels.
Setting Mocap Pose¶
# Directly set the world-frame pose of a mocap body
env.set_mocap_pos_and_quat({
"ActorManipulator_Anchor": {
"pos": np.array([0.5, 0.0, 0.8], dtype=np.float64),
"quat": np.array([1.0, 0.0, 0.0, 0.0], dtype=np.float64),
}
})
# Must call mj_forward() to refresh derived quantities
env.mj_forward()
Reading Mocap Pose¶
# Read mocap pose via env.data
mocap_pos = env.data.mocap_pos("ActorManipulator_Anchor") # (3,)
mocap_quat = env.data.mocap_quat("ActorManipulator_Anchor") # (4,)
Mocap + Equality Constraints = Object Manipulation¶
Under the Euler system, OrcaGymEulerEnv does not provide anchor_actor / release_body_anchored
high-level public methods (these exist only in the Local system). Programmatic operations must be
orchestrated with public equality-constraint primitives, consistent with the UI-grasp internal
methods _anchor_actor / _release_body_anchored:
equality_find_slot_by_body(body_name)— find the equality constraint slot containing the specified bodyequality_constraint(slot)— read the full data of the slot (for snapshot/restore)equality_update(slot, ...)— atomically write slot fields (type/obj1/obj2/data/active...)set_mocap_pos_and_quat(...)— align the mocap pose to the target body
import mujoco
# ── 1. Anchor an object ──
anchor_mocap_name = "ActorManipulator_Anchor" # or ORCA_MANIPULATOR_<uuid>_Anchor
actor_name = "target_object"
# Find the equality constraint slot containing the anchor mocap
slot = env.equality_find_slot_by_body(anchor_mocap_name)
if slot == -1:
raise ValueError(f"No equality slot containing {anchor_mocap_name} in the model")
# Save the original constraint snapshot (restore on release)
original_eq = env.equality_constraint(slot)
# Align the mocap pose to the actor's current pose (avoid yanking on the next frame)
actor_pose = env.get_body_xpos_xmat_xquat([actor_name])[actor_name]
env.set_mocap_pos_and_quat({
anchor_mocap_name: {
"pos": actor_pose["xpos"],
"quat": actor_pose["xquat"],
}
})
# Decide whether to change obj1 or obj2 (keep the mocap end, change the other end to actor)
mocap_id = env.model.body_name2id(anchor_mocap_name)
if original_eq["obj1_id"] == mocap_id:
new_obj1_name = anchor_mocap_name
new_obj2_name = actor_name
else:
new_obj1_name = actor_name
new_obj2_name = anchor_mocap_name
# Write the constraint (type/obj, internal mj_forward)
env.equality_update(
slot,
eq_type=mujoco.mjtEq.mjEQ_WELD,
obj1_name=new_obj1_name,
obj2_name=new_obj2_name,
)
# ── 2. Move the anchor → object follows ──
env.set_mocap_pos_and_quat({
anchor_mocap_name: {
"pos": new_target_pos,
"quat": new_target_quat,
}
})
env.mj_forward()
# ── 3. Release (restore the original constraint from the snapshot) ──
slot = env.equality_find_slot_by_body(actor_name)
if slot != -1:
env.equality_update(
slot,
eq_type=original_eq["type"],
obj1_name=env.model.body_id2name(original_eq["obj1_id"]),
obj2_name=env.model.body_id2name(original_eq["obj2_id"]),
data=original_eq["data"],
)
Note:
obj1_name/obj2_nameofequality_updatemust be full body names including the agent prefix (this primitive does not perform namespace resolution).
Trajectory Tracking Example¶
def follow_trajectory(env, trajectory: list[np.ndarray], duration: float):
"""Have the anchor follow a trajectory"""
steps = int(duration / env.dt)
for i in range(steps):
t = i / steps
idx = min(int(t * len(trajectory)), len(trajectory) - 1)
target_pos = trajectory[idx]
env.set_mocap_pos_and_quat({
"ActorManipulator_Anchor": {
"pos": target_pos,
"quat": np.array([1, 0, 0, 0]),
}
})
env.mj_forward()
env.render()