Skip to content

Commit 10eb7ee

Browse files
YilingQiaoduburcqa
andauthored
[FEATURE] Add batched simulation of heterogeneous objects. (#2202)
Co-authored-by: Alexis Duburcq <alexis.duburcq@gmail.com>
1 parent 801f2ea commit 10eb7ee

File tree

10 files changed

+982
-55
lines changed

10 files changed

+982
-55
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
Heterogeneous Simulation Example
3+
================================
4+
5+
This example demonstrates heterogeneous simulation, where different parallel
6+
environments can have different geometry variants for the same entity.
7+
8+
Variant Assignment Rules:
9+
When passing a list of morphs to scene.add_entity(), variants are distributed
10+
across environments using the following rules:
11+
12+
1. When n_envs >= n_variants:
13+
Balanced block assignment. Environments are divided into blocks, with each
14+
block assigned to one variant. For example, with 4 variants and 8 environments:
15+
- Environments 0-1 -> Variant 0
16+
- Environments 2-3 -> Variant 1
17+
- Environments 4-5 -> Variant 2
18+
- Environments 6-7 -> Variant 3
19+
20+
2. When n_envs < n_variants:
21+
Each environment i gets variant i (0-indexed). Variants beyond n_envs are
22+
unused. For example, with 4 variants and 2 environments:
23+
- Environment 0 -> Variant 0 (first morph in list)
24+
- Environment 1 -> Variant 1 (second morph in list)
25+
- Variants 2 and 3 are unused
26+
27+
Usage:
28+
python heterogeneous_simulation.py -v -n 4 # 4 environments (matches 4 variants)
29+
python heterogeneous_simulation.py -v -n 8 # 8 environments (2 per variant)
30+
python heterogeneous_simulation.py -v -n 2 # 2 environments (only first 2 variants used)
31+
"""
32+
33+
import argparse
34+
35+
import numpy as np
36+
import genesis as gs
37+
38+
39+
def main():
40+
parser = argparse.ArgumentParser()
41+
parser.add_argument("-v", "--vis", action="store_true", default=False)
42+
parser.add_argument("-n", "--n_envs", type=int, default=4)
43+
args = parser.parse_args()
44+
45+
########################## init ##########################
46+
gs.init(backend=gs.gpu, precision="32")
47+
########################## create a scene ##########################
48+
scene = gs.Scene(
49+
viewer_options=gs.options.ViewerOptions(
50+
camera_pos=(3, -1, 1.5),
51+
camera_lookat=(0.0, 0.0, 0.5),
52+
),
53+
show_viewer=args.vis,
54+
)
55+
56+
########################## entities ##########################
57+
plane = scene.add_entity(
58+
gs.morphs.Plane(),
59+
)
60+
franka = scene.add_entity(
61+
gs.morphs.MJCF(file="xml/franka_emika_panda/panda.xml"),
62+
)
63+
64+
# Define 4 geometry variants - see module docstring for variant assignment rules
65+
morphs_heterogeneous = [
66+
gs.morphs.Box(size=(0.04, 0.04, 0.04), pos=(0.65, 0.0, 0.02)), # Variant 0
67+
gs.morphs.Box(size=(0.02, 0.02, 0.02), pos=(0.65, 0.0, 0.02)), # Variant 1
68+
gs.morphs.Sphere(radius=0.015, pos=(0.65, 0.0, 0.02)), # Variant 2
69+
gs.morphs.Sphere(radius=0.025, pos=(0.65, 0.0, 0.02)), # Variant 3
70+
]
71+
grasping_object = scene.add_entity(
72+
morph=morphs_heterogeneous,
73+
)
74+
########################## build ##########################
75+
scene.build(n_envs=args.n_envs, env_spacing=(1, 1))
76+
77+
motors_dof = np.arange(7)
78+
fingers_dof = np.arange(7, 9)
79+
l_qpos = [-1.0124, 1.5559, 1.3662, -1.6878, -1.5799, 1.7757, 1.4602, 0.04, 0.04]
80+
if args.n_envs == 0:
81+
franka.set_qpos(np.array(l_qpos))
82+
else:
83+
franka.set_qpos(np.array([l_qpos] * args.n_envs))
84+
scene.step()
85+
86+
AABB = grasping_object.get_AABB()
87+
mass = grasping_object.get_mass()
88+
print("heterogeneous AABB", AABB)
89+
print("heterogeneous mass", mass)
90+
91+
end_effector = franka.get_link("hand")
92+
qpos = franka.inverse_kinematics(
93+
link=end_effector,
94+
pos=np.array([[0.65, 0.0, 0.135]] * args.n_envs),
95+
quat=np.array([[0, 1, 0, 0]] * args.n_envs),
96+
)
97+
franka.control_dofs_position(qpos[..., :-2], motors_dof)
98+
99+
# hold
100+
for i in range(100):
101+
print("hold", i)
102+
scene.step()
103+
104+
# grasp
105+
finder_pos = 0.0
106+
for i in range(100):
107+
print("grasp", i)
108+
franka.control_dofs_position(qpos[..., :-2], motors_dof)
109+
franka.control_dofs_position(np.array([[finder_pos, finder_pos]] * args.n_envs), fingers_dof)
110+
scene.step()
111+
112+
# lift
113+
qpos = franka.inverse_kinematics(
114+
link=end_effector,
115+
pos=np.array([[0.65, 0.0, 0.3]] * args.n_envs),
116+
quat=np.array([[0, 1, 0, 0]] * args.n_envs),
117+
)
118+
for i in range(200):
119+
print("lift", i)
120+
franka.control_dofs_position(qpos[..., :-2], motors_dof)
121+
franka.control_dofs_position(np.array([[finder_pos, finder_pos]] * args.n_envs), fingers_dof)
122+
scene.step()
123+
124+
125+
if __name__ == "__main__":
126+
main()

0 commit comments

Comments
 (0)