mani-skill-nightly 2025.5.3.1545__py3-none-any.whl → 2025.5.3.1612__py3-none-any.whl

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.
@@ -403,6 +403,18 @@ class XArm6Robotiq(BaseAgent):
403
403
  )
404
404
  return torch.logical_and(lflag, rflag)
405
405
 
406
+ @staticmethod
407
+ def build_grasp_pose(approaching, closing, center):
408
+ """Build a grasp pose ()."""
409
+ assert np.abs(1 - np.linalg.norm(approaching)) < 1e-3
410
+ assert np.abs(1 - np.linalg.norm(closing)) < 1e-3
411
+ assert np.abs(approaching @ closing) <= 1e-3
412
+ ortho = np.cross(closing, approaching)
413
+ T = np.eye(4)
414
+ T[:3, :3] = np.stack([ortho, closing, approaching], axis=1)
415
+ T[:3, 3] = center
416
+ return sapien.Pose(T)
417
+
406
418
  def is_static(self, threshold: float = 0.2):
407
419
  qvel = self.robot.get_qvel()[..., :-6]
408
420
  return torch.max(torch.abs(qvel), 1)[0] <= threshold
@@ -131,7 +131,7 @@ class OpenCabinetDrawerEnv(BaseEnv):
131
131
  model_ids = self._batched_episode_rng.choice(self.all_model_ids)
132
132
  link_ids = self._batched_episode_rng.randint(0, 2**31)
133
133
 
134
- self._cabinets = []
134
+ self._cabinets: List[Articulation] = []
135
135
  handle_links: List[List[Link]] = []
136
136
  handle_links_meshes: List[List[trimesh.Trimesh]] = []
137
137
  for i, model_id in enumerate(model_ids):
@@ -163,7 +163,7 @@ class OpenCabinetDrawerEnv(BaseEnv):
163
163
  handle_links[-1].append(link)
164
164
  # save the first mesh in the link object that correspond with a handle
165
165
  handle_links_meshes[-1].append(
166
- link.generate_visual_mesh(
166
+ link.generate_mesh(
167
167
  filter=lambda _, render_shape: "handle"
168
168
  in render_shape.name,
169
169
  mesh_name="handle",
@@ -183,28 +183,29 @@ class PlugChargerEnv(BaseEnv):
183
183
  self.scene_builder.initialize(env_idx)
184
184
 
185
185
  # Initialize agent
186
- qpos = torch.tensor(
187
- [
188
- 0.0,
189
- np.pi / 8,
190
- 0,
191
- -np.pi * 5 / 8,
192
- 0,
193
- np.pi * 3 / 4,
194
- np.pi / 4,
195
- 0.04,
196
- 0.04,
197
- ]
198
- )
199
- qpos = (
200
- torch.normal(
201
- 0, self.robot_init_qpos_noise, (b, len(qpos)), device=self.device
186
+ if self.agent.uid == "panda_wristcam":
187
+ qpos = torch.tensor(
188
+ [
189
+ 0.0,
190
+ np.pi / 8,
191
+ 0,
192
+ -np.pi * 5 / 8,
193
+ 0,
194
+ np.pi * 3 / 4,
195
+ np.pi / 4,
196
+ 0.04,
197
+ 0.04,
198
+ ]
202
199
  )
203
- + qpos
204
- )
205
- qpos[:, -2:] = 0.04
206
- self.agent.robot.set_qpos(qpos)
207
- self.agent.robot.set_pose(sapien.Pose([-0.615, 0, 0]))
200
+ qpos = (
201
+ torch.normal(
202
+ 0, self.robot_init_qpos_noise, (b, len(qpos)), device=self.device
203
+ )
204
+ + qpos
205
+ )
206
+ qpos[:, -2:] = 0.04
207
+ self.agent.robot.set_qpos(qpos)
208
+ self.agent.robot.set_pose(sapien.Pose([-0.615, 0, 0]))
208
209
 
209
210
  # Initialize charger
210
211
  xy = randomization.uniform(
File without changes
@@ -0,0 +1,111 @@
1
+ import numpy as np
2
+ import mplib
3
+ from mani_skill.examples.motionplanning.panda.motionplanner import PandaArmMotionPlanningSolver
4
+ from mani_skill.envs.sapien_env import BaseEnv
5
+ import sapien.core as sapien
6
+
7
+ from mani_skill.utils.structs.pose import to_sapien_pose
8
+
9
+
10
+ class XArm6RobotiqMotionPlanningSolver(PandaArmMotionPlanningSolver):
11
+ CLOSED = 0.81
12
+ OPEN = 0
13
+
14
+ def __init__(self, env: BaseEnv, debug: bool = False, vis: bool = True, base_pose: sapien.Pose = None, visualize_target_grasp_pose: bool = True, print_env_info: bool = True, joint_vel_limits=0.9, joint_acc_limits=0.9):
15
+ super().__init__(env, debug, vis, base_pose, visualize_target_grasp_pose, print_env_info, joint_vel_limits, joint_acc_limits)
16
+ self.gripper_state = self.OPEN
17
+
18
+ def setup_planner(self):
19
+ link_names = [link.get_name() for link in self.robot.get_links()]
20
+ joint_names = [joint.get_name() for joint in self.robot.get_active_joints()]
21
+ planner = mplib.Planner(
22
+ urdf=self.env_agent.urdf_path,
23
+ srdf=self.env_agent.urdf_path.replace(".urdf", ".srdf"),
24
+ user_link_names=link_names,
25
+ user_joint_names=joint_names,
26
+ move_group="eef",
27
+ joint_vel_limits=np.ones(6) * self.joint_vel_limits,
28
+ joint_acc_limits=np.ones(6) * self.joint_acc_limits,
29
+ )
30
+ planner.set_base_pose(np.hstack([self.base_pose.p, self.base_pose.q]))
31
+ return planner
32
+
33
+ def open_gripper(self):
34
+ self.gripper_state = self.OPEN
35
+ qpos = self.robot.get_qpos()[0, :6].cpu().numpy()
36
+ for i in range(6):
37
+ if self.control_mode == "pd_joint_pos":
38
+ action = np.hstack([qpos, self.gripper_state])
39
+ else:
40
+ action = np.hstack([qpos, qpos * 0, self.gripper_state])
41
+ obs, reward, terminated, truncated, info = self.env.step(action)
42
+ self.elapsed_steps += 1
43
+ if self.print_env_info:
44
+ print(
45
+ f"[{self.elapsed_steps:3}] Env Output: reward={reward} info={info}"
46
+ )
47
+ if self.vis:
48
+ self.base_env.render_human()
49
+ return obs, reward, terminated, truncated, info
50
+
51
+ def close_gripper(self, t=6, gripper_state = CLOSED):
52
+ self.gripper_state = gripper_state
53
+ qpos = self.robot.get_qpos()[0, :6].cpu().numpy()
54
+ for i in range(t):
55
+ if self.control_mode == "pd_joint_pos":
56
+ action = np.hstack([qpos, self.gripper_state])
57
+ else:
58
+ action = np.hstack([qpos, qpos * 0, self.gripper_state])
59
+ obs, reward, terminated, truncated, info = self.env.step(action)
60
+ self.elapsed_steps += 1
61
+ if self.print_env_info:
62
+ print(
63
+ f"[{self.elapsed_steps:3}] Env Output: reward={reward} info={info}"
64
+ )
65
+ if self.vis:
66
+ self.base_env.render_human()
67
+ return obs, reward, terminated, truncated, info
68
+
69
+ def move_to_pose_with_RRTStar(
70
+ self, pose: sapien.Pose, dry_run: bool = False, refine_steps: int = 0
71
+ ):
72
+ pose = to_sapien_pose(pose)
73
+ if self.grasp_pose_visual is not None:
74
+ self.grasp_pose_visual.set_pose(pose)
75
+ pose = sapien.Pose(p=pose.p, q=pose.q)
76
+ result = self.planner.plan_qpos_to_pose(
77
+ np.concatenate([pose.p, pose.q]),
78
+ self.robot.get_qpos().cpu().numpy()[0],
79
+ time_step=self.base_env.control_timestep,
80
+ use_point_cloud=self.use_point_cloud,
81
+ rrt_range=0.0,
82
+ planning_time=1,
83
+ planner_name="RRTstar",
84
+ wrt_world=True,
85
+ )
86
+ if result["status"] != "Success":
87
+ print(result["status"])
88
+ self.render_wait()
89
+ return -1
90
+ self.render_wait()
91
+ if dry_run:
92
+ return result
93
+ return self.follow_path(result, refine_steps=refine_steps)
94
+
95
+
96
+ class XArm6PandaGripperMotionPlanningSolver(PandaArmMotionPlanningSolver):
97
+ def setup_planner(self):
98
+ link_names = [link.get_name() for link in self.robot.get_links()]
99
+ joint_names = [joint.get_name() for joint in self.robot.get_active_joints()]
100
+ planner = mplib.Planner(
101
+ urdf=self.env_agent.urdf_path,
102
+ srdf=self.env_agent.urdf_path.replace(".urdf", ".srdf"),
103
+ user_link_names=link_names,
104
+ user_joint_names=joint_names,
105
+ move_group="panda_hand_tcp",
106
+ joint_vel_limits=np.ones(6) * self.joint_vel_limits,
107
+ joint_acc_limits=np.ones(6) * self.joint_acc_limits,
108
+ )
109
+ planner.set_base_pose(np.hstack([self.base_pose.p, self.base_pose.q]))
110
+ return planner
111
+
@@ -0,0 +1,148 @@
1
+ import multiprocessing as mp
2
+ import os
3
+ from copy import deepcopy
4
+ import time
5
+ import argparse
6
+ import gymnasium as gym
7
+ import numpy as np
8
+ from tqdm import tqdm
9
+ import os.path as osp
10
+ from mani_skill.utils.wrappers.record import RecordEpisode
11
+ from mani_skill.trajectory.merge_trajectory import merge_trajectories
12
+ from mani_skill.examples.motionplanning.xarm6.solutions import solvePickCube, solvePushCube, solveStackCube, solvePlugCharger
13
+
14
+ MP_SOLUTIONS = {
15
+ "PickCube-v1": solvePickCube,
16
+ "PushCube-v1": solvePushCube,
17
+ "StackCube-v1": solveStackCube,
18
+ "PlugCharger-v1": solvePlugCharger,
19
+ }
20
+
21
+ def parse_args(args=None):
22
+ parser = argparse.ArgumentParser()
23
+ parser.add_argument("-e", "--env-id", type=str, default="PickCube-v1", help=f"Environment to run motion planning solver on. Available options are {list(MP_SOLUTIONS.keys())}")
24
+ parser.add_argument("-o", "--obs-mode", type=str, default="none", help="Observation mode to use. Usually this is kept as 'none' as observations are not necesary to be stored, they can be replayed later via the mani_skill.trajectory.replay_trajectory script.")
25
+ parser.add_argument("-n", "--num-traj", type=int, default=10, help="Number of trajectories to generate.")
26
+ parser.add_argument("--only-count-success", action="store_true", help="If true, generates trajectories until num_traj of them are successful and only saves the successful trajectories/videos")
27
+ parser.add_argument("--reward-mode", type=str)
28
+ parser.add_argument("-b", "--sim-backend", type=str, default="auto", help="Which simulation backend to use. Can be 'auto', 'cpu', 'gpu'")
29
+ parser.add_argument("--render-mode", type=str, default="rgb_array", help="can be 'sensors' or 'rgb_array' which only affect what is saved to videos")
30
+ parser.add_argument("--vis", action="store_true", help="whether or not to open a GUI to visualize the solution live")
31
+ parser.add_argument("--save-video", action="store_true", help="whether or not to save videos locally")
32
+ parser.add_argument("--traj-name", type=str, help="The name of the trajectory .h5 file that will be created.")
33
+ parser.add_argument("--shader", default="default", type=str, help="Change shader used for rendering. Default is 'default' which is very fast. Can also be 'rt' for ray tracing and generating photo-realistic renders. Can also be 'rt-fast' for a faster but lower quality ray-traced renderer")
34
+ parser.add_argument("--record-dir", type=str, default="demos", help="where to save the recorded trajectories")
35
+ parser.add_argument("--num-procs", type=int, default=1, help="Number of processes to use to help parallelize the trajectory replay process. This uses CPU multiprocessing and only works with the CPU simulation backend at the moment.")
36
+ return parser.parse_args()
37
+
38
+ def _main(args, proc_id: int = 0, start_seed: int = 0) -> str:
39
+ env_id = args.env_id
40
+ env = gym.make(
41
+ env_id,
42
+ obs_mode=args.obs_mode,
43
+ control_mode="pd_joint_pos",
44
+ render_mode=args.render_mode,
45
+ robot_uids="xarm6_robotiq",
46
+ reward_mode="dense" if args.reward_mode is None else args.reward_mode,
47
+ sensor_configs=dict(shader_pack=args.shader),
48
+ human_render_camera_configs=dict(shader_pack=args.shader),
49
+ viewer_camera_configs=dict(shader_pack=args.shader),
50
+ sim_backend=args.sim_backend
51
+ )
52
+ if env_id not in MP_SOLUTIONS:
53
+ raise RuntimeError(f"No already written motion planning solutions for {env_id}. Available options are {list(MP_SOLUTIONS.keys())}")
54
+
55
+ if not args.traj_name:
56
+ new_traj_name = time.strftime("%Y%m%d_%H%M%S")
57
+ else:
58
+ new_traj_name = args.traj_name
59
+
60
+ if args.num_procs > 1:
61
+ new_traj_name = new_traj_name + "." + str(proc_id)
62
+ env = RecordEpisode(
63
+ env,
64
+ output_dir=osp.join(args.record_dir, env_id, "motionplanning"),
65
+ trajectory_name=new_traj_name, save_video=args.save_video,
66
+ source_type="motionplanning",
67
+ source_desc="official motion planning solution from ManiSkill contributors",
68
+ video_fps=30,
69
+ save_on_reset=False
70
+ )
71
+ output_h5_path = env._h5_file.filename
72
+ solve = MP_SOLUTIONS[env_id]
73
+ print(f"Motion Planning Running on {env_id}")
74
+ pbar = tqdm(range(args.num_traj), desc=f"proc_id: {proc_id}")
75
+ seed = start_seed
76
+ successes = []
77
+ solution_episode_lengths = []
78
+ failed_motion_plans = 0
79
+ passed = 0
80
+ while True:
81
+ try:
82
+ res = solve(env, seed=seed, debug=False, vis=True if args.vis else False)
83
+ except Exception as e:
84
+ print(f"Cannot find valid solution because of an error in motion planning solution: {e}")
85
+ res = -1
86
+
87
+ if res == -1:
88
+ success = False
89
+ failed_motion_plans += 1
90
+ else:
91
+ success = res[-1]["success"].item()
92
+ elapsed_steps = res[-1]["elapsed_steps"].item()
93
+ solution_episode_lengths.append(elapsed_steps)
94
+ successes.append(success)
95
+ if args.only_count_success and not success:
96
+ seed += 1
97
+ env.flush_trajectory(save=False)
98
+ if args.save_video:
99
+ env.flush_video(save=False)
100
+ continue
101
+ else:
102
+ env.flush_trajectory()
103
+ if args.save_video:
104
+ env.flush_video()
105
+ pbar.update(1)
106
+ pbar.set_postfix(
107
+ dict(
108
+ success_rate=np.mean(successes),
109
+ failed_motion_plan_rate=failed_motion_plans / (seed + 1),
110
+ avg_episode_length=np.mean(solution_episode_lengths),
111
+ max_episode_length=np.max(solution_episode_lengths),
112
+ # min_episode_length=np.min(solution_episode_lengths)
113
+ )
114
+ )
115
+ seed += 1
116
+ passed += 1
117
+ if passed == args.num_traj:
118
+ break
119
+ env.close()
120
+ return output_h5_path
121
+
122
+ def main(args):
123
+ if args.num_procs > 1 and args.num_procs < args.num_traj:
124
+ if args.num_traj < args.num_procs:
125
+ raise ValueError("Number of trajectories should be greater than or equal to number of processes")
126
+ args.num_traj = args.num_traj // args.num_procs
127
+ seeds = [*range(0, args.num_procs * args.num_traj, args.num_traj)]
128
+ pool = mp.Pool(args.num_procs)
129
+ proc_args = [(deepcopy(args), i, seeds[i]) for i in range(args.num_procs)]
130
+ res = pool.starmap(_main, proc_args)
131
+ pool.close()
132
+ # Merge trajectory files
133
+ output_path = res[0][: -len("0.h5")] + "h5"
134
+ merge_trajectories(output_path, res)
135
+ for h5_path in res:
136
+ tqdm.write(f"Remove {h5_path}")
137
+ os.remove(h5_path)
138
+ json_path = h5_path.replace(".h5", ".json")
139
+ tqdm.write(f"Remove {json_path}")
140
+ os.remove(json_path)
141
+ else:
142
+ _main(args)
143
+
144
+ if __name__ == "__main__":
145
+ # start = time.time()
146
+ mp.set_start_method("spawn")
147
+ main(parse_args())
148
+ # print(f"Total time taken: {time.time() - start}")
@@ -0,0 +1,4 @@
1
+ from .pick_cube import solve as solvePickCube
2
+ from .push_cube import solve as solvePushCube
3
+ from .stack_cube import solve as solveStackCube
4
+ from .plug_charger import solve as solvePlugCharger
@@ -0,0 +1,66 @@
1
+ import numpy as np
2
+ import sapien
3
+
4
+ from mani_skill.envs.tasks import PickCubeEnv
5
+ from mani_skill.examples.motionplanning.xarm6.motionplanner import \
6
+ XArm6RobotiqMotionPlanningSolver, XArm6PandaGripperMotionPlanningSolver
7
+ from mani_skill.examples.motionplanning.panda.utils import (
8
+ compute_grasp_info_by_obb, get_actor_obb)
9
+
10
+ def solve(env: PickCubeEnv, seed=None, debug=False, vis=False):
11
+ env.reset(seed=seed)
12
+
13
+ if env.unwrapped.robot_uids == "xarm6_robotiq":
14
+ planner_cls = XArm6RobotiqMotionPlanningSolver
15
+ elif env.unwrapped.robot_uids == "xarm6_pandagripper":
16
+ planner_cls = XArm6PandaGripperMotionPlanningSolver
17
+ else:
18
+ raise ValueError(f"Unsupported robot uid: {env.robot_uid}")
19
+ planner = planner_cls(
20
+ env,
21
+ debug=debug,
22
+ vis=vis,
23
+ base_pose=env.unwrapped.agent.robot.pose,
24
+ visualize_target_grasp_pose=vis,
25
+ print_env_info=False,
26
+ )
27
+
28
+ FINGER_LENGTH = 0.025
29
+ env = env.unwrapped
30
+
31
+ # retrieves the object oriented bounding box (trimesh box object)
32
+ obb = get_actor_obb(env.cube)
33
+
34
+ approaching = np.array([0, 0, -1])
35
+ # get transformation matrix of the tcp pose, is default batched and on torch
36
+ target_closing = env.agent.tcp.pose.to_transformation_matrix()[0, :3, 1].cpu().numpy()
37
+ # we can build a simple grasp pose using this information
38
+ grasp_info = compute_grasp_info_by_obb(
39
+ obb,
40
+ approaching=approaching,
41
+ target_closing=target_closing,
42
+ depth=FINGER_LENGTH,
43
+ )
44
+ closing, center = grasp_info["closing"], grasp_info["center"]
45
+ grasp_pose = env.agent.build_grasp_pose(approaching, closing, env.cube.pose.sp.p)
46
+
47
+ # -------------------------------------------------------------------------- #
48
+ # Reach
49
+ # -------------------------------------------------------------------------- #
50
+ reach_pose = grasp_pose * sapien.Pose([0, 0, -0.05])
51
+ planner.move_to_pose_with_RRTStar(reach_pose)
52
+
53
+ # -------------------------------------------------------------------------- #
54
+ # Grasp
55
+ # -------------------------------------------------------------------------- #
56
+ planner.move_to_pose_with_screw(grasp_pose)
57
+ planner.close_gripper()
58
+
59
+ # -------------------------------------------------------------------------- #
60
+ # Move to goal pose
61
+ # -------------------------------------------------------------------------- #
62
+ goal_pose = sapien.Pose(env.goal_site.pose.sp.p, grasp_pose.q)
63
+ res = planner.move_to_pose_with_screw(goal_pose)
64
+
65
+ planner.close()
66
+ return res
@@ -0,0 +1,111 @@
1
+ import gymnasium as gym
2
+ import numpy as np
3
+ import sapien.core as sapien
4
+ import trimesh
5
+ from tqdm import tqdm
6
+ from transforms3d.euler import euler2quat
7
+
8
+ from mani_skill.envs.tasks import PlugChargerEnv
9
+ from mani_skill.examples.motionplanning.xarm6.motionplanner import \
10
+ XArm6RobotiqMotionPlanningSolver, XArm6PandaGripperMotionPlanningSolver
11
+ from mani_skill.examples.motionplanning.panda.utils import (
12
+ compute_grasp_info_by_obb, get_actor_obb)
13
+
14
+
15
+ def main():
16
+ env: PlugChargerEnv = gym.make(
17
+ "PlugCharger-v1",
18
+ obs_mode="none",
19
+ control_mode="pd_joint_pos",
20
+ render_mode="rgb_array",
21
+ reward_mode="sparse",
22
+ )
23
+ for seed in tqdm(range(100)):
24
+ res = solve(env, seed=seed, debug=False, vis=True)
25
+ print(res[-1])
26
+ env.close()
27
+
28
+
29
+ def solve(env: PlugChargerEnv, seed=None, debug=False, vis=False):
30
+ env.reset(seed=seed)
31
+ assert env.unwrapped.control_mode in [
32
+ "pd_joint_pos",
33
+ "pd_joint_pos_vel",
34
+ ], env.unwrapped.control_mode
35
+ if env.unwrapped.robot_uids == "xarm6_robotiq":
36
+ planner_cls = XArm6RobotiqMotionPlanningSolver
37
+ elif env.unwrapped.robot_uids == "xarm6_pandagripper":
38
+ planner_cls = XArm6PandaGripperMotionPlanningSolver
39
+ else:
40
+ raise ValueError(f"Unsupported robot uid: {env.robot_uid}")
41
+ planner = planner_cls(
42
+ env,
43
+ debug=debug,
44
+ vis=vis,
45
+ base_pose=env.unwrapped.agent.robot.pose,
46
+ visualize_target_grasp_pose=False,
47
+ print_env_info=False,
48
+ joint_vel_limits=0.5,
49
+ joint_acc_limits=0.5,
50
+ )
51
+
52
+ FINGER_LENGTH = 0.025
53
+ env = env.unwrapped
54
+ charger_base_pose = env.charger_base_pose
55
+ charger_base_size = np.array(env.unwrapped._base_size) * 2
56
+
57
+ obb = trimesh.primitives.Box(
58
+ extents=charger_base_size,
59
+ transform=charger_base_pose.sp.to_transformation_matrix(),
60
+ )
61
+
62
+ approaching = np.array([0, 0, -1])
63
+ target_closing = env.agent.tcp.pose.sp.to_transformation_matrix()[:3, 1]
64
+ grasp_info = compute_grasp_info_by_obb(
65
+ obb,
66
+ approaching=approaching,
67
+ target_closing=target_closing,
68
+ depth=FINGER_LENGTH,
69
+ )
70
+ closing, center = grasp_info["closing"], grasp_info["center"]
71
+ grasp_pose = env.agent.build_grasp_pose(approaching, closing, center)
72
+
73
+ # add a angle to grasp
74
+ grasp_angle = np.deg2rad(15)
75
+ grasp_pose = grasp_pose * sapien.Pose(q=euler2quat(0, grasp_angle, 0))
76
+
77
+ # -------------------------------------------------------------------------- #
78
+ # Reach
79
+ # -------------------------------------------------------------------------- #
80
+ reach_pose = grasp_pose * sapien.Pose([0, 0, -0.05])
81
+ planner.move_to_pose_with_RRTStar(reach_pose)
82
+
83
+ # -------------------------------------------------------------------------- #
84
+ # Grasp
85
+ # -------------------------------------------------------------------------- #
86
+ planner.move_to_pose_with_screw(grasp_pose)
87
+ planner.close_gripper()
88
+
89
+ # -------------------------------------------------------------------------- #
90
+ # Align
91
+ # -------------------------------------------------------------------------- #
92
+ pre_insert_pose = (
93
+ env.goal_pose.sp
94
+ * sapien.Pose([-0.05, 0.0, 0.0])
95
+ * env.charger.pose.sp.inv()
96
+ * env.agent.tcp.pose.sp
97
+ )
98
+ insert_pose = env.goal_pose.sp * env.charger.pose.sp.inv() * env.agent.tcp.pose.sp * sapien.Pose([0, 0, -0.005])
99
+ planner.move_to_pose_with_screw(pre_insert_pose, refine_steps=0)
100
+ planner.move_to_pose_with_screw(pre_insert_pose, refine_steps=5)
101
+ # -------------------------------------------------------------------------- #
102
+ # Insert
103
+ # -------------------------------------------------------------------------- #
104
+ res = planner.move_to_pose_with_screw(insert_pose)
105
+
106
+ planner.close()
107
+ return res
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
@@ -0,0 +1,38 @@
1
+ import numpy as np
2
+ import sapien
3
+
4
+ from mani_skill.envs.tasks import PushCubeEnv
5
+ from mani_skill.examples.motionplanning.xarm6.motionplanner import \
6
+ XArm6RobotiqMotionPlanningSolver, XArm6PandaGripperMotionPlanningSolver
7
+
8
+ def solve(env: PushCubeEnv, seed=None, debug=False, vis=False):
9
+ env.reset(seed=seed)
10
+ if env.unwrapped.robot_uids == "xarm6_robotiq":
11
+ planner_cls = XArm6RobotiqMotionPlanningSolver
12
+ elif env.unwrapped.robot_uids == "xarm6_pandagripper":
13
+ planner_cls = XArm6PandaGripperMotionPlanningSolver
14
+ else:
15
+ raise ValueError(f"Unsupported robot uid: {env.robot_uid}")
16
+ planner = planner_cls(
17
+ env,
18
+ debug=debug,
19
+ vis=vis,
20
+ base_pose=env.unwrapped.agent.robot.pose,
21
+ visualize_target_grasp_pose=vis,
22
+ print_env_info=False,
23
+ )
24
+
25
+ FINGER_LENGTH = 0.025
26
+ env = env.unwrapped
27
+ planner.close_gripper()
28
+ reach_pose = sapien.Pose(p=env.obj.pose.sp.p + np.array([-0.05, 0, 0]), q=env.agent.tcp.pose.sp.q)
29
+ planner.move_to_pose_with_RRTStar(reach_pose)
30
+
31
+ # -------------------------------------------------------------------------- #
32
+ # Move to goal pose
33
+ # -------------------------------------------------------------------------- #
34
+ goal_pose = sapien.Pose(p=env.goal_region.pose.sp.p,q=env.agent.tcp.pose.sp.q)
35
+ res = planner.move_to_pose_with_screw(goal_pose)
36
+
37
+ planner.close()
38
+ return res
@@ -0,0 +1,92 @@
1
+ import argparse
2
+ import gymnasium as gym
3
+ import numpy as np
4
+ import sapien
5
+ from transforms3d.euler import euler2quat
6
+
7
+ from mani_skill.envs.tasks import StackCubeEnv
8
+ from mani_skill.examples.motionplanning.xarm6.motionplanner import \
9
+ XArm6RobotiqMotionPlanningSolver, XArm6PandaGripperMotionPlanningSolver
10
+ from mani_skill.examples.motionplanning.panda.utils import (
11
+ compute_grasp_info_by_obb, get_actor_obb)
12
+ from mani_skill.utils.wrappers.record import RecordEpisode
13
+
14
+ def solve(env: StackCubeEnv, seed=None, debug=False, vis=False):
15
+ env.reset(seed=seed)
16
+ assert env.unwrapped.control_mode in [
17
+ "pd_joint_pos",
18
+ "pd_joint_pos_vel",
19
+ ], env.unwrapped.control_mode
20
+ if env.unwrapped.robot_uids == "xarm6_robotiq":
21
+ planner_cls = XArm6RobotiqMotionPlanningSolver
22
+ elif env.unwrapped.robot_uids == "xarm6_pandagripper":
23
+ planner_cls = XArm6PandaGripperMotionPlanningSolver
24
+ else:
25
+ raise ValueError(f"Unsupported robot uid: {env.robot_uid}")
26
+ planner = planner_cls(
27
+ env,
28
+ debug=debug,
29
+ vis=vis,
30
+ base_pose=env.unwrapped.agent.robot.pose,
31
+ visualize_target_grasp_pose=vis,
32
+ print_env_info=False,
33
+ )
34
+ FINGER_LENGTH = 0.025
35
+ env = env.unwrapped
36
+ obb = get_actor_obb(env.cubeA)
37
+
38
+ approaching = np.array([0, 0, -1])
39
+ target_closing = env.agent.tcp.pose.to_transformation_matrix()[0, :3, 1].numpy()
40
+ grasp_info = compute_grasp_info_by_obb(
41
+ obb,
42
+ approaching=approaching,
43
+ target_closing=target_closing,
44
+ depth=FINGER_LENGTH,
45
+ )
46
+ closing, center = grasp_info["closing"], grasp_info["center"]
47
+ grasp_pose = env.agent.build_grasp_pose(approaching, closing, center)
48
+
49
+ # Search a valid pose
50
+ angles = np.arange(0, np.pi * 2 / 3, np.pi / 2)
51
+ angles = np.repeat(angles, 2)
52
+ angles[1::2] *= -1
53
+ for angle in angles:
54
+ delta_pose = sapien.Pose(q=euler2quat(0, 0, angle))
55
+ grasp_pose2 = grasp_pose * delta_pose
56
+ res = planner.move_to_pose_with_RRTStar(grasp_pose2, dry_run=True)
57
+ if res == -1:
58
+ continue
59
+ grasp_pose = grasp_pose2
60
+ break
61
+ else:
62
+ print("Fail to find a valid grasp pose")
63
+
64
+ # -------------------------------------------------------------------------- #
65
+ # Reach
66
+ # -------------------------------------------------------------------------- #
67
+ reach_pose = grasp_pose * sapien.Pose([0, 0, -0.05])
68
+ planner.move_to_pose_with_RRTStar(reach_pose)
69
+
70
+ # -------------------------------------------------------------------------- #
71
+ # Grasp
72
+ # -------------------------------------------------------------------------- #
73
+ planner.move_to_pose_with_screw(grasp_pose)
74
+ planner.close_gripper()
75
+
76
+ # -------------------------------------------------------------------------- #
77
+ # Lift
78
+ # -------------------------------------------------------------------------- #
79
+ lift_pose = sapien.Pose([0, 0, 0.1]) * grasp_pose
80
+ planner.move_to_pose_with_screw(lift_pose)
81
+
82
+ # -------------------------------------------------------------------------- #
83
+ # Stack
84
+ # -------------------------------------------------------------------------- #
85
+ goal_pose = env.cubeB.pose * sapien.Pose([0, 0, env.cube_half_size[2] * 2])
86
+ offset = (goal_pose.p - env.cubeA.pose.p).numpy()[0] # remember that all data in ManiSkill is batched and a torch tensor
87
+ align_pose = sapien.Pose(lift_pose.p + offset, lift_pose.q)
88
+ planner.move_to_pose_with_RRTStar(align_pose)
89
+
90
+ res = planner.open_gripper()
91
+ planner.close()
92
+ return res
@@ -185,6 +185,7 @@ def initialize_data_sources():
185
185
  )
186
186
  DATA_SOURCES["xarm6"] = DataSource(
187
187
  source_type="robot",
188
+ # TODO: update to new url with .convex.stl assets for xarm6 to support motion planning
188
189
  url="https://github.com/haosulab/ManiSkill-XArm6/archive/refs/tags/v0.1.0.zip",
189
190
  target_path="robots/xarm6",
190
191
  )
@@ -242,27 +242,33 @@ class Actor(PhysxRigidDynamicComponentStruct[sapien.Entity]):
242
242
  cg[group] = value
243
243
  cs.set_collision_groups(cg)
244
244
 
245
- def get_first_collision_mesh(self, to_world_frame: bool = True) -> trimesh.Trimesh:
245
+ def get_first_collision_mesh(
246
+ self, to_world_frame: bool = True
247
+ ) -> Union[trimesh.Trimesh, None]:
246
248
  """
247
249
  Returns the collision mesh of the first managed actor object. Note results of this are not cached or optimized at the moment
248
- so this function can be slow if called too often
250
+ so this function can be slow if called too often. Some actors have no collision meshes, in which case this function returns None
249
251
 
250
252
  Args:
251
253
  to_world_frame (bool): Whether to transform the collision mesh pose to the world frame
252
254
  """
253
- return self.get_collision_meshes(to_world_frame=to_world_frame, first_only=True)
255
+ mesh = self.get_collision_meshes(to_world_frame=to_world_frame, first_only=True)
256
+ if isinstance(mesh, trimesh.Trimesh):
257
+ return mesh
258
+ return None
254
259
 
255
260
  def get_collision_meshes(
256
261
  self, to_world_frame: bool = True, first_only: bool = False
257
- ) -> List[trimesh.Trimesh]:
262
+ ) -> Union[List[trimesh.Trimesh], trimesh.Trimesh]:
258
263
  """
259
264
  Returns the collision mesh of each managed actor object. Note results of this are not cached or optimized at the moment
260
- so this function can be slow if called too often
265
+ so this function can be slow if called too often. Some actors have no collision meshes, in which case this function returns an empty list.
261
266
 
262
267
  Args:
263
268
  to_world_frame (bool): Whether to transform the collision mesh pose to the world frame
264
269
  first_only (bool): Whether to return the collision mesh of just the first actor managed by this object. If True,
265
- this also returns a single Trimesh.Mesh object instead of a list
270
+ this also returns a single Trimesh.Mesh object instead of a list. This can be useful for efficiency reasons if you know
271
+ ahead of time all of the managed actors have the same collision mesh
266
272
  """
267
273
  assert (
268
274
  not self.merged
@@ -274,9 +280,12 @@ class Actor(PhysxRigidDynamicComponentStruct[sapien.Entity]):
274
280
  actor_meshes = []
275
281
  for comp in actor.components:
276
282
  if isinstance(comp, physx.PhysxRigidBaseComponent):
277
- actor_meshes.append(merge_meshes(get_component_meshes(comp)))
283
+ merged = merge_meshes(get_component_meshes(comp))
284
+ if merged is not None:
285
+ actor_meshes.append(merged)
278
286
  mesh = merge_meshes(actor_meshes)
279
- meshes.append(mesh)
287
+ if mesh is not None:
288
+ meshes.append(mesh)
280
289
  if first_only:
281
290
  break
282
291
  if to_world_frame:
@@ -287,6 +296,8 @@ class Actor(PhysxRigidDynamicComponentStruct[sapien.Entity]):
287
296
  mesh.apply_transform(mat[i].sp.to_transformation_matrix())
288
297
  else:
289
298
  mesh.apply_transform(mat.sp.to_transformation_matrix())
299
+ if len(meshes) == 0:
300
+ return []
290
301
  if first_only:
291
302
  return meshes[0]
292
303
  return meshes
@@ -320,19 +320,24 @@ class Articulation(BaseStruct[physx.PhysxArticulation]):
320
320
  # g0, g1, g2, g3 = s.get_collision_groups()
321
321
  # s.set_collision_groups([g0, g1, g2 | (1 << 29), g3])
322
322
 
323
- def get_first_collision_mesh(self, to_world_frame: bool = True) -> trimesh.Trimesh:
323
+ def get_first_collision_mesh(
324
+ self, to_world_frame: bool = True
325
+ ) -> Union[trimesh.Trimesh, None]:
324
326
  """
325
327
  Returns the collision mesh of the first managed articulation object. Note results of this are not cached or optimized at the moment
326
- so this function can be slow if called too often
328
+ so this function can be slow if called too often. Some articulations have no collision meshes, in which case this function returns None
327
329
 
328
330
  Args:
329
331
  to_world_frame (bool): Whether to transform the collision mesh pose to the world frame
330
332
  """
331
- return self.get_collision_meshes(to_world_frame=to_world_frame, first_only=True)
333
+ mesh = self.get_collision_meshes(to_world_frame=to_world_frame, first_only=True)
334
+ if isinstance(mesh, trimesh.Trimesh):
335
+ return mesh
336
+ return None
332
337
 
333
338
  def get_collision_meshes(
334
339
  self, to_world_frame: bool = True, first_only: bool = False
335
- ) -> List[trimesh.Trimesh]:
340
+ ) -> Union[List[trimesh.Trimesh], trimesh.Trimesh]:
336
341
  """
337
342
  Returns the collision mesh of each managed articulation object. Note results of this are not cached or optimized at the moment
338
343
  so this function can be slow if called too often
@@ -340,7 +345,8 @@ class Articulation(BaseStruct[physx.PhysxArticulation]):
340
345
  Args:
341
346
  to_world_frame (bool): Whether to transform the collision mesh pose to the world frame
342
347
  first_only (bool): Whether to return the collision mesh of just the first articulation managed by this object. If True,
343
- this also returns a single Trimesh.Mesh object instead of a list
348
+ this also returns a single Trimesh.Mesh object instead of a list. This can be useful for efficiency reasons if you know
349
+ ahead of time all of the managed actors have the same collision mesh
344
350
  """
345
351
  assert (
346
352
  not self.merged
@@ -365,9 +371,12 @@ class Articulation(BaseStruct[physx.PhysxArticulation]):
365
371
  link_mesh.apply_transform(pose.sp.to_transformation_matrix())
366
372
  art_meshes.append(link_mesh)
367
373
  mesh = merge_meshes(art_meshes)
368
- meshes.append(mesh)
374
+ if mesh is not None:
375
+ meshes.append(mesh)
369
376
  if first_only:
370
377
  break
378
+ if len(meshes) == 0:
379
+ return []
371
380
  if first_only:
372
381
  return meshes[0]
373
382
  return meshes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mani-skill-nightly
3
- Version: 2025.5.3.1545
3
+ Version: 2025.5.3.1612
4
4
  Summary: ManiSkill3: A Unified Benchmark for Generalizable Manipulation Skills
5
5
  Home-page: https://github.com/haosulab/ManiSkill
6
6
  Author: ManiSkill contributors
@@ -65,7 +65,7 @@ mani_skill/agents/robots/xarm/__init__.py,sha256=6Mhn4vV4f9XxcK493U5W9VE6yGGgydP
65
65
  mani_skill/agents/robots/xarm/xarm7_ability.py,sha256=yj7CUBQpbGVUiT22qweJKTniJE0DxdEyyKj329vr0HY,6106
66
66
  mani_skill/agents/robots/xarm6/__init__.py,sha256=0r19OsKmm1ssKB5Rrie8syWQvpXNooVOv6m-iygrdM0,109
67
67
  mani_skill/agents/robots/xarm6/xarm6_nogripper.py,sha256=FPhOpWQw5RPsSHLhZ9JWjYeh25GboO4I5_Hn05Ub84Q,7379
68
- mani_skill/agents/robots/xarm6/xarm6_robotiq.py,sha256=FIc_BvV7x5Fs9GsE5-m88NaSjPitnDYIGU2ZJbqE-m4,15651
68
+ mani_skill/agents/robots/xarm6/xarm6_robotiq.py,sha256=80DsYdfcPbqL-o1VjZ_KkTWweEH1a9NS9y4lFHCtMSk,16129
69
69
  mani_skill/assets/maniskill2-scene-2.glb,sha256=C5om9o9r6B-fWoauzNfUm2WV5sh8Nf7AvZRlYo1-IXQ,4737204
70
70
  mani_skill/assets/deformable_manipulation/beaker.glb,sha256=MMaoH6OruVSzO8CKuK2AMyaxA5kjsbbDQXyTVycWsPI,18104
71
71
  mani_skill/assets/deformable_manipulation/bottle.glb,sha256=AHWoATBEBeesfbiYNfSB0O0PWhsH0oa2wUBv79w9AVA,36476
@@ -587,7 +587,7 @@ mani_skill/envs/tasks/humanoid/assets/cardboard_box/01.png,sha256=UAzLp_o885YkTu
587
587
  mani_skill/envs/tasks/humanoid/assets/cardboard_box/box.mtl,sha256=E7F4ELdSP2Yd62T_rF_-gN1xa6N6Eo3AqAb17R5TSa8,219
588
588
  mani_skill/envs/tasks/humanoid/assets/cardboard_box/textured.obj,sha256=XQEt4EeMiW4kx4GhNOYk8TPi7lSuS6XTEBQjWSsxKJ0,12723
589
589
  mani_skill/envs/tasks/mobile_manipulation/__init__.py,sha256=GQajIA-t_p_9Hrhkj5-MEEmbGfaIkCYgaDepVUdhJ2Q,98
590
- mani_skill/envs/tasks/mobile_manipulation/open_cabinet_drawer.py,sha256=RBSjXseWSulU76gMIkDdAAhghZ2Wp38qF3hxb21UI8I,15925
590
+ mani_skill/envs/tasks/mobile_manipulation/open_cabinet_drawer.py,sha256=xokzhopVVnNJJjm0kyw7OlvrdADmIz7rkxyXG6XPj9E,15938
591
591
  mani_skill/envs/tasks/mobile_manipulation/robocasa/__init__.py,sha256=mjpK3FNM5GnR4CaWfD1i2gVk5tU4N6tTkDm2pCqsfoo,40
592
592
  mani_skill/envs/tasks/mobile_manipulation/robocasa/kitchen.py,sha256=fXLoTIDgk-0eEaH7TrCODYWfVOVkGP6cRzinyfKEkWc,25625
593
593
  mani_skill/envs/tasks/quadruped/__init__.py,sha256=_AHS3H9Cj2XT7--5jwW1NMmVWbZxRpdGDNXudT6OrZM,92
@@ -602,7 +602,7 @@ mani_skill/envs/tasks/tabletop/pick_cube.py,sha256=dQC3smYUttMLJHXAFMO31vFZKzQ4N
602
602
  mani_skill/envs/tasks/tabletop/pick_cube_cfgs.py,sha256=Zz_F3QI_sSqKNDsngp1mzPgkiyewVu47N2iHz522uOg,2166
603
603
  mani_skill/envs/tasks/tabletop/pick_single_ycb.py,sha256=mrqEoOa9UVF34Z5fpsvjcr683diUffsKEjJ9Zh0qfFU,10409
604
604
  mani_skill/envs/tasks/tabletop/place_sphere.py,sha256=J3ReBFK7TyZQlleIFspz7Pl1wqAzaYoveGZfNNL5DVM,10101
605
- mani_skill/envs/tasks/tabletop/plug_charger.py,sha256=0g-rkNf-oo2ovttlcQ58jqUq6So4SKvYMTHORZUOi_0,10571
605
+ mani_skill/envs/tasks/tabletop/plug_charger.py,sha256=jgLD2o0eRj-raAEh3ma8OLZ09es6wg8wX1ckWBdr53o,10710
606
606
  mani_skill/envs/tasks/tabletop/poke_cube.py,sha256=KV6mp-Xgm9h4GYUcAUop2AZ4IECTdQKEMRRd9NThyBo,9343
607
607
  mani_skill/envs/tasks/tabletop/pull_cube.py,sha256=txtWuIRFzTKxJtWbrygxVEgOPDvm73WlERXldjlUB-I,5546
608
608
  mani_skill/envs/tasks/tabletop/pull_cube_tool.py,sha256=NaZpdbYYL4zC41GVY__eq4uRIQpVXthzAqe5oSq8YWU,9951
@@ -669,6 +669,14 @@ mani_skill/examples/motionplanning/panda/solutions/pull_cube.py,sha256=rQkvCRYjV
669
669
  mani_skill/examples/motionplanning/panda/solutions/pull_cube_tool.py,sha256=g6adx921V2SOVYYFlh_gLwV5I0tnz70qCLm81oA6YhA,3609
670
670
  mani_skill/examples/motionplanning/panda/solutions/push_cube.py,sha256=EynyseBJ_njMP74o9gVxqWOOqoC5j1rBc4XQzFug9EQ,1113
671
671
  mani_skill/examples/motionplanning/panda/solutions/stack_cube.py,sha256=QIa280jNOJfJqqgbb5WWEBxErFPE7Mv4-_ZL9TCsRos,3280
672
+ mani_skill/examples/motionplanning/xarm6/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
673
+ mani_skill/examples/motionplanning/xarm6/motionplanner.py,sha256=OcJ2OgY09j3kKl7tFa8GcuGGknF999I_bWZGHZF8lps,4751
674
+ mani_skill/examples/motionplanning/xarm6/run.py,sha256=yejnUUasyllXZYWpWD9ksvdjgM8o9TwGVfY24kXQxSI,7053
675
+ mani_skill/examples/motionplanning/xarm6/solutions/__init__.py,sha256=csJg82Tjow5MsYEQEY86ETdPoR59s55KwSg4xaEVyy0,191
676
+ mani_skill/examples/motionplanning/xarm6/solutions/pick_cube.py,sha256=7gKuTgGrQOuSQr32nZDg07IRk2Dp9jIRCl3TZ1xlPsc,2566
677
+ mani_skill/examples/motionplanning/xarm6/solutions/plug_charger.py,sha256=myvHjl9Gc2RM2UYb12cOi56C3QbEbeNmvUBLMiHcHp0,3895
678
+ mani_skill/examples/motionplanning/xarm6/solutions/push_cube.py,sha256=iiZHwvOGGAaxPkzE_sAN6WdTYfcxEMVx0Da3e2rzL48,1419
679
+ mani_skill/examples/motionplanning/xarm6/solutions/stack_cube.py,sha256=zarkqZ2MSfBu-Uv7DNNiRmqyv_JLPiIWcpONGyfbf0Y,3595
672
680
  mani_skill/examples/teleoperation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
673
681
  mani_skill/examples/teleoperation/interactive_panda.py,sha256=NsGy_ghtXl3HPbwLjKINkizOXqX_rMr30lUfscmhyQ4,10423
674
682
  mani_skill/render/__init__.py,sha256=Uy6h1bzammUO8QVPVCDcuCuhnuN3e5votaho45drAGw,118
@@ -698,7 +706,7 @@ mani_skill/utils/registration.py,sha256=u8ftfGvQP4qzlKNqLQjGi3cRF_-h6Rz-28xbLkW_
698
706
  mani_skill/utils/sapien_utils.py,sha256=QMV0jRZO51KzIMB5CVW_Ne-4fPw0-mqM4a3yhNZaMYo,16430
699
707
  mani_skill/utils/assets/README.md,sha256=5kkmsIiV64ySEGO34HaAlpjXTyrGs1KTV5WnofK46G0,70
700
708
  mani_skill/utils/assets/__init__.py,sha256=gQVKwAczcImTXArSltBWKlSUUuguO12sZYO3Jh5KLso,159
701
- mani_skill/utils/assets/data.py,sha256=YRobBeCxdf23kJxMac9J-dvvJoLSck8mQTbPZGeIAi4,8591
709
+ mani_skill/utils/assets/data.py,sha256=7TsxGBVL05Cg6TLjzx9Nv93uzLby1TaatHs-m15oHew,8686
702
710
  mani_skill/utils/building/__init__.py,sha256=quCI5WYGhzGLMVg_NDyYv2G_MxRTBL8R6XD4a6iY8qc,218
703
711
  mani_skill/utils/building/_mjcf_loader.py,sha256=SqzSoRootFvItHrzwrDuSHScePxbaPqWb7262M7HzIU,37011
704
712
  mani_skill/utils/building/actor_builder.py,sha256=sTnpoThOqWegD-87Q7GyaVvB40fiWUwPOQpYkth-sis,14956
@@ -776,8 +784,8 @@ mani_skill/utils/scene_builder/table/assets/Dining_Table_204_1.glb,sha256=IleHi3
776
784
  mani_skill/utils/scene_builder/table/assets/table.glb,sha256=yw69itZDjBFg8JXZAr9VQV-dZD-MaZChhqBSJR_nlRo,3891588
777
785
  mani_skill/utils/structs/README.md,sha256=qnYKimp_ZkgNcduURrYQxVTimNmq_usDMKoQ8VtMdCs,286
778
786
  mani_skill/utils/structs/__init__.py,sha256=BItR3Xe0z6xCrMHAEaH0AAAVyeonsQ3q-DJUyRUibAA,524
779
- mani_skill/utils/structs/actor.py,sha256=ImzUZULVnsaBksk5Zu2FD-GgYpfg1u4XZcH1jCtd-vQ,16772
780
- mani_skill/utils/structs/articulation.py,sha256=6olG0RxQ84J8p7479Mn3bdy6tsMKLpWKMu3NRbbgzfo,34235
787
+ mani_skill/utils/structs/actor.py,sha256=L0p6vkr8rGtJmF22xAq8Q7nhXKnDD5dahzODSAko0bg,17394
788
+ mani_skill/utils/structs/articulation.py,sha256=c7e_Rfkhskg_Rqz1bkesqHCTzNubObLGZQS0BkkFZF0,34691
781
789
  mani_skill/utils/structs/articulation_joint.py,sha256=TY6joQ0RpnVlHbQHdtx_QQYqTWgFHLiZ642SSWZUuTw,11736
782
790
  mani_skill/utils/structs/base.py,sha256=meGQK5Y4KtHKLnp9VeOZS2gtwg9tE55whuEeqOguBaI,19465
783
791
  mani_skill/utils/structs/decorators.py,sha256=Lv6wQ989dOnreo2tB-qopDnkeBp_jsn1pmfUR-OY8VQ,535
@@ -802,8 +810,8 @@ mani_skill/vector/wrappers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
802
810
  mani_skill/vector/wrappers/gymnasium.py,sha256=v1MDPIrVACBKCulrpdXBK2jDZQI7LKYFZgGgaCC5avY,7408
803
811
  mani_skill/vector/wrappers/sb3.py,sha256=SlXdiEPqcNHYMhJCzA29kBU6zK7DKTe1nc0L6Z3QQtY,4722
804
812
  mani_skill/viewer/__init__.py,sha256=srvDBsk4LQU75K2VIttrhiQ68p_ro7PSDqQRls2PY5c,1722
805
- mani_skill_nightly-2025.5.3.1545.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
806
- mani_skill_nightly-2025.5.3.1545.dist-info/METADATA,sha256=Rg4NFpfjPnBr_gJij40MK3UbXVVocui1dkrKW3xecf0,9291
807
- mani_skill_nightly-2025.5.3.1545.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
808
- mani_skill_nightly-2025.5.3.1545.dist-info/top_level.txt,sha256=bkBgOVl_MZMoQx2aRFsSFEYlZLxjWlip5vtJ39FB3jA,11
809
- mani_skill_nightly-2025.5.3.1545.dist-info/RECORD,,
813
+ mani_skill_nightly-2025.5.3.1612.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
814
+ mani_skill_nightly-2025.5.3.1612.dist-info/METADATA,sha256=4nixw1uljNAUYQ0gC6Th21pC-pStemPbqA4SlftUBdU,9291
815
+ mani_skill_nightly-2025.5.3.1612.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
816
+ mani_skill_nightly-2025.5.3.1612.dist-info/top_level.txt,sha256=bkBgOVl_MZMoQx2aRFsSFEYlZLxjWlip5vtJ39FB3jA,11
817
+ mani_skill_nightly-2025.5.3.1612.dist-info/RECORD,,