chrisjcc commited on
Commit
34d2632
·
verified ·
1 Parent(s): 1c67e52

Systematically discovers joint offsets (qpos, qvel) for each object.

Browse files

Notes:
- This code assumes unnamed joints but relies on joint order; if you prefer robustness, explicitly name each joint (e.g. <joint name="puck_joint" type="free"/>) and update the mj_name2id() calls accordingly.
- joint_qpos_addrs and joint_qvel_addrs are general lists that will support arbitrary numbers of bodies with free joints.
- For each object, the simulation handles push dynamics, orientation extraction, and saves a structured state and rendered GIF.

Files changed (1) hide show
  1. tray_sim.py +54 -23
tray_sim.py CHANGED
@@ -34,6 +34,29 @@ def run_tray_simulation(
34
  # Explicitly reset the simulation state
35
  model = mujoco.MjModel.from_xml_path(MODEL_PATH)
36
  data = mujoco.MjData(model)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  #tray_id = mujoco.mj_name2id(
39
  # model,
@@ -48,37 +71,43 @@ def run_tray_simulation(
48
  # Compute DOF offsets
49
 
50
  # Verify the number of bodies (puck + tray)
51
- expected_bodies = num_objects + 1 # Puck + tray
52
- actual_bodies = sum(1 for i in range(model.nbody) if mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i))
53
- if actual_bodies < expected_bodies:
54
- raise ValueError(f"Model has fewer bodies ({actual_bodies}) than expected ({expected_bodies})")
55
 
56
- mujoco.mj_resetData(model, data)
57
 
58
  # Find puck body ID
59
- puck_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, "puck")
60
- if puck_id == -1:
61
- raise ValueError("Puck body not found in model")
62
 
63
- print(f"Puck body id {puck_id} pos at start {data.xpos[puck_id]}")
64
 
65
  # Puck (pusher) setup (indices for single object)
66
- puck_idx = 0 # First body
67
- puck_vel_idx = 0 # First body velocities
68
 
69
  # Verify qpos and qvel sizes
70
- if puck_idx + 7 > model.nq:
71
- raise ValueError(f"Puck position index {puck_idx} out of bounds for qpos size {model.nq}")
72
- if puck_vel_idx + 6 > model.nv:
73
- raise ValueError(f"Puck velocity index {puck_vel_idx} out of bounds for qvel size {model.nv}")
74
 
75
  # Puck position and orientation
 
 
 
 
76
  puck_pos = np.array([0.35, -0.35, 0.08])
77
  puck_quat = [1, 0, 0, 0]
78
-
 
 
79
  #data.qpos[puck_idx:puck_idx+3] = puck_pos
80
  #data.qpos[puck_idx+3:puck_idx+7] = puck_quat
81
 
 
82
  # Compute velocity towards center
83
  target = np.array([0.0, 0.0, 0.05]) # Slightly outside tray corner
84
  direction = target - puck_pos
@@ -107,7 +136,6 @@ def run_tray_simulation(
107
  data.qvel[puck_vel_idx:puck_vel_idx+3] = push_velocity
108
 
109
  mujoco.mj_step(model, data)
110
-
111
  renderer.update_scene(data, camera=cam, scene_option=scene_option)
112
  img = renderer.render()
113
 
@@ -118,6 +146,7 @@ def run_tray_simulation(
118
 
119
  renderer.close()
120
 
 
121
  euler_angles = []
122
  for i in range(num_objects):
123
  quat = data.qpos[i*7+3:i*7+7] # qw, qx, qy, qz
@@ -129,15 +158,18 @@ def run_tray_simulation(
129
  # Fully structured snapshot of: Positions,
130
  # Orientations (in both quaternion and Euler angle forms),
131
  # Linear and angular velocities
 
 
132
  physics_state = {
133
- "positions_xyz": data.qpos[:num_objects * 7].reshape(num_objects, 7)[:, :3].tolist(),
134
- "orientations_quat": data.qpos[:num_objects * 7].reshape(num_objects, 7)[:, 3:].tolist(),
135
  "orientations_euler": euler_angles,
136
- "velocities_linear": data.qvel[:num_objects * 6].reshape(num_objects, 6)[:, :3].tolist(),
137
- "velocities_angular": data.qvel[:num_objects * 6].reshape(num_objects, 6)[:, 3:].tolist(),
138
  }
139
  # Save JSON Snapshot with unique ID (optional, for LLM input/debugging)
140
  json_path = os.path.join(tempfile.gettempdir(), f"tray_sim_{unique_id}_state.json")
 
141
  with open(json_path, "w") as f:
142
  json.dump(physics_state, f, indent=2)
143
 
@@ -146,10 +178,9 @@ def run_tray_simulation(
146
  imageio.mimsave(gif_path, frames, fps=20)
147
 
148
  print(f"Bodies: {model.nbody}, qpos size: {model.nq}, qvel size: {model.nv}")
149
-
150
  for i in range(model.nbody):
151
  name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i)
152
  if name:
153
- print(f"Body {i}: {name}")
154
 
155
  return gif_path, physics_state, json_path
 
34
  # Explicitly reset the simulation state
35
  model = mujoco.MjModel.from_xml_path(MODEL_PATH)
36
  data = mujoco.MjData(model)
37
+ mujoco.mj_resetData(model, data)
38
+
39
+ # Object naming convention: puck, obj0, obj1, ...
40
+ object_names = ["puck"] + [f"obj{i}" for i in range(num_objects - 1)]
41
+
42
+ # Validate all objects exist
43
+ for name in object_names:
44
+ if mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, name) == -1:
45
+ raise ValueError(f"Body '{name}' not found in model")
46
+
47
+ # Resolve joint offsets
48
+ joint_qpos_addrs = []
49
+ joint_qvel_addrs = []
50
+ for name in object_names:
51
+ joint_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, f"{name}_joint") # assumes <joint> has no name
52
+ if joint_id == -1:
53
+ # fallback: assume exactly one joint per object, unnamed
54
+ joint_id = sum(1 for i in range(model.njnt)
55
+ if model.jnt_type[i] == mujoco.mjtJoint.mjJNT_FREE)
56
+ if len(joint_qpos_addrs) >= joint_id:
57
+ raise ValueError(f"Failed to resolve joint for body '{name}'")
58
+ joint_qpos_addrs.append(model.jnt_qposadr[joint_id])
59
+ joint_qvel_addrs.append(model.jnt_dofadr[joint_id])
60
 
61
  #tray_id = mujoco.mj_name2id(
62
  # model,
 
71
  # Compute DOF offsets
72
 
73
  # Verify the number of bodies (puck + tray)
74
+ #expected_bodies = num_objects + 1 # Puck + tray
75
+ #actual_bodies = sum(1 for i in range(model.nbody) if mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i))
76
+ #if actual_bodies < expected_bodies:
77
+ # raise ValueError(f"Model has fewer bodies ({actual_bodies}) than expected ({expected_bodies})")
78
 
 
79
 
80
  # Find puck body ID
81
+ #puck_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, "puck")
82
+ #if puck_id == -1:
83
+ # raise ValueError("Puck body not found in model")
84
 
85
+ #print(f"Puck body id {puck_id} pos at start {data.xpos[puck_id]}")
86
 
87
  # Puck (pusher) setup (indices for single object)
88
+ ##puck_idx = 0 # First body
89
+ ##puck_vel_idx = 0 # First body velocities
90
 
91
  # Verify qpos and qvel sizes
92
+ #if puck_idx + 7 > model.nq:
93
+ # raise ValueError(f"Puck position index {puck_idx} out of bounds for qpos size {model.nq}")
94
+ #if puck_vel_idx + 6 > model.nv:
95
+ # raise ValueError(f"Puck velocity index {puck_vel_idx} out of bounds for qvel size {model.nv}")
96
 
97
  # Puck position and orientation
98
+ #puck_pos = np.array([0.35, -0.35, 0.08])
99
+ #puck_quat = [1, 0, 0, 0]
100
+
101
+ # Set initial position and orientation for the puck
102
  puck_pos = np.array([0.35, -0.35, 0.08])
103
  puck_quat = [1, 0, 0, 0]
104
+ #data.qpos[joint_qpos_addrs[0]:joint_qpos_addrs[0]+3] = puck_pos
105
+ #data.qpos[joint_qpos_addrs[0]+3:joint_qpos_addrs[0]+7] = puck_quat
106
+
107
  #data.qpos[puck_idx:puck_idx+3] = puck_pos
108
  #data.qpos[puck_idx+3:puck_idx+7] = puck_quat
109
 
110
+ # Push velocity directed toward center
111
  # Compute velocity towards center
112
  target = np.array([0.0, 0.0, 0.05]) # Slightly outside tray corner
113
  direction = target - puck_pos
 
136
  data.qvel[puck_vel_idx:puck_vel_idx+3] = push_velocity
137
 
138
  mujoco.mj_step(model, data)
 
139
  renderer.update_scene(data, camera=cam, scene_option=scene_option)
140
  img = renderer.render()
141
 
 
146
 
147
  renderer.close()
148
 
149
+ # Orientation extraction
150
  euler_angles = []
151
  for i in range(num_objects):
152
  quat = data.qpos[i*7+3:i*7+7] # qw, qx, qy, qz
 
158
  # Fully structured snapshot of: Positions,
159
  # Orientations (in both quaternion and Euler angle forms),
160
  # Linear and angular velocities
161
+ qpos = np.array([data.qpos[addr:addr+7] for addr in joint_qpos_addrs])
162
+ qvel = np.array([data.qvel[addr:addr+6] for addr in joint_qvel_addrs])
163
  physics_state = {
164
+ "positions_xyz": qpos[:, :3].tolist(),
165
+ "orientations_quat": qpos[:, 3:].tolist(),
166
  "orientations_euler": euler_angles,
167
+ "velocities_linear": qvel[:, :3].tolist(),
168
+ "velocities_angular": qvel[:, 3:].tolist(),
169
  }
170
  # Save JSON Snapshot with unique ID (optional, for LLM input/debugging)
171
  json_path = os.path.join(tempfile.gettempdir(), f"tray_sim_{unique_id}_state.json")
172
+
173
  with open(json_path, "w") as f:
174
  json.dump(physics_state, f, indent=2)
175
 
 
178
  imageio.mimsave(gif_path, frames, fps=20)
179
 
180
  print(f"Bodies: {model.nbody}, qpos size: {model.nq}, qvel size: {model.nv}")
 
181
  for i in range(model.nbody):
182
  name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_BODY, i)
183
  if name:
184
+ print(f"Body {i}: {name}, xpos: {data.xpos[i]}")
185
 
186
  return gif_path, physics_state, json_path