Creating a soft-body simulation#
This tutorial shows how to create a soft-body simulation using SimulationManager. It covers the setup of the simulation context, adding a deformable mesh (soft object), and running the simulation loop.
The Code#
The tutorial corresponds to the create_softbody.py script in the scripts/tutorials/sim directory.
Code for create_softbody.py
1# ----------------------------------------------------------------------------
2# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15# ----------------------------------------------------------------------------
16
17"""
18This script demonstrates how to create a simulation scene using SimulationManager.
19It shows the basic setup of simulation context, adding objects, lighting, and sensors.
20"""
21
22from __future__ import annotations
23
24import argparse
25import time
26from embodichain.cli.sim import add_sim_args_to_parser
27
28
29def build_parser() -> argparse.ArgumentParser:
30 """Build CLI options without initializing simulation resources."""
31 parser = argparse.ArgumentParser(
32 description="Create a simulation scene with SimulationManager"
33 )
34 add_sim_args_to_parser(parser)
35 parser.set_defaults(device="cuda", physics="newton")
36 return parser
37
38
39if __name__ == "__main__":
40 # Parse before importing optional simulation/planning dependencies.
41 _cli_args = build_parser().parse_args()
42
43
44from embodichain.data import get_data_path
45from embodichain.lab.sim import SimulationManager, SimulationManagerCfg
46from embodichain.lab.visualization import visualization_cfg_from_args
47from embodichain.lab.sim.cfg import (
48 NewtonCollisionPipelineCfg,
49 NewtonPhysicsCfg,
50 RenderCfg,
51 VolumeDeformableObjectCfg,
52 VolumeDeformableMeshingCfg,
53 VolumeDeformablePhysicsCfg,
54)
55from embodichain.lab.sim.shapes import MeshCfg
56from embodichain.lab.sim.objects import VolumeDeformableObject
57
58
59def main(args: argparse.Namespace | None = None) -> None:
60 """Main function to create and run the simulation scene."""
61
62 # Parse command line arguments
63 parser = build_parser()
64 if args is None:
65 args = parser.parse_args()
66 if args.physics != "newton":
67 parser.error("Soft bodies require --physics newton.")
68 if not str(args.device).startswith("cuda"):
69 parser.error("Soft bodies require a CUDA device.")
70
71 # Configure the simulation
72 sim_cfg = SimulationManagerCfg(
73 width=1920,
74 height=1080,
75 headless=args.headless,
76 num_envs=args.num_envs,
77 arena_space=args.arena_space,
78 gpu_id=args.gpu_id,
79 physics_dt=1.0 / 100.0, # Physics timestep (100 Hz)
80 device=args.device,
81 render_cfg=RenderCfg(
82 renderer=args.renderer
83 ), # Enable ray tracing for better visuals
84 physics_cfg=NewtonPhysicsCfg(
85 num_substeps=6,
86 solver_cfg={
87 "solver_type": "vbd",
88 "iterations": 8,
89 "particle_enable_self_contact": False,
90 "particle_self_contact_radius": 0.001,
91 "particle_self_contact_margin": 0.001,
92 "particle_topological_contact_filter_threshold": 3,
93 "particle_enable_tile_solve": True,
94 "soft_contact_ke": 5.0e4,
95 "soft_contact_kd": 1.0e-3,
96 "soft_contact_mu": 1.5,
97 },
98 collision_cfg=NewtonCollisionPipelineCfg(
99 soft_contact_margin=0.002,
100 ),
101 ),
102 visualization=visualization_cfg_from_args(args),
103 )
104
105 # Create the simulation instance
106 sim = SimulationManager(sim_cfg)
107
108 print("[INFO]: Scene setup complete!")
109
110 # add softbody to the scene
111 cow: VolumeDeformableObject = sim.add_deformable_object(
112 cfg=VolumeDeformableObjectCfg(
113 uid="cow",
114 shape=MeshCfg(
115 fpath=get_data_path("Cow/cow.obj"),
116 ),
117 init_pos=[0.0, 5.0, 3.0],
118 particle_radius=0.01,
119 meshing=VolumeDeformableMeshingCfg(
120 triangle_remesh_resolution=24,
121 simulation_mesh_resolution=16,
122 voxel_num_relaxation_iters=5,
123 ),
124 attrs=VolumeDeformablePhysicsCfg(
125 # Equivalent to the DexSim demo's k_mu=1e4 and k_lambda=5e4.
126 youngs=2.833333333e4,
127 poissons=5.0 / 12.0,
128 density=50.0,
129 elasticity_damping=2.0e-3,
130 ),
131 ),
132 )
133 print("[INFO]: Add soft object complete!")
134
135 sim.prepare()
136
137 # Open window when the scene has been set up
138 if not args.headless:
139 sim.open_window()
140
141 print(f"[INFO]: Running simulation with {args.num_envs} environment(s)")
142 print("[INFO]: Press Ctrl+C to stop the simulation")
143
144 # Run the simulation
145 run_simulation(sim, cow)
146
147
148def run_simulation(sim: SimulationManager, soft_obj: VolumeDeformableObject) -> None:
149 """Run the simulation loop.
150
151 Args:
152 sim: The SimulationManager instance to run
153 soft_obj: soft object
154 """
155
156 step_count = 0
157
158 try:
159 last_time = time.time()
160 last_step = 0
161 while True:
162 # Update physics simulation
163 sim.update(step=1)
164 step_count += 1
165
166 # Print FPS every second
167 if step_count % 100 == 0:
168 current_time = time.time()
169 elapsed = current_time - last_time
170 fps = (
171 sim.num_envs * (step_count - last_step) / elapsed
172 if elapsed > 0
173 else 0
174 )
175 print(f"[INFO]: Simulation step: {step_count}, FPS: {fps:.2f}")
176 last_time = current_time
177 last_step = step_count
178 if step_count % 500 == 0:
179 soft_obj.reset()
180
181 except KeyboardInterrupt:
182 print("\n[INFO]: Stopping simulation...")
183 finally:
184 # Clean up resources
185 sim.destroy()
186 print("[INFO]: Simulation terminated successfully")
187
188
189if __name__ == "__main__":
190 main(_cli_args)
The Code Explained#
Configuring the simulation#
The first step is to configure the simulation environment. This is done using the SimulationManagerCfg data class, which allows you to specify parameters like window dimensions, headless mode, physics timestep, simulation device (CPU/GPU), and rendering options like ray tracing. Soft-body simulation requires a CUDA device and the Newton backend; the script validates both options before creating the scene.
# Configure the simulation
sim_cfg = SimulationManagerCfg(
width=1920,
height=1080,
headless=args.headless,
num_envs=args.num_envs,
arena_space=args.arena_space,
gpu_id=args.gpu_id,
physics_dt=1.0 / 100.0, # Physics timestep (100 Hz)
device=args.device,
render_cfg=RenderCfg(
renderer=args.renderer
), # Enable ray tracing for better visuals
physics_cfg=NewtonPhysicsCfg(
num_substeps=6,
solver_cfg={
"solver_type": "vbd",
"iterations": 8,
"particle_enable_self_contact": False,
"particle_self_contact_radius": 0.001,
"particle_self_contact_margin": 0.001,
"particle_topological_contact_filter_threshold": 3,
"particle_enable_tile_solve": True,
"soft_contact_ke": 5.0e4,
"soft_contact_kd": 1.0e-3,
"soft_contact_mu": 1.5,
},
collision_cfg=NewtonCollisionPipelineCfg(
soft_contact_margin=0.002,
),
),
visualization=visualization_cfg_from_args(args),
)
# Create the simulation instance
sim = SimulationManager(sim_cfg)
print("[INFO]: Scene setup complete!")
Adding a soft body to the scene#
With the simulation context created, we can add a soft (deformable) object. This tutorial demonstrates adding a soft-body cow mesh to the scene using the SimulationManager.add_deformable_object() method. The object’s geometry and physical parameters are defined through configuration objects:
cfg.MeshCfgfor the mesh shape (cow.obj)cfg.VolumeDeformableMeshingCfgfor voxelization and simulation mesh resolutioncfg.VolumeDeformablePhysicsCfgforattrs(youngs,poissons, volume density, andsurface_props)
# add softbody to the scene
cow: VolumeDeformableObject = sim.add_deformable_object(
cfg=VolumeDeformableObjectCfg(
uid="cow",
shape=MeshCfg(
fpath=get_data_path("Cow/cow.obj"),
),
init_pos=[0.0, 5.0, 3.0],
particle_radius=0.01,
meshing=VolumeDeformableMeshingCfg(
triangle_remesh_resolution=24,
simulation_mesh_resolution=16,
voxel_num_relaxation_iters=5,
),
attrs=VolumeDeformablePhysicsCfg(
# Equivalent to the DexSim demo's k_mu=1e4 and k_lambda=5e4.
youngs=2.833333333e4,
poissons=5.0 / 12.0,
density=50.0,
elasticity_damping=2.0e-3,
),
),
)
print("[INFO]: Add soft object complete!")
The Code Execution#
To run the script and see the result, execute the following command:
python scripts/tutorials/sim/create_softbody.py
A window should appear showing a soft-body cow mesh falling onto a ground plane. To stop the simulation, you can either close the window or press Ctrl+C in the terminal.
You can also pass arguments to customize the simulation. For example, to run in headless mode with n parallel environments using the specified device:
python scripts/tutorials/sim/create_softbody.py --headless --num_envs <n> --device <cuda/cpu>
To inspect the deforming collision surface in a browser:
python scripts/tutorials/sim/create_softbody.py \
--viser \
--viser-soft-body-fps 5
DexSim exposes live soft-body collision vertices but not their triangle
connectivity. The Viser preview therefore uses a stable convex-hull surface:
motion and deformation are visible, while concave details of the original cow
render mesh are intentionally simplified. Lower
--viser-soft-body-fps when publishing large soft bodies or several
environments.
See Browser visualization with Viser for details.
Now that we have a basic understanding of how to create a soft-body scene, let’s move on to more advanced topics.