Skip to content

Commit 7bcd708

Browse files
committed
updated mujoco cc with urdf blocks
1 parent 759576c commit 7bcd708

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env python3
2+
"""
3+
This script benchmarks collision checking for two MuJoCo models by randomizing joint positions within the URDF-defined joint limits.
4+
It also adds static obstacles from a URDF file and checks for collisions both within the robot (self-collisions) and between the robot and obstacles.
5+
Additionally, an interactive visualization mode is provided that uses MuJoCo's viewer.
6+
"""
7+
8+
import mujoco
9+
import mujoco.viewer
10+
import numpy as np
11+
import time
12+
import sys
13+
from scipy.spatial.transform import Rotation
14+
15+
16+
spec_a = mujoco.MjSpec.from_file("../../assets/panda/panda.urdf")
17+
spec_b = mujoco.MjSpec.from_file("../../assets/panda/spherized_panda.urdf")
18+
19+
# Add a ground plane to each model.
20+
spec_a.worldbody.add_geom(
21+
type=mujoco.mjtGeom.mjGEOM_PLANE,
22+
size=[2, 2, 0.1],
23+
rgba=[0.2, 0.2, 0.2, 1]
24+
)
25+
26+
27+
spec_b.worldbody.add_geom(
28+
type=mujoco.mjtGeom.mjGEOM_PLANE,
29+
size=[2, 2, 0.1],
30+
rgba=[0.2, 0.2, 0.2, 1]
31+
)
32+
33+
# Load obstacles from obstacles.urdf
34+
def load_obstacles_from_urdf(spec, urdf_path):
35+
"""
36+
Loads obstacles from a given URDF file and adds them to the simulation.
37+
Returns a list of added geometry objects.
38+
"""
39+
obstacle_geoms = []
40+
41+
# Load the URDF file
42+
obstacle_spec = mujoco.MjSpec.from_file(urdf_path)
43+
44+
for geom in obstacle_spec.worldbody.geoms: # Changed from geom to geoms
45+
obstacle_geom = spec.worldbody.add_geom(
46+
type=geom.type,
47+
size=geom.size,
48+
pos=geom.pos if hasattr(geom, 'pos') else None,
49+
quat=geom.quat if hasattr(geom, 'quat') else None,
50+
rgba=geom.rgba if hasattr(geom, 'rgba') else [1, 1, 1, 1],
51+
condim=geom.condim if hasattr(geom, 'condim') else 3,
52+
friction=geom.friction if hasattr(geom, 'friction') else [1, 0.1, 0.1]
53+
)
54+
obstacle_geoms.append(obstacle_geom)
55+
56+
# Handle any child bodies recursively
57+
if hasattr(obstacle_spec.worldbody, 'body'):
58+
for body in obstacle_spec.worldbody.body:
59+
# Add a new body to the spec
60+
new_body = spec.worldbody.add_body()
61+
62+
# Copy over the body properties
63+
if hasattr(body, 'pos'):
64+
new_body.pos = body.pos
65+
if hasattr(body, 'quat'):
66+
new_body.quat = body.quat
67+
68+
# Add all geoms from this body
69+
for geom in body.geoms: # Changed from geom to geoms
70+
obstacle_geom = new_body.add_geom(
71+
type=geom.type,
72+
size=geom.size,
73+
pos=geom.pos if hasattr(geom, 'pos') else None,
74+
quat=geom.quat if hasattr(geom, 'quat') else None,
75+
rgba=geom.rgba if hasattr(geom, 'rgba') else [1, 1, 1, 1],
76+
condim=geom.condim if hasattr(geom, 'condim') else 3,
77+
friction=geom.friction if hasattr(geom, 'friction') else [1, 0.1, 0.1]
78+
)
79+
obstacle_geoms.append(obstacle_geom)
80+
return obstacle_geoms
81+
82+
obstacles_urdf_path = "../obstacles.urdf"
83+
obstacles_geoms = load_obstacles_from_urdf(spec_a, obstacles_urdf_path)
84+
85+
# Function to randomize joint positions within the limits of the URDF
86+
def random_joint_positions_from_limits(model):
87+
"""
88+
Generates random joint positions that respect the joint limits provided
89+
in the URDF for a fixed-base robot. Assumes that model.nq (number of generalized
90+
coordinates) equals model.njnt (number of joints with limits).
91+
"""
92+
qpos = np.empty(model.nq)
93+
for i in range(model.njnt):
94+
lower, upper = model.jnt_range[i]
95+
if lower == upper:
96+
qpos[i] = lower
97+
else:
98+
qpos[i] = np.random.uniform(lower, upper)
99+
return qpos
100+
101+
def debug_simulation(model, data, step, threshold=1e3):
102+
unstable = False
103+
for i, acc in enumerate(data.qacc):
104+
if np.isnan(acc) or np.isinf(acc) or np.abs(acc) > threshold:
105+
print(f"Warning: Unstable acceleration at DOF {i} on step {step}: {acc}")
106+
unstable = True
107+
if unstable:
108+
print("---- Debug Information ----")
109+
print(f"Time: {data.time:.4f}")
110+
print("qpos:", data.qpos)
111+
print("qvel:", data.qvel)
112+
print("qacc:", data.qacc)
113+
print("---------------------------")
114+
115+
def benchmark_collision_checking(spec, obstacle_geom_ids, num_samples=100_000):
116+
model = spec.compile()
117+
data = mujoco.MjData(model)
118+
119+
print(model.ngeom)
120+
print("Model nq =", model.nq, "model njnt =", model.njnt)
121+
122+
obstacle_geom_indices = set()
123+
for geom_obj in obstacle_geom_ids:
124+
obstacle_geom_indices.add(geom_obj.id)
125+
126+
self_collision_count = 0
127+
obstacle_collision_count = 0
128+
collision_check_total_time = 0.0
129+
130+
start_time = time.perf_counter()
131+
132+
for _ in range(num_samples):
133+
data.qpos[:] = random_joint_positions_from_limits(model)
134+
data.qvel[:] = 0
135+
data.ctrl[:] = 0
136+
137+
start_check = time.perf_counter()
138+
mujoco.mj_forward(model, data)
139+
140+
for i in range(data.ncon):
141+
contact = data.contact[i]
142+
if contact.dist < 0: # Collision detected
143+
geom1, geom2 = contact.geom1, contact.geom2
144+
145+
if geom1 in obstacle_geom_indices or geom2 in obstacle_geom_indices:
146+
obstacle_collision_count += 1
147+
else:
148+
self_collision_count += 1
149+
150+
collision_check_total_time += time.perf_counter() - start_check
151+
152+
elapsed_time = time.perf_counter() - start_time
153+
154+
avg_self_collisions = self_collision_count / num_samples
155+
avg_obstacle_collisions = obstacle_collision_count / num_samples
156+
avg_collision_check_time = collision_check_total_time / num_samples
157+
avg_elapsed_time = elapsed_time / num_samples
158+
159+
return avg_self_collisions, avg_obstacle_collisions, avg_elapsed_time, avg_collision_check_time
160+
161+
162+
def benchmark_collision_checking_visual(spec, obstacle_geom_ids, sleep_time=0.0001, num_samples=100_000):
163+
model = spec.compile()
164+
data = mujoco.MjData(model)
165+
obstacle_geom_indices = set()
166+
for geom_obj in obstacle_geom_ids:
167+
obstacle_geom_indices.add(geom_obj.id)
168+
169+
self_collision_count = 0
170+
obstacle_collision_count = 0
171+
collision_check_total_time = 0.0
172+
173+
start_time = time.perf_counter()
174+
175+
with mujoco.viewer.launch_passive(model, data) as viewer:
176+
for _ in range(num_samples):
177+
data.qpos[:] = random_joint_positions_from_limits(model)
178+
data.qvel[:] = 0
179+
data.ctrl[:] = 0
180+
181+
start_check = time.perf_counter()
182+
mujoco.mj_forward(model, data)
183+
184+
for i in range(data.ncon):
185+
contact = data.contact[i]
186+
if contact.dist < 0: # Collision detected
187+
geom1, geom2 = contact.geom1, contact.geom2
188+
189+
if geom1 in obstacle_geom_indices or geom2 in obstacle_geom_indices:
190+
obstacle_collision_count += 1
191+
else:
192+
self_collision_count += 1
193+
viewer.sync()
194+
collision_check_total_time += time.perf_counter() - start_check
195+
time.sleep(sleep_time)
196+
197+
elapsed_time = time.perf_counter() - start_time
198+
199+
avg_self_collisions = self_collision_count / num_samples
200+
avg_obstacle_collisions = obstacle_collision_count / num_samples
201+
avg_collision_check_time = collision_check_total_time / num_samples
202+
avg_elapsed_time = elapsed_time / num_samples
203+
204+
return avg_self_collisions, avg_obstacle_collisions, avg_elapsed_time, avg_collision_check_time
205+
206+
if __name__ == "__main__":
207+
# Load obstacles from the URDF file and add them to both models
208+
obstacles_a_geoms = load_obstacles_from_urdf(spec_a, obstacles_urdf_path)
209+
obstacles_b_geoms = load_obstacles_from_urdf(spec_b, obstacles_urdf_path)
210+
211+
# Run the benchmark for both models
212+
print("Benchmarking Model A (Complex)")
213+
a_self_count, a_obst_count, a_total_time, a_check_time = benchmark_collision_checking(spec_a, obstacles_a_geoms)
214+
print("Benchmarking Model B (Simple)")
215+
b_self_count, b_obst_count, b_total_time, b_check_time = benchmark_collision_checking(spec_b, obstacles_b_geoms)
216+
217+
# Output the results
218+
print(f"\nModel A (Complex):")
219+
print(f" Average Simulation Time: {a_total_time:.3f} sec")
220+
print(f" Average Self-Collisions: {a_self_count}")
221+
print(f" Average Obstacle Collisions: {a_obst_count}")
222+
print(f" Average Collision Check Time: {a_check_time:.6f} sec")
223+
224+
print(f"\nModel B (Simple):")
225+
print(f" Average Simulation Time: {b_total_time:.3f} sec")
226+
print(f" Average Self-Collisions: {b_self_count}")
227+
print(f" Average Obstacle Collisions: {b_obst_count}")
228+
print(f" Average Collision Check Time: {b_check_time:.6f} sec")
File renamed without changes.

0 commit comments

Comments
 (0)