warp-lang 1.9.1__py3-none-win_amd64.whl → 1.10.0rc2__py3-none-win_amd64.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.

Potentially problematic release.


This version of warp-lang might be problematic. Click here for more details.

Files changed (346) hide show
  1. warp/__init__.py +301 -287
  2. warp/__init__.pyi +794 -305
  3. warp/_src/__init__.py +14 -0
  4. warp/_src/autograd.py +1075 -0
  5. warp/_src/build.py +618 -0
  6. warp/_src/build_dll.py +640 -0
  7. warp/{builtins.py → _src/builtins.py} +1382 -377
  8. warp/_src/codegen.py +4359 -0
  9. warp/{config.py → _src/config.py} +178 -169
  10. warp/_src/constants.py +57 -0
  11. warp/_src/context.py +8294 -0
  12. warp/_src/dlpack.py +462 -0
  13. warp/_src/fabric.py +355 -0
  14. warp/_src/fem/__init__.py +14 -0
  15. warp/_src/fem/adaptivity.py +508 -0
  16. warp/_src/fem/cache.py +687 -0
  17. warp/_src/fem/dirichlet.py +188 -0
  18. warp/{fem → _src/fem}/domain.py +40 -30
  19. warp/_src/fem/field/__init__.py +131 -0
  20. warp/_src/fem/field/field.py +701 -0
  21. warp/{fem → _src/fem}/field/nodal_field.py +30 -15
  22. warp/{fem → _src/fem}/field/restriction.py +1 -1
  23. warp/{fem → _src/fem}/field/virtual.py +53 -27
  24. warp/_src/fem/geometry/__init__.py +32 -0
  25. warp/{fem → _src/fem}/geometry/adaptive_nanogrid.py +77 -163
  26. warp/_src/fem/geometry/closest_point.py +97 -0
  27. warp/{fem → _src/fem}/geometry/deformed_geometry.py +14 -22
  28. warp/{fem → _src/fem}/geometry/element.py +32 -10
  29. warp/{fem → _src/fem}/geometry/geometry.py +48 -20
  30. warp/{fem → _src/fem}/geometry/grid_2d.py +12 -23
  31. warp/{fem → _src/fem}/geometry/grid_3d.py +12 -23
  32. warp/{fem → _src/fem}/geometry/hexmesh.py +40 -63
  33. warp/{fem → _src/fem}/geometry/nanogrid.py +255 -248
  34. warp/{fem → _src/fem}/geometry/partition.py +121 -63
  35. warp/{fem → _src/fem}/geometry/quadmesh.py +26 -45
  36. warp/{fem → _src/fem}/geometry/tetmesh.py +40 -63
  37. warp/{fem → _src/fem}/geometry/trimesh.py +26 -45
  38. warp/{fem → _src/fem}/integrate.py +164 -158
  39. warp/_src/fem/linalg.py +383 -0
  40. warp/_src/fem/operator.py +396 -0
  41. warp/_src/fem/polynomial.py +229 -0
  42. warp/{fem → _src/fem}/quadrature/pic_quadrature.py +15 -20
  43. warp/{fem → _src/fem}/quadrature/quadrature.py +95 -47
  44. warp/_src/fem/space/__init__.py +248 -0
  45. warp/{fem → _src/fem}/space/basis_function_space.py +20 -11
  46. warp/_src/fem/space/basis_space.py +679 -0
  47. warp/{fem → _src/fem}/space/dof_mapper.py +3 -3
  48. warp/{fem → _src/fem}/space/function_space.py +14 -13
  49. warp/{fem → _src/fem}/space/grid_2d_function_space.py +4 -7
  50. warp/{fem → _src/fem}/space/grid_3d_function_space.py +4 -4
  51. warp/{fem → _src/fem}/space/hexmesh_function_space.py +4 -10
  52. warp/{fem → _src/fem}/space/nanogrid_function_space.py +3 -9
  53. warp/{fem → _src/fem}/space/partition.py +117 -60
  54. warp/{fem → _src/fem}/space/quadmesh_function_space.py +4 -10
  55. warp/{fem → _src/fem}/space/restriction.py +66 -33
  56. warp/_src/fem/space/shape/__init__.py +152 -0
  57. warp/{fem → _src/fem}/space/shape/cube_shape_function.py +9 -9
  58. warp/{fem → _src/fem}/space/shape/shape_function.py +8 -9
  59. warp/{fem → _src/fem}/space/shape/square_shape_function.py +6 -6
  60. warp/{fem → _src/fem}/space/shape/tet_shape_function.py +3 -3
  61. warp/{fem → _src/fem}/space/shape/triangle_shape_function.py +3 -3
  62. warp/{fem → _src/fem}/space/tetmesh_function_space.py +3 -9
  63. warp/_src/fem/space/topology.py +459 -0
  64. warp/{fem → _src/fem}/space/trimesh_function_space.py +3 -9
  65. warp/_src/fem/types.py +112 -0
  66. warp/_src/fem/utils.py +486 -0
  67. warp/_src/jax.py +186 -0
  68. warp/_src/jax_experimental/__init__.py +14 -0
  69. warp/_src/jax_experimental/custom_call.py +387 -0
  70. warp/_src/jax_experimental/ffi.py +1284 -0
  71. warp/_src/jax_experimental/xla_ffi.py +656 -0
  72. warp/_src/marching_cubes.py +708 -0
  73. warp/_src/math.py +414 -0
  74. warp/_src/optim/__init__.py +14 -0
  75. warp/_src/optim/adam.py +163 -0
  76. warp/_src/optim/linear.py +1606 -0
  77. warp/_src/optim/sgd.py +112 -0
  78. warp/_src/paddle.py +406 -0
  79. warp/_src/render/__init__.py +14 -0
  80. warp/_src/render/imgui_manager.py +289 -0
  81. warp/_src/render/render_opengl.py +3636 -0
  82. warp/_src/render/render_usd.py +937 -0
  83. warp/_src/render/utils.py +160 -0
  84. warp/_src/sparse.py +2716 -0
  85. warp/_src/tape.py +1206 -0
  86. warp/{thirdparty → _src/thirdparty}/unittest_parallel.py +9 -2
  87. warp/_src/torch.py +391 -0
  88. warp/_src/types.py +5870 -0
  89. warp/_src/utils.py +1693 -0
  90. warp/autograd.py +12 -1054
  91. warp/bin/warp-clang.dll +0 -0
  92. warp/bin/warp.dll +0 -0
  93. warp/build.py +8 -588
  94. warp/build_dll.py +6 -721
  95. warp/codegen.py +6 -4251
  96. warp/constants.py +6 -39
  97. warp/context.py +12 -8062
  98. warp/dlpack.py +6 -444
  99. warp/examples/distributed/example_jacobi_mpi.py +4 -5
  100. warp/examples/fem/example_adaptive_grid.py +1 -1
  101. warp/examples/fem/example_apic_fluid.py +1 -1
  102. warp/examples/fem/example_burgers.py +8 -8
  103. warp/examples/fem/example_diffusion.py +1 -1
  104. warp/examples/fem/example_distortion_energy.py +1 -1
  105. warp/examples/fem/example_mixed_elasticity.py +2 -2
  106. warp/examples/fem/example_navier_stokes.py +1 -1
  107. warp/examples/fem/example_nonconforming_contact.py +7 -7
  108. warp/examples/fem/example_stokes.py +1 -1
  109. warp/examples/fem/example_stokes_transfer.py +1 -1
  110. warp/examples/fem/utils.py +2 -2
  111. warp/examples/interop/example_jax_callable.py +1 -1
  112. warp/examples/interop/example_jax_ffi_callback.py +1 -1
  113. warp/examples/interop/example_jax_kernel.py +1 -1
  114. warp/examples/tile/example_tile_mcgp.py +191 -0
  115. warp/fabric.py +6 -337
  116. warp/fem/__init__.py +159 -97
  117. warp/fem/adaptivity.py +7 -489
  118. warp/fem/cache.py +9 -648
  119. warp/fem/dirichlet.py +6 -184
  120. warp/fem/field/__init__.py +8 -109
  121. warp/fem/field/field.py +7 -652
  122. warp/fem/geometry/__init__.py +7 -18
  123. warp/fem/geometry/closest_point.py +11 -77
  124. warp/fem/linalg.py +18 -366
  125. warp/fem/operator.py +11 -369
  126. warp/fem/polynomial.py +9 -209
  127. warp/fem/space/__init__.py +5 -211
  128. warp/fem/space/basis_space.py +6 -662
  129. warp/fem/space/shape/__init__.py +41 -118
  130. warp/fem/space/topology.py +6 -437
  131. warp/fem/types.py +6 -81
  132. warp/fem/utils.py +11 -444
  133. warp/jax.py +8 -165
  134. warp/jax_experimental/__init__.py +14 -1
  135. warp/jax_experimental/custom_call.py +8 -365
  136. warp/jax_experimental/ffi.py +17 -873
  137. warp/jax_experimental/xla_ffi.py +5 -605
  138. warp/marching_cubes.py +5 -689
  139. warp/math.py +16 -393
  140. warp/native/array.h +385 -37
  141. warp/native/builtin.h +314 -37
  142. warp/native/bvh.cpp +43 -9
  143. warp/native/bvh.cu +62 -27
  144. warp/native/bvh.h +310 -309
  145. warp/native/clang/clang.cpp +102 -97
  146. warp/native/coloring.cpp +0 -1
  147. warp/native/crt.h +208 -0
  148. warp/native/exports.h +156 -0
  149. warp/native/hashgrid.cu +2 -0
  150. warp/native/intersect.h +24 -1
  151. warp/native/intersect_tri.h +44 -35
  152. warp/native/mat.h +1456 -276
  153. warp/native/mesh.cpp +4 -4
  154. warp/native/mesh.cu +4 -2
  155. warp/native/mesh.h +176 -61
  156. warp/native/quat.h +0 -52
  157. warp/native/scan.cu +2 -0
  158. warp/native/sparse.cu +7 -3
  159. warp/native/spatial.h +12 -0
  160. warp/native/tile.h +681 -89
  161. warp/native/tile_radix_sort.h +1 -1
  162. warp/native/tile_reduce.h +394 -46
  163. warp/native/tile_scan.h +4 -4
  164. warp/native/vec.h +469 -0
  165. warp/native/version.h +23 -0
  166. warp/native/volume.cpp +1 -1
  167. warp/native/volume.cu +1 -0
  168. warp/native/volume.h +1 -1
  169. warp/native/volume_builder.cu +2 -0
  170. warp/native/warp.cpp +57 -29
  171. warp/native/warp.cu +253 -171
  172. warp/native/warp.h +11 -8
  173. warp/optim/__init__.py +6 -3
  174. warp/optim/adam.py +6 -145
  175. warp/optim/linear.py +14 -1585
  176. warp/optim/sgd.py +6 -94
  177. warp/paddle.py +6 -388
  178. warp/render/__init__.py +8 -4
  179. warp/render/imgui_manager.py +7 -267
  180. warp/render/render_opengl.py +6 -3618
  181. warp/render/render_usd.py +6 -919
  182. warp/render/utils.py +6 -142
  183. warp/sparse.py +37 -2563
  184. warp/tape.py +6 -1188
  185. warp/tests/__main__.py +1 -1
  186. warp/tests/cuda/test_async.py +4 -4
  187. warp/tests/cuda/test_conditional_captures.py +1 -1
  188. warp/tests/cuda/test_multigpu.py +1 -1
  189. warp/tests/cuda/test_streams.py +58 -1
  190. warp/tests/geometry/test_bvh.py +157 -22
  191. warp/tests/geometry/test_marching_cubes.py +0 -1
  192. warp/tests/geometry/test_mesh.py +5 -3
  193. warp/tests/geometry/test_mesh_query_aabb.py +5 -12
  194. warp/tests/geometry/test_mesh_query_point.py +5 -2
  195. warp/tests/geometry/test_mesh_query_ray.py +15 -3
  196. warp/tests/geometry/test_volume_write.py +5 -5
  197. warp/tests/interop/test_dlpack.py +14 -14
  198. warp/tests/interop/test_jax.py +772 -49
  199. warp/tests/interop/test_paddle.py +1 -1
  200. warp/tests/test_adam.py +0 -1
  201. warp/tests/test_arithmetic.py +9 -9
  202. warp/tests/test_array.py +527 -100
  203. warp/tests/test_array_reduce.py +3 -3
  204. warp/tests/test_atomic.py +12 -8
  205. warp/tests/test_atomic_bitwise.py +209 -0
  206. warp/tests/test_atomic_cas.py +4 -4
  207. warp/tests/test_bool.py +2 -2
  208. warp/tests/test_builtins_resolution.py +5 -571
  209. warp/tests/test_codegen.py +33 -14
  210. warp/tests/test_conditional.py +1 -1
  211. warp/tests/test_context.py +6 -6
  212. warp/tests/test_copy.py +242 -161
  213. warp/tests/test_ctypes.py +3 -3
  214. warp/tests/test_devices.py +24 -2
  215. warp/tests/test_examples.py +16 -84
  216. warp/tests/test_fabricarray.py +35 -35
  217. warp/tests/test_fast_math.py +0 -2
  218. warp/tests/test_fem.py +56 -10
  219. warp/tests/test_fixedarray.py +3 -3
  220. warp/tests/test_func.py +8 -5
  221. warp/tests/test_generics.py +1 -1
  222. warp/tests/test_indexedarray.py +24 -24
  223. warp/tests/test_intersect.py +39 -9
  224. warp/tests/test_large.py +1 -1
  225. warp/tests/test_lerp.py +3 -1
  226. warp/tests/test_linear_solvers.py +1 -1
  227. warp/tests/test_map.py +35 -4
  228. warp/tests/test_mat.py +52 -62
  229. warp/tests/test_mat_constructors.py +4 -5
  230. warp/tests/test_mat_lite.py +1 -1
  231. warp/tests/test_mat_scalar_ops.py +121 -121
  232. warp/tests/test_math.py +34 -0
  233. warp/tests/test_module_aot.py +4 -4
  234. warp/tests/test_modules_lite.py +28 -2
  235. warp/tests/test_print.py +11 -11
  236. warp/tests/test_quat.py +93 -58
  237. warp/tests/test_runlength_encode.py +1 -1
  238. warp/tests/test_scalar_ops.py +38 -10
  239. warp/tests/test_smoothstep.py +1 -1
  240. warp/tests/test_sparse.py +126 -15
  241. warp/tests/test_spatial.py +105 -87
  242. warp/tests/test_special_values.py +6 -6
  243. warp/tests/test_static.py +7 -7
  244. warp/tests/test_struct.py +13 -2
  245. warp/tests/test_triangle_closest_point.py +48 -1
  246. warp/tests/test_types.py +27 -15
  247. warp/tests/test_utils.py +52 -52
  248. warp/tests/test_vec.py +29 -29
  249. warp/tests/test_vec_constructors.py +5 -5
  250. warp/tests/test_vec_scalar_ops.py +97 -97
  251. warp/tests/test_version.py +75 -0
  252. warp/tests/tile/test_tile.py +178 -0
  253. warp/tests/tile/test_tile_atomic_bitwise.py +403 -0
  254. warp/tests/tile/test_tile_cholesky.py +7 -4
  255. warp/tests/tile/test_tile_load.py +26 -2
  256. warp/tests/tile/test_tile_mathdx.py +3 -3
  257. warp/tests/tile/test_tile_matmul.py +1 -1
  258. warp/tests/tile/test_tile_mlp.py +2 -4
  259. warp/tests/tile/test_tile_reduce.py +214 -13
  260. warp/tests/unittest_suites.py +6 -14
  261. warp/tests/unittest_utils.py +10 -9
  262. warp/tests/walkthrough_debug.py +3 -1
  263. warp/torch.py +6 -373
  264. warp/types.py +29 -5764
  265. warp/utils.py +10 -1659
  266. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/METADATA +46 -99
  267. warp_lang-1.10.0rc2.dist-info/RECORD +468 -0
  268. warp_lang-1.10.0rc2.dist-info/licenses/licenses/Gaia-LICENSE.txt +6 -0
  269. warp_lang-1.10.0rc2.dist-info/licenses/licenses/appdirs-LICENSE.txt +22 -0
  270. warp_lang-1.10.0rc2.dist-info/licenses/licenses/asset_pixel_jpg-LICENSE.txt +3 -0
  271. warp_lang-1.10.0rc2.dist-info/licenses/licenses/cuda-LICENSE.txt +1582 -0
  272. warp_lang-1.10.0rc2.dist-info/licenses/licenses/dlpack-LICENSE.txt +201 -0
  273. warp_lang-1.10.0rc2.dist-info/licenses/licenses/fp16-LICENSE.txt +28 -0
  274. warp_lang-1.10.0rc2.dist-info/licenses/licenses/libmathdx-LICENSE.txt +220 -0
  275. warp_lang-1.10.0rc2.dist-info/licenses/licenses/llvm-LICENSE.txt +279 -0
  276. warp_lang-1.10.0rc2.dist-info/licenses/licenses/moller-LICENSE.txt +16 -0
  277. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nanovdb-LICENSE.txt +2 -0
  278. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nvrtc-LICENSE.txt +1592 -0
  279. warp_lang-1.10.0rc2.dist-info/licenses/licenses/svd-LICENSE.txt +23 -0
  280. warp_lang-1.10.0rc2.dist-info/licenses/licenses/unittest_parallel-LICENSE.txt +21 -0
  281. warp_lang-1.10.0rc2.dist-info/licenses/licenses/usd-LICENSE.txt +213 -0
  282. warp_lang-1.10.0rc2.dist-info/licenses/licenses/windingnumber-LICENSE.txt +21 -0
  283. warp/examples/assets/cartpole.urdf +0 -110
  284. warp/examples/assets/crazyflie.usd +0 -0
  285. warp/examples/assets/nv_ant.xml +0 -92
  286. warp/examples/assets/nv_humanoid.xml +0 -183
  287. warp/examples/assets/quadruped.urdf +0 -268
  288. warp/examples/optim/example_bounce.py +0 -266
  289. warp/examples/optim/example_cloth_throw.py +0 -228
  290. warp/examples/optim/example_drone.py +0 -870
  291. warp/examples/optim/example_inverse_kinematics.py +0 -182
  292. warp/examples/optim/example_inverse_kinematics_torch.py +0 -191
  293. warp/examples/optim/example_softbody_properties.py +0 -400
  294. warp/examples/optim/example_spring_cage.py +0 -245
  295. warp/examples/optim/example_trajectory.py +0 -227
  296. warp/examples/sim/example_cartpole.py +0 -143
  297. warp/examples/sim/example_cloth.py +0 -225
  298. warp/examples/sim/example_cloth_self_contact.py +0 -316
  299. warp/examples/sim/example_granular.py +0 -130
  300. warp/examples/sim/example_granular_collision_sdf.py +0 -202
  301. warp/examples/sim/example_jacobian_ik.py +0 -244
  302. warp/examples/sim/example_particle_chain.py +0 -124
  303. warp/examples/sim/example_quadruped.py +0 -203
  304. warp/examples/sim/example_rigid_chain.py +0 -203
  305. warp/examples/sim/example_rigid_contact.py +0 -195
  306. warp/examples/sim/example_rigid_force.py +0 -133
  307. warp/examples/sim/example_rigid_gyroscopic.py +0 -115
  308. warp/examples/sim/example_rigid_soft_contact.py +0 -140
  309. warp/examples/sim/example_soft_body.py +0 -196
  310. warp/examples/tile/example_tile_walker.py +0 -327
  311. warp/sim/__init__.py +0 -74
  312. warp/sim/articulation.py +0 -793
  313. warp/sim/collide.py +0 -2570
  314. warp/sim/graph_coloring.py +0 -307
  315. warp/sim/import_mjcf.py +0 -791
  316. warp/sim/import_snu.py +0 -227
  317. warp/sim/import_urdf.py +0 -579
  318. warp/sim/import_usd.py +0 -898
  319. warp/sim/inertia.py +0 -357
  320. warp/sim/integrator.py +0 -245
  321. warp/sim/integrator_euler.py +0 -2000
  322. warp/sim/integrator_featherstone.py +0 -2101
  323. warp/sim/integrator_vbd.py +0 -2487
  324. warp/sim/integrator_xpbd.py +0 -3295
  325. warp/sim/model.py +0 -4821
  326. warp/sim/particles.py +0 -121
  327. warp/sim/render.py +0 -431
  328. warp/sim/utils.py +0 -431
  329. warp/tests/sim/disabled_kinematics.py +0 -244
  330. warp/tests/sim/test_cloth.py +0 -863
  331. warp/tests/sim/test_collision.py +0 -743
  332. warp/tests/sim/test_coloring.py +0 -347
  333. warp/tests/sim/test_inertia.py +0 -161
  334. warp/tests/sim/test_model.py +0 -226
  335. warp/tests/sim/test_sim_grad.py +0 -287
  336. warp/tests/sim/test_sim_grad_bounce_linear.py +0 -212
  337. warp/tests/sim/test_sim_kinematics.py +0 -98
  338. warp/thirdparty/__init__.py +0 -0
  339. warp_lang-1.9.1.dist-info/RECORD +0 -456
  340. /warp/{fem → _src/fem}/quadrature/__init__.py +0 -0
  341. /warp/{tests/sim → _src/thirdparty}/__init__.py +0 -0
  342. /warp/{thirdparty → _src/thirdparty}/appdirs.py +0 -0
  343. /warp/{thirdparty → _src/thirdparty}/dlpack.py +0 -0
  344. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/WHEEL +0 -0
  345. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/licenses/LICENSE.md +0 -0
  346. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/top_level.txt +0 -0
@@ -1,3295 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
- # SPDX-License-Identifier: Apache-2.0
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
- import warp as wp
17
-
18
- from .integrator import Integrator
19
- from .model import (
20
- JOINT_MODE_FORCE,
21
- JOINT_MODE_TARGET_POSITION,
22
- JOINT_MODE_TARGET_VELOCITY,
23
- PARTICLE_FLAG_ACTIVE,
24
- Control,
25
- Model,
26
- ModelShapeMaterials,
27
- State,
28
- )
29
- from .utils import vec_abs, vec_leaky_max, vec_leaky_min, vec_max, vec_min, velocity_at_point
30
-
31
-
32
- @wp.kernel
33
- def solve_particle_ground_contacts(
34
- particle_x: wp.array(dtype=wp.vec3),
35
- particle_v: wp.array(dtype=wp.vec3),
36
- invmass: wp.array(dtype=float),
37
- particle_radius: wp.array(dtype=float),
38
- particle_flags: wp.array(dtype=wp.uint32),
39
- ke: float,
40
- kd: float,
41
- kf: float,
42
- mu: float,
43
- ground: wp.array(dtype=float),
44
- dt: float,
45
- relaxation: float,
46
- delta: wp.array(dtype=wp.vec3),
47
- ):
48
- tid = wp.tid()
49
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
50
- return
51
-
52
- wi = invmass[tid]
53
- if wi == 0.0:
54
- return
55
-
56
- x = particle_x[tid]
57
- v = particle_v[tid]
58
-
59
- n = wp.vec3(ground[0], ground[1], ground[2])
60
- c = wp.min(wp.dot(n, x) + ground[3] - particle_radius[tid], 0.0)
61
-
62
- if c > 0.0:
63
- return
64
-
65
- # normal
66
- lambda_n = c
67
- delta_n = n * lambda_n
68
-
69
- # friction
70
- vn = wp.dot(n, v)
71
- vt = v - n * vn
72
-
73
- lambda_f = wp.max(mu * lambda_n, 0.0 - wp.length(vt) * dt)
74
- delta_f = wp.normalize(vt) * lambda_f
75
-
76
- wp.atomic_add(delta, tid, (delta_f - delta_n) * relaxation)
77
-
78
-
79
- @wp.kernel
80
- def apply_particle_shape_restitution(
81
- particle_x_new: wp.array(dtype=wp.vec3),
82
- particle_v_new: wp.array(dtype=wp.vec3),
83
- particle_x_old: wp.array(dtype=wp.vec3),
84
- particle_v_old: wp.array(dtype=wp.vec3),
85
- particle_invmass: wp.array(dtype=float),
86
- particle_radius: wp.array(dtype=float),
87
- particle_flags: wp.array(dtype=wp.uint32),
88
- body_q: wp.array(dtype=wp.transform),
89
- body_qd: wp.array(dtype=wp.spatial_vector),
90
- body_com: wp.array(dtype=wp.vec3),
91
- body_m_inv: wp.array(dtype=float),
92
- body_I_inv: wp.array(dtype=wp.mat33),
93
- shape_body: wp.array(dtype=int),
94
- shape_materials: ModelShapeMaterials,
95
- particle_ka: float,
96
- restitution: float,
97
- contact_count: wp.array(dtype=int),
98
- contact_particle: wp.array(dtype=int),
99
- contact_shape: wp.array(dtype=int),
100
- contact_body_pos: wp.array(dtype=wp.vec3),
101
- contact_body_vel: wp.array(dtype=wp.vec3),
102
- contact_normal: wp.array(dtype=wp.vec3),
103
- contact_max: int,
104
- dt: float,
105
- relaxation: float,
106
- particle_v_out: wp.array(dtype=wp.vec3),
107
- ):
108
- tid = wp.tid()
109
-
110
- count = min(contact_max, contact_count[0])
111
- if tid >= count:
112
- return
113
-
114
- shape_index = contact_shape[tid]
115
- body_index = shape_body[shape_index]
116
- particle_index = contact_particle[tid]
117
-
118
- if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
119
- return
120
-
121
- # x_new = particle_x_new[particle_index]
122
- v_new = particle_v_new[particle_index]
123
- px = particle_x_old[particle_index]
124
- v_old = particle_v_old[particle_index]
125
-
126
- X_wb = wp.transform_identity()
127
- # X_com = wp.vec3()
128
-
129
- if body_index >= 0:
130
- X_wb = body_q[body_index]
131
- # X_com = body_com[body_index]
132
-
133
- # body position in world space
134
- bx = wp.transform_point(X_wb, contact_body_pos[tid])
135
- # r = bx - wp.transform_point(X_wb, X_com)
136
-
137
- n = contact_normal[tid]
138
- c = wp.dot(n, px - bx) - particle_radius[particle_index]
139
-
140
- if c > particle_ka:
141
- return
142
-
143
- rel_vel_old = wp.dot(n, v_old)
144
- rel_vel_new = wp.dot(n, v_new)
145
-
146
- if rel_vel_old < 0.0:
147
- # dv = -n * wp.max(-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0), 0.0)
148
- dv = n * (-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0))
149
-
150
- # compute inverse masses
151
- # w1 = particle_invmass[particle_index]
152
- # w2 = 0.0
153
- # if body_index >= 0:
154
- # angular = wp.cross(r, n)
155
- # q = wp.transform_get_rotation(X_wb)
156
- # rot_angular = wp.quat_rotate_inv(q, angular)
157
- # I_inv = body_I_inv[body_index]
158
- # w2 = body_m_inv[body_index] + wp.dot(rot_angular, I_inv * rot_angular)
159
- # denom = w1 + w2
160
- # if denom == 0.0:
161
- # return
162
-
163
- wp.atomic_add(particle_v_out, tid, dv)
164
-
165
-
166
- @wp.kernel
167
- def apply_particle_ground_restitution(
168
- particle_x_new: wp.array(dtype=wp.vec3),
169
- particle_v_new: wp.array(dtype=wp.vec3),
170
- particle_x_old: wp.array(dtype=wp.vec3),
171
- particle_v_old: wp.array(dtype=wp.vec3),
172
- particle_invmass: wp.array(dtype=float),
173
- particle_radius: wp.array(dtype=float),
174
- particle_flags: wp.array(dtype=wp.uint32),
175
- particle_ka: float,
176
- restitution: float,
177
- ground: wp.array(dtype=float),
178
- dt: float,
179
- relaxation: float,
180
- particle_v_out: wp.array(dtype=wp.vec3),
181
- ):
182
- tid = wp.tid()
183
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
184
- return
185
-
186
- wi = particle_invmass[tid]
187
- if wi == 0.0:
188
- return
189
-
190
- x = particle_x_old[tid]
191
- v_old = particle_v_old[tid]
192
- v_new = particle_v_new[tid]
193
-
194
- n = wp.vec3(ground[0], ground[1], ground[2])
195
- c = wp.dot(n, x) + ground[3] - particle_radius[tid]
196
-
197
- if c > particle_ka:
198
- return
199
-
200
- vn = wp.dot(n, v_old)
201
- vn_new = wp.dot(n, v_new)
202
-
203
- if vn < 0.0:
204
- dv = n * (-vn_new + wp.max(-restitution * vn, 0.0))
205
- wp.atomic_add(particle_v_out, tid, dv)
206
-
207
-
208
- @wp.kernel
209
- def solve_particle_shape_contacts(
210
- particle_x: wp.array(dtype=wp.vec3),
211
- particle_v: wp.array(dtype=wp.vec3),
212
- particle_invmass: wp.array(dtype=float),
213
- particle_radius: wp.array(dtype=float),
214
- particle_flags: wp.array(dtype=wp.uint32),
215
- body_q: wp.array(dtype=wp.transform),
216
- body_qd: wp.array(dtype=wp.spatial_vector),
217
- body_com: wp.array(dtype=wp.vec3),
218
- body_m_inv: wp.array(dtype=float),
219
- body_I_inv: wp.array(dtype=wp.mat33),
220
- shape_body: wp.array(dtype=int),
221
- shape_materials: ModelShapeMaterials,
222
- particle_mu: float,
223
- particle_ka: float,
224
- contact_count: wp.array(dtype=int),
225
- contact_particle: wp.array(dtype=int),
226
- contact_shape: wp.array(dtype=int),
227
- contact_body_pos: wp.array(dtype=wp.vec3),
228
- contact_body_vel: wp.array(dtype=wp.vec3),
229
- contact_normal: wp.array(dtype=wp.vec3),
230
- contact_max: int,
231
- dt: float,
232
- relaxation: float,
233
- # outputs
234
- delta: wp.array(dtype=wp.vec3),
235
- body_delta: wp.array(dtype=wp.spatial_vector),
236
- ):
237
- tid = wp.tid()
238
-
239
- count = min(contact_max, contact_count[0])
240
- if tid >= count:
241
- return
242
-
243
- shape_index = contact_shape[tid]
244
- body_index = shape_body[shape_index]
245
- particle_index = contact_particle[tid]
246
-
247
- if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
248
- return
249
-
250
- px = particle_x[particle_index]
251
- pv = particle_v[particle_index]
252
-
253
- X_wb = wp.transform_identity()
254
- X_com = wp.vec3()
255
-
256
- if body_index >= 0:
257
- X_wb = body_q[body_index]
258
- X_com = body_com[body_index]
259
-
260
- # body position in world space
261
- bx = wp.transform_point(X_wb, contact_body_pos[tid])
262
- r = bx - wp.transform_point(X_wb, X_com)
263
-
264
- n = contact_normal[tid]
265
- c = wp.dot(n, px - bx) - particle_radius[particle_index]
266
-
267
- if c > particle_ka:
268
- return
269
-
270
- # take average material properties of shape and particle parameters
271
- mu = 0.5 * (particle_mu + shape_materials.mu[shape_index])
272
-
273
- # body velocity
274
- body_v_s = wp.spatial_vector()
275
- if body_index >= 0:
276
- body_v_s = body_qd[body_index]
277
-
278
- body_w = wp.spatial_top(body_v_s)
279
- body_v = wp.spatial_bottom(body_v_s)
280
-
281
- # compute the body velocity at the particle position
282
- bv = body_v + wp.cross(body_w, r) + wp.transform_vector(X_wb, contact_body_vel[tid])
283
-
284
- # relative velocity
285
- v = pv - bv
286
-
287
- # normal
288
- lambda_n = c
289
- delta_n = n * lambda_n
290
-
291
- # friction
292
- vn = wp.dot(n, v)
293
- vt = v - n * vn
294
-
295
- # compute inverse masses
296
- w1 = particle_invmass[particle_index]
297
- w2 = 0.0
298
- if body_index >= 0:
299
- angular = wp.cross(r, n)
300
- q = wp.transform_get_rotation(X_wb)
301
- rot_angular = wp.quat_rotate_inv(q, angular)
302
- I_inv = body_I_inv[body_index]
303
- w2 = body_m_inv[body_index] + wp.dot(rot_angular, I_inv * rot_angular)
304
- denom = w1 + w2
305
- if denom == 0.0:
306
- return
307
-
308
- lambda_f = wp.max(mu * lambda_n, -wp.length(vt) * dt)
309
- delta_f = wp.normalize(vt) * lambda_f
310
- delta_total = (delta_f - delta_n) / denom * relaxation
311
-
312
- wp.atomic_add(delta, particle_index, w1 * delta_total)
313
-
314
- if body_index >= 0:
315
- delta_t = wp.cross(r, delta_total)
316
- wp.atomic_sub(body_delta, body_index, wp.spatial_vector(delta_t, delta_total))
317
-
318
-
319
- @wp.kernel
320
- def solve_particle_particle_contacts(
321
- grid: wp.uint64,
322
- particle_x: wp.array(dtype=wp.vec3),
323
- particle_v: wp.array(dtype=wp.vec3),
324
- particle_invmass: wp.array(dtype=float),
325
- particle_radius: wp.array(dtype=float),
326
- particle_flags: wp.array(dtype=wp.uint32),
327
- k_mu: float,
328
- k_cohesion: float,
329
- max_radius: float,
330
- dt: float,
331
- relaxation: float,
332
- # outputs
333
- deltas: wp.array(dtype=wp.vec3),
334
- ):
335
- tid = wp.tid()
336
-
337
- # order threads by cell
338
- i = wp.hash_grid_point_id(grid, tid)
339
- if i == -1:
340
- # hash grid has not been built yet
341
- return
342
- if (particle_flags[i] & PARTICLE_FLAG_ACTIVE) == 0:
343
- return
344
-
345
- x = particle_x[i]
346
- v = particle_v[i]
347
- radius = particle_radius[i]
348
- w1 = particle_invmass[i]
349
-
350
- # particle contact
351
- query = wp.hash_grid_query(grid, x, radius + max_radius + k_cohesion)
352
- index = int(0)
353
-
354
- delta = wp.vec3(0.0)
355
-
356
- while wp.hash_grid_query_next(query, index):
357
- if (particle_flags[index] & PARTICLE_FLAG_ACTIVE) != 0 and index != i:
358
- # compute distance to point
359
- n = x - particle_x[index]
360
- d = wp.length(n)
361
- err = d - radius - particle_radius[index]
362
-
363
- # compute inverse masses
364
- w2 = particle_invmass[index]
365
- denom = w1 + w2
366
-
367
- if err <= k_cohesion and denom > 0.0:
368
- n = n / d
369
- vrel = v - particle_v[index]
370
-
371
- # normal
372
- lambda_n = err
373
- delta_n = n * lambda_n
374
-
375
- # friction
376
- vn = wp.dot(n, vrel)
377
- vt = v - n * vn
378
-
379
- lambda_f = wp.max(k_mu * lambda_n, -wp.length(vt) * dt)
380
- delta_f = wp.normalize(vt) * lambda_f
381
- delta += (delta_f - delta_n) / denom
382
-
383
- wp.atomic_add(deltas, i, delta * w1 * relaxation)
384
-
385
-
386
- @wp.kernel
387
- def solve_springs(
388
- x: wp.array(dtype=wp.vec3),
389
- v: wp.array(dtype=wp.vec3),
390
- invmass: wp.array(dtype=float),
391
- spring_indices: wp.array(dtype=int),
392
- spring_rest_lengths: wp.array(dtype=float),
393
- spring_stiffness: wp.array(dtype=float),
394
- spring_damping: wp.array(dtype=float),
395
- dt: float,
396
- lambdas: wp.array(dtype=float),
397
- delta: wp.array(dtype=wp.vec3),
398
- ):
399
- tid = wp.tid()
400
-
401
- i = spring_indices[tid * 2 + 0]
402
- j = spring_indices[tid * 2 + 1]
403
-
404
- if i == -1 or j == -1:
405
- return
406
-
407
- ke = spring_stiffness[tid]
408
- kd = spring_damping[tid]
409
- rest = spring_rest_lengths[tid]
410
-
411
- xi = x[i]
412
- xj = x[j]
413
-
414
- vi = v[i]
415
- vj = v[j]
416
-
417
- xij = xi - xj
418
- vij = vi - vj
419
-
420
- l = wp.length(xij)
421
-
422
- if l == 0.0:
423
- return
424
-
425
- n = xij / l
426
-
427
- c = l - rest
428
- grad_c_xi = n
429
- grad_c_xj = -1.0 * n
430
-
431
- wi = invmass[i]
432
- wj = invmass[j]
433
-
434
- denom = wi + wj
435
-
436
- # Note strict inequality for damping -- 0 damping is ok
437
- if denom <= 0.0 or ke <= 0.0 or kd < 0.0:
438
- return
439
-
440
- alpha = 1.0 / (ke * dt * dt)
441
- gamma = kd / (ke * dt)
442
-
443
- grad_c_dot_v = dt * wp.dot(grad_c_xi, vij) # Note: dt because from the paper we want x_i - x^n, not v...
444
- dlambda = -1.0 * (c + alpha * lambdas[tid] + gamma * grad_c_dot_v) / ((1.0 + gamma) * denom + alpha)
445
-
446
- dxi = wi * dlambda * grad_c_xi
447
- dxj = wj * dlambda * grad_c_xj
448
-
449
- lambdas[tid] = lambdas[tid] + dlambda
450
-
451
- wp.atomic_add(delta, i, dxi)
452
- wp.atomic_add(delta, j, dxj)
453
-
454
-
455
- @wp.kernel
456
- def bending_constraint(
457
- x: wp.array(dtype=wp.vec3),
458
- v: wp.array(dtype=wp.vec3),
459
- invmass: wp.array(dtype=float),
460
- indices: wp.array2d(dtype=int),
461
- rest: wp.array(dtype=float),
462
- bending_properties: wp.array2d(dtype=float),
463
- dt: float,
464
- lambdas: wp.array(dtype=float),
465
- delta: wp.array(dtype=wp.vec3),
466
- ):
467
- tid = wp.tid()
468
- eps = 1.0e-6
469
-
470
- ke = bending_properties[tid, 0]
471
- kd = bending_properties[tid, 1]
472
-
473
- i = indices[tid, 0]
474
- j = indices[tid, 1]
475
- k = indices[tid, 2]
476
- l = indices[tid, 3]
477
-
478
- if i == -1 or j == -1 or k == -1 or l == -1:
479
- return
480
-
481
- rest_angle = rest[tid]
482
-
483
- x1 = x[i]
484
- x2 = x[j]
485
- x3 = x[k]
486
- x4 = x[l]
487
-
488
- v1 = v[i]
489
- v2 = v[j]
490
- v3 = v[k]
491
- v4 = v[l]
492
-
493
- w1 = invmass[i]
494
- w2 = invmass[j]
495
- w3 = invmass[k]
496
- w4 = invmass[l]
497
-
498
- n1 = wp.cross(x3 - x1, x4 - x1) # normal to face 1
499
- n2 = wp.cross(x4 - x2, x3 - x2) # normal to face 2
500
-
501
- n1_length = wp.length(n1)
502
- n2_length = wp.length(n2)
503
-
504
- if n1_length < eps or n2_length < eps:
505
- return
506
-
507
- n1 /= n1_length
508
- n2 /= n2_length
509
-
510
- cos_theta = wp.dot(n1, n2)
511
-
512
- e = x4 - x3
513
- e_hat = wp.normalize(e)
514
- e_length = wp.length(e)
515
-
516
- derivative_flip = wp.sign(wp.dot(wp.cross(n1, n2), e))
517
- derivative_flip *= -1.0
518
- angle = wp.acos(cos_theta)
519
-
520
- grad_x1 = n1 * e_length * derivative_flip
521
- grad_x2 = n2 * e_length * derivative_flip
522
- grad_x3 = (n1 * wp.dot(x1 - x4, e_hat) + n2 * wp.dot(x2 - x4, e_hat)) * derivative_flip
523
- grad_x4 = (n1 * wp.dot(x3 - x1, e_hat) + n2 * wp.dot(x3 - x2, e_hat)) * derivative_flip
524
- c = angle - rest_angle
525
- denominator = (
526
- w1 * wp.length_sq(grad_x1)
527
- + w2 * wp.length_sq(grad_x2)
528
- + w3 * wp.length_sq(grad_x3)
529
- + w4 * wp.length_sq(grad_x4)
530
- )
531
-
532
- # Note strict inequality for damping -- 0 damping is ok
533
- if denominator <= 0.0 or ke <= 0.0 or kd < 0.0:
534
- return
535
-
536
- alpha = 1.0 / (ke * dt * dt)
537
- gamma = kd / (ke * dt)
538
-
539
- grad_dot_v = dt * (wp.dot(grad_x1, v1) + wp.dot(grad_x2, v2) + wp.dot(grad_x3, v3) + wp.dot(grad_x4, v4))
540
-
541
- dlambda = -1.0 * (c + alpha * lambdas[tid] + gamma * grad_dot_v) / ((1.0 + gamma) * denominator + alpha)
542
-
543
- delta0 = w1 * dlambda * grad_x1
544
- delta1 = w2 * dlambda * grad_x2
545
- delta2 = w3 * dlambda * grad_x3
546
- delta3 = w4 * dlambda * grad_x4
547
-
548
- lambdas[tid] = lambdas[tid] + dlambda
549
-
550
- wp.atomic_add(delta, i, delta0)
551
- wp.atomic_add(delta, j, delta1)
552
- wp.atomic_add(delta, k, delta2)
553
- wp.atomic_add(delta, l, delta3)
554
-
555
-
556
- @wp.kernel
557
- def solve_tetrahedra(
558
- x: wp.array(dtype=wp.vec3),
559
- v: wp.array(dtype=wp.vec3),
560
- inv_mass: wp.array(dtype=float),
561
- indices: wp.array(dtype=int, ndim=2),
562
- rest_matrix: wp.array(dtype=wp.mat33),
563
- activation: wp.array(dtype=float),
564
- materials: wp.array(dtype=float, ndim=2),
565
- dt: float,
566
- relaxation: float,
567
- delta: wp.array(dtype=wp.vec3),
568
- ):
569
- tid = wp.tid()
570
-
571
- i = indices[tid, 0]
572
- j = indices[tid, 1]
573
- k = indices[tid, 2]
574
- l = indices[tid, 3]
575
-
576
- # act = activation[tid]
577
-
578
- # k_mu = materials[tid, 0]
579
- # k_lambda = materials[tid, 1]
580
- # k_damp = materials[tid, 2]
581
-
582
- x0 = x[i]
583
- x1 = x[j]
584
- x2 = x[k]
585
- x3 = x[l]
586
-
587
- # v0 = v[i]
588
- # v1 = v[j]
589
- # v2 = v[k]
590
- # v3 = v[l]
591
-
592
- w0 = inv_mass[i]
593
- w1 = inv_mass[j]
594
- w2 = inv_mass[k]
595
- w3 = inv_mass[l]
596
-
597
- x10 = x1 - x0
598
- x20 = x2 - x0
599
- x30 = x3 - x0
600
-
601
- Ds = wp.matrix_from_cols(x10, x20, x30)
602
- Dm = rest_matrix[tid]
603
- inv_QT = wp.transpose(Dm)
604
-
605
- inv_rest_volume = wp.determinant(Dm) * 6.0
606
-
607
- # F = Xs*Xm^-1
608
- F = Ds * Dm
609
-
610
- f1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
611
- f2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
612
- f3 = wp.vec3(F[0, 2], F[1, 2], F[2, 2])
613
-
614
- tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
615
-
616
- C = float(0.0)
617
- dC = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
618
- compliance = float(0.0)
619
-
620
- stretching_compliance = relaxation
621
- volume_compliance = relaxation
622
-
623
- num_terms = 2
624
- for term in range(0, num_terms):
625
- if term == 0:
626
- # deviatoric, stable
627
- C = tr - 3.0
628
- dC = F * 2.0
629
- compliance = stretching_compliance
630
- elif term == 1:
631
- # volume conservation
632
- C = wp.determinant(F) - 1.0
633
- dC = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))
634
- compliance = volume_compliance
635
-
636
- if C != 0.0:
637
- dP = dC * inv_QT
638
- grad1 = wp.vec3(dP[0][0], dP[1][0], dP[2][0])
639
- grad2 = wp.vec3(dP[0][1], dP[1][1], dP[2][1])
640
- grad3 = wp.vec3(dP[0][2], dP[1][2], dP[2][2])
641
- grad0 = -grad1 - grad2 - grad3
642
-
643
- w = (
644
- wp.dot(grad0, grad0) * w0
645
- + wp.dot(grad1, grad1) * w1
646
- + wp.dot(grad2, grad2) * w2
647
- + wp.dot(grad3, grad3) * w3
648
- )
649
-
650
- if w > 0.0:
651
- alpha = compliance / dt / dt
652
- if inv_rest_volume > 0.0:
653
- alpha *= inv_rest_volume
654
- dlambda = -C / (w + alpha)
655
-
656
- wp.atomic_add(delta, i, w0 * dlambda * grad0)
657
- wp.atomic_add(delta, j, w1 * dlambda * grad1)
658
- wp.atomic_add(delta, k, w2 * dlambda * grad2)
659
- wp.atomic_add(delta, l, w3 * dlambda * grad3)
660
- # wp.atomic_add(particle.num_corr, id0, 1)
661
- # wp.atomic_add(particle.num_corr, id1, 1)
662
- # wp.atomic_add(particle.num_corr, id2, 1)
663
- # wp.atomic_add(particle.num_corr, id3, 1)
664
-
665
- # C_Spherical
666
- # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
667
- # r_s_inv = 1.0/r_s
668
- # C = r_s - wp.sqrt(3.0)
669
- # dCdx = F*wp.transpose(Dm)*r_s_inv
670
- # alpha = 1.0
671
-
672
- # C_D
673
- # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
674
- # C = r_s*r_s - 3.0
675
- # dCdx = F*wp.transpose(Dm)*2.0
676
- # alpha = 1.0
677
-
678
- # grad1 = wp.vec3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0])
679
- # grad2 = wp.vec3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1])
680
- # grad3 = wp.vec3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2])
681
- # grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0)
682
-
683
- # denom = (
684
- # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
685
- # )
686
- # multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume))
687
-
688
- # delta0 = grad0 * multiplier
689
- # delta1 = grad1 * multiplier
690
- # delta2 = grad2 * multiplier
691
- # delta3 = grad3 * multiplier
692
-
693
- # # hydrostatic part
694
- # J = wp.determinant(F)
695
-
696
- # C_vol = J - alpha
697
- # # dCdx = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))*wp.transpose(Dm)
698
-
699
- # # grad1 = wp.vec3(dCdx[0,0], dCdx[1,0], dCdx[2,0])
700
- # # grad2 = wp.vec3(dCdx[0,1], dCdx[1,1], dCdx[2,1])
701
- # # grad3 = wp.vec3(dCdx[0,2], dCdx[1,2], dCdx[2,2])
702
- # # grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0)
703
-
704
- # s = inv_rest_volume / 6.0
705
- # grad1 = wp.cross(x20, x30) * s
706
- # grad2 = wp.cross(x30, x10) * s
707
- # grad3 = wp.cross(x10, x20) * s
708
- # grad0 = -(grad1 + grad2 + grad3)
709
-
710
- # denom = (
711
- # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
712
- # )
713
- # multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume))
714
-
715
- # delta0 += grad0 * multiplier
716
- # delta1 += grad1 * multiplier
717
- # delta2 += grad2 * multiplier
718
- # delta3 += grad3 * multiplier
719
-
720
- # # # apply forces
721
- # # wp.atomic_sub(delta, i, delta0 * w0 * relaxation)
722
- # # wp.atomic_sub(delta, j, delta1 * w1 * relaxation)
723
- # # wp.atomic_sub(delta, k, delta2 * w2 * relaxation)
724
- # # wp.atomic_sub(delta, l, delta3 * w3 * relaxation)
725
-
726
-
727
- @wp.kernel
728
- def solve_tetrahedra2(
729
- x: wp.array(dtype=wp.vec3),
730
- v: wp.array(dtype=wp.vec3),
731
- inv_mass: wp.array(dtype=float),
732
- indices: wp.array(dtype=int, ndim=2),
733
- pose: wp.array(dtype=wp.mat33),
734
- activation: wp.array(dtype=float),
735
- materials: wp.array(dtype=float, ndim=2),
736
- dt: float,
737
- relaxation: float,
738
- delta: wp.array(dtype=wp.vec3),
739
- ):
740
- tid = wp.tid()
741
-
742
- i = indices[tid, 0]
743
- j = indices[tid, 1]
744
- k = indices[tid, 2]
745
- l = indices[tid, 3]
746
-
747
- # act = activation[tid]
748
-
749
- k_mu = materials[tid, 0]
750
- k_lambda = materials[tid, 1]
751
- # k_damp = materials[tid, 2]
752
-
753
- x0 = x[i]
754
- x1 = x[j]
755
- x2 = x[k]
756
- x3 = x[l]
757
-
758
- # v0 = v[i]
759
- # v1 = v[j]
760
- # v2 = v[k]
761
- # v3 = v[l]
762
-
763
- w0 = inv_mass[i]
764
- w1 = inv_mass[j]
765
- w2 = inv_mass[k]
766
- w3 = inv_mass[l]
767
-
768
- x10 = x1 - x0
769
- x20 = x2 - x0
770
- x30 = x3 - x0
771
-
772
- Ds = wp.matrix_from_cols(x10, x20, x30)
773
- Dm = pose[tid]
774
-
775
- inv_rest_volume = wp.determinant(Dm) * 6.0
776
- rest_volume = 1.0 / inv_rest_volume
777
-
778
- # F = Xs*Xm^-1
779
- F = Ds * Dm
780
-
781
- f1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
782
- f2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
783
- f3 = wp.vec3(F[0, 2], F[1, 2], F[2, 2])
784
-
785
- # C_sqrt
786
- # tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
787
- # r_s = wp.sqrt(abs(tr - 3.0))
788
- # C = r_s
789
-
790
- # if (r_s == 0.0):
791
- # return
792
-
793
- # if (tr < 3.0):
794
- # r_s = 0.0 - r_s
795
-
796
- # dCdx = F*wp.transpose(Dm)*(1.0/r_s)
797
- # alpha = 1.0 + k_mu / k_lambda
798
-
799
- # C_Neo
800
- r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
801
- if r_s == 0.0:
802
- return
803
- # tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
804
- # if (tr < 3.0):
805
- # r_s = -r_s
806
- r_s_inv = 1.0 / r_s
807
- C = r_s
808
- dCdx = F * wp.transpose(Dm) * r_s_inv
809
- alpha = 1.0 + k_mu / k_lambda
810
-
811
- # C_Spherical
812
- # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
813
- # r_s_inv = 1.0/r_s
814
- # C = r_s - wp.sqrt(3.0)
815
- # dCdx = F*wp.transpose(Dm)*r_s_inv
816
- # alpha = 1.0
817
-
818
- # C_D
819
- # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
820
- # C = r_s*r_s - 3.0
821
- # dCdx = F*wp.transpose(Dm)*2.0
822
- # alpha = 1.0
823
-
824
- grad1 = wp.vec3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0])
825
- grad2 = wp.vec3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1])
826
- grad3 = wp.vec3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2])
827
- grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0)
828
-
829
- denom = (
830
- wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
831
- )
832
- multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume))
833
-
834
- delta0 = grad0 * multiplier
835
- delta1 = grad1 * multiplier
836
- delta2 = grad2 * multiplier
837
- delta3 = grad3 * multiplier
838
-
839
- # hydrostatic part
840
- J = wp.determinant(F)
841
-
842
- C_vol = J - alpha
843
- # dCdx = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))*wp.transpose(Dm)
844
-
845
- # grad1 = wp.vec3(dCdx[0,0], dCdx[1,0], dCdx[2,0])
846
- # grad2 = wp.vec3(dCdx[0,1], dCdx[1,1], dCdx[2,1])
847
- # grad3 = wp.vec3(dCdx[0,2], dCdx[1,2], dCdx[2,2])
848
- # grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0)
849
-
850
- s = inv_rest_volume / 6.0
851
- grad1 = wp.cross(x20, x30) * s
852
- grad2 = wp.cross(x30, x10) * s
853
- grad3 = wp.cross(x10, x20) * s
854
- grad0 = -(grad1 + grad2 + grad3)
855
-
856
- denom = (
857
- wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
858
- )
859
- multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume))
860
-
861
- delta0 += grad0 * multiplier
862
- delta1 += grad1 * multiplier
863
- delta2 += grad2 * multiplier
864
- delta3 += grad3 * multiplier
865
-
866
- # apply forces
867
- wp.atomic_sub(delta, i, delta0 * w0 * relaxation)
868
- wp.atomic_sub(delta, j, delta1 * w1 * relaxation)
869
- wp.atomic_sub(delta, k, delta2 * w2 * relaxation)
870
- wp.atomic_sub(delta, l, delta3 * w3 * relaxation)
871
-
872
-
873
- @wp.kernel
874
- def apply_particle_deltas(
875
- x_orig: wp.array(dtype=wp.vec3),
876
- x_pred: wp.array(dtype=wp.vec3),
877
- particle_flags: wp.array(dtype=wp.uint32),
878
- delta: wp.array(dtype=wp.vec3),
879
- dt: float,
880
- v_max: float,
881
- x_out: wp.array(dtype=wp.vec3),
882
- v_out: wp.array(dtype=wp.vec3),
883
- ):
884
- tid = wp.tid()
885
- if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
886
- return
887
-
888
- x0 = x_orig[tid]
889
- xp = x_pred[tid]
890
-
891
- # constraint deltas
892
- d = delta[tid]
893
-
894
- x_new = xp + d
895
- v_new = (x_new - x0) / dt
896
-
897
- # enforce velocity limit to prevent instability
898
- v_new_mag = wp.length(v_new)
899
- if v_new_mag > v_max:
900
- v_new *= v_max / v_new_mag
901
-
902
- x_out[tid] = x_new
903
- v_out[tid] = v_new
904
-
905
-
906
- @wp.kernel
907
- def apply_body_deltas(
908
- q_in: wp.array(dtype=wp.transform),
909
- qd_in: wp.array(dtype=wp.spatial_vector),
910
- body_com: wp.array(dtype=wp.vec3),
911
- body_I: wp.array(dtype=wp.mat33),
912
- body_inv_m: wp.array(dtype=float),
913
- body_inv_I: wp.array(dtype=wp.mat33),
914
- deltas: wp.array(dtype=wp.spatial_vector),
915
- constraint_inv_weights: wp.array(dtype=float),
916
- dt: float,
917
- # outputs
918
- q_out: wp.array(dtype=wp.transform),
919
- qd_out: wp.array(dtype=wp.spatial_vector),
920
- ):
921
- tid = wp.tid()
922
- inv_m = body_inv_m[tid]
923
- if inv_m == 0.0:
924
- q_out[tid] = q_in[tid]
925
- qd_out[tid] = qd_in[tid]
926
- return
927
- inv_I = body_inv_I[tid]
928
-
929
- tf = q_in[tid]
930
- delta = deltas[tid]
931
-
932
- p0 = wp.transform_get_translation(tf)
933
- q0 = wp.transform_get_rotation(tf)
934
-
935
- weight = 1.0
936
- if constraint_inv_weights:
937
- inv_weight = constraint_inv_weights[tid]
938
- if inv_weight > 0.0:
939
- weight = 1.0 / inv_weight
940
-
941
- dp = wp.spatial_bottom(delta) * (inv_m * weight)
942
- dq = wp.spatial_top(delta) * weight
943
- dq = wp.quat_rotate(q0, inv_I * wp.quat_rotate_inv(q0, dq))
944
-
945
- # update orientation
946
- q1 = q0 + 0.5 * wp.quat(dq * dt, 0.0) * q0
947
- q1 = wp.normalize(q1)
948
-
949
- # update position
950
- com = body_com[tid]
951
- x_com = p0 + wp.quat_rotate(q0, com)
952
- p1 = x_com + dp * dt
953
- p1 -= wp.quat_rotate(q1, com)
954
-
955
- q_out[tid] = wp.transform(p1, q1)
956
-
957
- v0 = wp.spatial_bottom(qd_in[tid])
958
- w0 = wp.spatial_top(qd_in[tid])
959
-
960
- # update linear and angular velocity
961
- v1 = v0 + dp
962
- # angular part (compute in body frame)
963
- wb = wp.quat_rotate_inv(q0, w0 + dq)
964
- tb = -wp.cross(wb, body_I[tid] * wb) # coriolis forces
965
- w1 = wp.quat_rotate(q0, wb + inv_I * tb * dt)
966
-
967
- # XXX this improves gradient stability
968
- if wp.length(v1) < 1e-4:
969
- v1 = wp.vec3(0.0)
970
- if wp.length(w1) < 1e-4:
971
- w1 = wp.vec3(0.0)
972
-
973
- qd_out[tid] = wp.spatial_vector(w1, v1)
974
-
975
-
976
- @wp.kernel
977
- def apply_body_delta_velocities(
978
- deltas: wp.array(dtype=wp.spatial_vector),
979
- qd_out: wp.array(dtype=wp.spatial_vector),
980
- ):
981
- tid = wp.tid()
982
- wp.atomic_add(qd_out, tid, deltas[tid])
983
-
984
-
985
- @wp.kernel
986
- def apply_joint_actions(
987
- body_q: wp.array(dtype=wp.transform),
988
- body_com: wp.array(dtype=wp.vec3),
989
- joint_type: wp.array(dtype=int),
990
- joint_parent: wp.array(dtype=int),
991
- joint_child: wp.array(dtype=int),
992
- joint_X_p: wp.array(dtype=wp.transform),
993
- joint_X_c: wp.array(dtype=wp.transform),
994
- joint_axis_start: wp.array(dtype=int),
995
- joint_axis_dim: wp.array(dtype=int, ndim=2),
996
- joint_axis: wp.array(dtype=wp.vec3),
997
- joint_axis_mode: wp.array(dtype=int),
998
- joint_act: wp.array(dtype=float),
999
- body_f: wp.array(dtype=wp.spatial_vector),
1000
- ):
1001
- tid = wp.tid()
1002
- type = joint_type[tid]
1003
- if type == wp.sim.JOINT_FIXED:
1004
- return
1005
- if type == wp.sim.JOINT_FREE:
1006
- return
1007
- if type == wp.sim.JOINT_DISTANCE:
1008
- return
1009
- if type == wp.sim.JOINT_BALL:
1010
- return
1011
-
1012
- # rigid body indices of the child and parent
1013
- id_c = joint_child[tid]
1014
- id_p = joint_parent[tid]
1015
-
1016
- X_pj = joint_X_p[tid]
1017
- # X_cj = joint_X_c[tid]
1018
-
1019
- X_wp = X_pj
1020
- pose_p = X_pj
1021
- com_p = wp.vec3(0.0)
1022
- # parent transform and moment arm
1023
- if id_p >= 0:
1024
- pose_p = body_q[id_p]
1025
- X_wp = pose_p * X_wp
1026
- com_p = body_com[id_p]
1027
- r_p = wp.transform_get_translation(X_wp) - wp.transform_point(pose_p, com_p)
1028
-
1029
- # child transform and moment arm
1030
- pose_c = body_q[id_c]
1031
- X_wc = pose_c
1032
- com_c = body_com[id_c]
1033
- r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
1034
-
1035
- # # local joint rotations
1036
- # q_p = wp.transform_get_rotation(X_wp)
1037
- # q_c = wp.transform_get_rotation(X_wc)
1038
-
1039
- # joint properties (for 1D joints)
1040
- axis_start = joint_axis_start[tid]
1041
- lin_axis_count = joint_axis_dim[tid, 0]
1042
- ang_axis_count = joint_axis_dim[tid, 1]
1043
-
1044
- # total force/torque on the parent
1045
- t_total = wp.vec3()
1046
- f_total = wp.vec3()
1047
-
1048
- # handle angular constraints
1049
- if type == wp.sim.JOINT_REVOLUTE:
1050
- mode = joint_axis_mode[axis_start]
1051
- if mode == wp.sim.JOINT_MODE_FORCE:
1052
- axis = joint_axis[axis_start]
1053
- act = joint_act[axis_start]
1054
- a_p = wp.transform_vector(X_wp, axis)
1055
- t_total += act * a_p
1056
- elif type == wp.sim.JOINT_PRISMATIC:
1057
- mode = joint_axis_mode[axis_start]
1058
- if mode == wp.sim.JOINT_MODE_FORCE:
1059
- axis = joint_axis[axis_start]
1060
- act = joint_act[axis_start]
1061
- a_p = wp.transform_vector(X_wp, axis)
1062
- f_total += act * a_p
1063
- elif type == wp.sim.JOINT_COMPOUND:
1064
- # q_off = wp.transform_get_rotation(X_cj)
1065
- # q_pc = wp.quat_inverse(q_off)*wp.quat_inverse(q_p)*q_c*q_off
1066
- # # decompose to a compound rotation each axis
1067
- # angles = quat_decompose(q_pc)
1068
-
1069
- # # reconstruct rotation axes
1070
- # axis_0 = wp.vec3(1.0, 0.0, 0.0)
1071
- # q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1072
-
1073
- # axis_1 = wp.quat_rotate(q_0, wp.vec3(0.0, 1.0, 0.0))
1074
- # q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1075
-
1076
- # axis_2 = wp.quat_rotate(q_1*q_0, wp.vec3(0.0, 0.0, 1.0))
1077
-
1078
- # q_w = q_p*q_off
1079
- # t_total += joint_act[qd_start+0] * wp.quat_rotate(q_w, axis_0)
1080
- # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
1081
- # t_total += joint_act[qd_start+2] * wp.quat_rotate(q_w, axis_2)
1082
-
1083
- if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1084
- axis_0 = joint_axis[axis_start + 0]
1085
- t_total += joint_act[axis_start + 0] * wp.transform_vector(X_wp, axis_0)
1086
- if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1087
- axis_1 = joint_axis[axis_start + 1]
1088
- t_total += joint_act[axis_start + 1] * wp.transform_vector(X_wp, axis_1)
1089
- if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1090
- axis_2 = joint_axis[axis_start + 2]
1091
- t_total += joint_act[axis_start + 2] * wp.transform_vector(X_wp, axis_2)
1092
-
1093
- elif type == wp.sim.JOINT_UNIVERSAL:
1094
- # q_off = wp.transform_get_rotation(X_cj)
1095
- # q_pc = wp.quat_inverse(q_off)*wp.quat_inverse(q_p)*q_c*q_off
1096
-
1097
- # # decompose to a compound rotation each axis
1098
- # angles = quat_decompose(q_pc)
1099
-
1100
- # # reconstruct rotation axes
1101
- # axis_0 = wp.vec3(1.0, 0.0, 0.0)
1102
- # q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1103
-
1104
- # axis_1 = wp.quat_rotate(q_0, wp.vec3(0.0, 1.0, 0.0))
1105
- # q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1106
-
1107
- # axis_2 = wp.quat_rotate(q_1*q_0, wp.vec3(0.0, 0.0, 1.0))
1108
-
1109
- # q_w = q_p*q_off
1110
-
1111
- # free axes
1112
- # t_total += joint_act[qd_start+0] * wp.quat_rotate(q_w, axis_0)
1113
- # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
1114
-
1115
- if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1116
- axis_0 = joint_axis[axis_start + 0]
1117
- t_total += joint_act[axis_start + 0] * wp.transform_vector(X_wp, axis_0)
1118
- if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1119
- axis_1 = joint_axis[axis_start + 1]
1120
- t_total += joint_act[axis_start + 1] * wp.transform_vector(X_wp, axis_1)
1121
-
1122
- elif type == wp.sim.JOINT_D6:
1123
- # unroll for loop to ensure joint actions remain differentiable
1124
- # (since differentiating through a dynamic for loop that updates a local variable is not supported)
1125
-
1126
- if lin_axis_count > 0:
1127
- if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1128
- axis = joint_axis[axis_start + 0]
1129
- act = joint_act[axis_start + 0]
1130
- a_p = wp.transform_vector(X_wp, axis)
1131
- f_total += act * a_p
1132
- if lin_axis_count > 1:
1133
- if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1134
- axis = joint_axis[axis_start + 1]
1135
- act = joint_act[axis_start + 1]
1136
- a_p = wp.transform_vector(X_wp, axis)
1137
- f_total += act * a_p
1138
- if lin_axis_count > 2:
1139
- if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1140
- axis = joint_axis[axis_start + 2]
1141
- act = joint_act[axis_start + 2]
1142
- a_p = wp.transform_vector(X_wp, axis)
1143
- f_total += act * a_p
1144
-
1145
- if ang_axis_count > 0:
1146
- if joint_axis_mode[axis_start + lin_axis_count + 0] == wp.sim.JOINT_MODE_FORCE:
1147
- axis = joint_axis[axis_start + lin_axis_count + 0]
1148
- act = joint_act[axis_start + lin_axis_count + 0]
1149
- a_p = wp.transform_vector(X_wp, axis)
1150
- t_total += act * a_p
1151
- if ang_axis_count > 1:
1152
- if joint_axis_mode[axis_start + lin_axis_count + 1] == wp.sim.JOINT_MODE_FORCE:
1153
- axis = joint_axis[axis_start + lin_axis_count + 1]
1154
- act = joint_act[axis_start + lin_axis_count + 1]
1155
- a_p = wp.transform_vector(X_wp, axis)
1156
- t_total += act * a_p
1157
- if ang_axis_count > 2:
1158
- if joint_axis_mode[axis_start + lin_axis_count + 2] == wp.sim.JOINT_MODE_FORCE:
1159
- axis = joint_axis[axis_start + lin_axis_count + 2]
1160
- act = joint_act[axis_start + lin_axis_count + 2]
1161
- a_p = wp.transform_vector(X_wp, axis)
1162
- t_total += act * a_p
1163
-
1164
- else:
1165
- print("joint type not handled in apply_joint_actions")
1166
-
1167
- # write forces
1168
- if id_p >= 0:
1169
- wp.atomic_sub(body_f, id_p, wp.spatial_vector(t_total + wp.cross(r_p, f_total), f_total))
1170
- wp.atomic_add(body_f, id_c, wp.spatial_vector(t_total + wp.cross(r_c, f_total), f_total))
1171
-
1172
-
1173
- @wp.func
1174
- def update_joint_axis_mode(mode: wp.int32, axis: wp.vec3, input_axis_mode: wp.vec3i):
1175
- # update the 3D axis mode flags given the axis vector and mode of this axis
1176
- mode_x = wp.max(wp.int32(wp.nonzero(axis[0])) * mode, input_axis_mode[0])
1177
- mode_y = wp.max(wp.int32(wp.nonzero(axis[1])) * mode, input_axis_mode[1])
1178
- mode_z = wp.max(wp.int32(wp.nonzero(axis[2])) * mode, input_axis_mode[2])
1179
- return wp.vec3i(mode_x, mode_y, mode_z)
1180
-
1181
-
1182
- @wp.func
1183
- def update_joint_axis_limits(axis: wp.vec3, limit_lower: float, limit_upper: float, input_limits: wp.spatial_vector):
1184
- # update the 3D linear/angular limits (spatial_vector [lower, upper]) given the axis vector and limits
1185
- lo_temp = axis * limit_lower
1186
- up_temp = axis * limit_upper
1187
- lo = vec_min(lo_temp, up_temp)
1188
- up = vec_max(lo_temp, up_temp)
1189
- input_lower = wp.spatial_top(input_limits)
1190
- input_upper = wp.spatial_bottom(input_limits)
1191
- lower = vec_min(input_lower, lo)
1192
- upper = vec_max(input_upper, up)
1193
- return wp.spatial_vector(lower, upper)
1194
-
1195
-
1196
- @wp.func
1197
- def update_joint_axis_target_ke_kd(
1198
- axis: wp.vec3, target: float, target_ke: float, target_kd: float, input_target_ke_kd: wp.mat33
1199
- ):
1200
- # update the 3D linear/angular target, target_ke, and target_kd (mat33 [target, ke, kd]) given the axis vector and target, target_ke, target_kd
1201
- axis_target = input_target_ke_kd[0]
1202
- axis_ke = input_target_ke_kd[1]
1203
- axis_kd = input_target_ke_kd[2]
1204
- stiffness = axis * target_ke
1205
- axis_target += stiffness * target # weighted target (to be normalized later by sum of target_ke)
1206
- axis_ke += vec_abs(stiffness)
1207
- axis_kd += vec_abs(axis * target_kd)
1208
- return wp.mat33(
1209
- axis_target[0],
1210
- axis_target[1],
1211
- axis_target[2],
1212
- axis_ke[0],
1213
- axis_ke[1],
1214
- axis_ke[2],
1215
- axis_kd[0],
1216
- axis_kd[1],
1217
- axis_kd[2],
1218
- )
1219
-
1220
-
1221
- @wp.func
1222
- def compute_linear_correction_3d(
1223
- dx: wp.vec3,
1224
- r1: wp.vec3,
1225
- r2: wp.vec3,
1226
- tf1: wp.transform,
1227
- tf2: wp.transform,
1228
- m_inv1: float,
1229
- m_inv2: float,
1230
- I_inv1: wp.mat33,
1231
- I_inv2: wp.mat33,
1232
- lambda_in: float,
1233
- compliance: float,
1234
- damping: float,
1235
- dt: float,
1236
- ) -> float:
1237
- c = wp.length(dx)
1238
- if c == 0.0:
1239
- # print("c == 0.0 in positional correction")
1240
- return 0.0
1241
-
1242
- n = wp.normalize(dx)
1243
-
1244
- q1 = wp.transform_get_rotation(tf1)
1245
- q2 = wp.transform_get_rotation(tf2)
1246
-
1247
- # Eq. 2-3 (make sure to project into the frame of the body)
1248
- r1xn = wp.quat_rotate_inv(q1, wp.cross(r1, n))
1249
- r2xn = wp.quat_rotate_inv(q2, wp.cross(r2, n))
1250
-
1251
- w1 = m_inv1 + wp.dot(r1xn, I_inv1 * r1xn)
1252
- w2 = m_inv2 + wp.dot(r2xn, I_inv2 * r2xn)
1253
- w = w1 + w2
1254
- if w == 0.0:
1255
- return 0.0
1256
- alpha = compliance
1257
- gamma = compliance * damping
1258
-
1259
- # Eq. 4-5
1260
- d_lambda = -c - alpha * lambda_in
1261
- # TODO consider damping for velocity correction?
1262
- # delta_lambda = -(err + alpha * lambda_in + gamma * derr)
1263
- if w + alpha > 0.0:
1264
- d_lambda /= w * (dt + gamma) + alpha / dt
1265
-
1266
- return d_lambda
1267
-
1268
-
1269
- @wp.func
1270
- def compute_angular_correction_3d(
1271
- corr: wp.vec3,
1272
- q1: wp.quat,
1273
- q2: wp.quat,
1274
- m_inv1: float,
1275
- m_inv2: float,
1276
- I_inv1: wp.mat33,
1277
- I_inv2: wp.mat33,
1278
- alpha_tilde: float,
1279
- # lambda_prev: float,
1280
- relaxation: float,
1281
- dt: float,
1282
- ):
1283
- # compute and apply the correction impulse for an angular constraint
1284
- theta = wp.length(corr)
1285
- if theta == 0.0:
1286
- return 0.0
1287
-
1288
- n = wp.normalize(corr)
1289
-
1290
- # project variables to body rest frame as they are in local matrix
1291
- n1 = wp.quat_rotate_inv(q1, n)
1292
- n2 = wp.quat_rotate_inv(q2, n)
1293
-
1294
- # Eq. 11-12
1295
- w1 = wp.dot(n1, I_inv1 * n1)
1296
- w2 = wp.dot(n2, I_inv2 * n2)
1297
- w = w1 + w2
1298
- if w == 0.0:
1299
- return 0.0
1300
-
1301
- # Eq. 13-14
1302
- lambda_prev = 0.0
1303
- d_lambda = (-theta - alpha_tilde * lambda_prev) / (w * dt + alpha_tilde / dt)
1304
- # TODO consider lambda_prev?
1305
- # p = d_lambda * n * relaxation
1306
-
1307
- # Eq. 15-16
1308
- return d_lambda
1309
-
1310
-
1311
- @wp.kernel
1312
- def solve_simple_body_joints(
1313
- body_q: wp.array(dtype=wp.transform),
1314
- body_qd: wp.array(dtype=wp.spatial_vector),
1315
- body_com: wp.array(dtype=wp.vec3),
1316
- body_inv_m: wp.array(dtype=float),
1317
- body_inv_I: wp.array(dtype=wp.mat33),
1318
- joint_type: wp.array(dtype=int),
1319
- joint_enabled: wp.array(dtype=int),
1320
- joint_parent: wp.array(dtype=int),
1321
- joint_child: wp.array(dtype=int),
1322
- joint_X_p: wp.array(dtype=wp.transform),
1323
- joint_X_c: wp.array(dtype=wp.transform),
1324
- joint_limit_lower: wp.array(dtype=float),
1325
- joint_limit_upper: wp.array(dtype=float),
1326
- joint_axis_start: wp.array(dtype=int),
1327
- joint_axis_dim: wp.array(dtype=int, ndim=2),
1328
- joint_axis_mode: wp.array(dtype=int),
1329
- joint_axis: wp.array(dtype=wp.vec3),
1330
- joint_target: wp.array(dtype=float),
1331
- joint_target_ke: wp.array(dtype=float),
1332
- joint_target_kd: wp.array(dtype=float),
1333
- joint_linear_compliance: wp.array(dtype=float),
1334
- joint_angular_compliance: wp.array(dtype=float),
1335
- angular_relaxation: float,
1336
- linear_relaxation: float,
1337
- dt: float,
1338
- deltas: wp.array(dtype=wp.spatial_vector),
1339
- ):
1340
- tid = wp.tid()
1341
- type = joint_type[tid]
1342
-
1343
- if joint_enabled[tid] == 0:
1344
- return
1345
- if type == wp.sim.JOINT_FREE:
1346
- return
1347
- if type == wp.sim.JOINT_COMPOUND:
1348
- return
1349
- if type == wp.sim.JOINT_UNIVERSAL:
1350
- return
1351
- if type == wp.sim.JOINT_DISTANCE:
1352
- return
1353
- if type == wp.sim.JOINT_D6:
1354
- return
1355
-
1356
- # rigid body indices of the child and parent
1357
- id_c = joint_child[tid]
1358
- id_p = joint_parent[tid]
1359
-
1360
- X_pj = joint_X_p[tid]
1361
- X_cj = joint_X_c[tid]
1362
-
1363
- X_wp = X_pj
1364
- m_inv_p = 0.0
1365
- I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1366
- pose_p = X_pj
1367
- com_p = wp.vec3(0.0)
1368
- # parent transform and moment arm
1369
- if id_p >= 0:
1370
- pose_p = body_q[id_p]
1371
- X_wp = pose_p * X_wp
1372
- com_p = body_com[id_p]
1373
- m_inv_p = body_inv_m[id_p]
1374
- I_inv_p = body_inv_I[id_p]
1375
- r_p = wp.transform_get_translation(X_wp) - wp.transform_point(pose_p, com_p)
1376
-
1377
- # child transform and moment arm
1378
- pose_c = body_q[id_c]
1379
- X_wc = pose_c * X_cj
1380
- com_c = body_com[id_c]
1381
- m_inv_c = body_inv_m[id_c]
1382
- I_inv_c = body_inv_I[id_c]
1383
- r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
1384
-
1385
- if m_inv_p == 0.0 and m_inv_c == 0.0:
1386
- # connection between two immovable bodies
1387
- return
1388
-
1389
- # accumulate constraint deltas
1390
- lin_delta_p = wp.vec3(0.0)
1391
- ang_delta_p = wp.vec3(0.0)
1392
- lin_delta_c = wp.vec3(0.0)
1393
- ang_delta_c = wp.vec3(0.0)
1394
-
1395
- # rel_pose = wp.transform_inverse(X_wp) * X_wc
1396
- # rel_p = wp.transform_get_translation(rel_pose)
1397
-
1398
- # joint connection points
1399
- # x_p = wp.transform_get_translation(X_wp)
1400
- x_c = wp.transform_get_translation(X_wc)
1401
-
1402
- # linear_compliance = joint_linear_compliance[tid]
1403
- angular_compliance = joint_angular_compliance[tid]
1404
- damping = 0.0
1405
-
1406
- axis_start = joint_axis_start[tid]
1407
- # mode = joint_axis_mode[axis_start]
1408
-
1409
- # local joint rotations
1410
- q_p = wp.transform_get_rotation(X_wp)
1411
- q_c = wp.transform_get_rotation(X_wc)
1412
- inertial_q_p = wp.transform_get_rotation(pose_p)
1413
- inertial_q_c = wp.transform_get_rotation(pose_c)
1414
-
1415
- # joint properties (for 1D joints)
1416
- axis = joint_axis[axis_start]
1417
-
1418
- if type == wp.sim.JOINT_FIXED:
1419
- limit_lower = 0.0
1420
- limit_upper = 0.0
1421
- else:
1422
- limit_lower = joint_limit_lower[axis_start]
1423
- limit_upper = joint_limit_upper[axis_start]
1424
-
1425
- # linear_alpha_tilde = linear_compliance / dt / dt
1426
- angular_alpha_tilde = angular_compliance / dt / dt
1427
-
1428
- # prevent division by zero
1429
- # linear_alpha_tilde = wp.max(linear_alpha_tilde, 1e-6)
1430
- # angular_alpha_tilde = wp.max(angular_alpha_tilde, 1e-6)
1431
-
1432
- # accumulate constraint deltas
1433
- lin_delta_p = wp.vec3(0.0)
1434
- ang_delta_p = wp.vec3(0.0)
1435
- lin_delta_c = wp.vec3(0.0)
1436
- ang_delta_c = wp.vec3(0.0)
1437
-
1438
- # handle angular constraints
1439
- if type == wp.sim.JOINT_REVOLUTE:
1440
- # align joint axes
1441
- a_p = wp.quat_rotate(q_p, axis)
1442
- a_c = wp.quat_rotate(q_c, axis)
1443
- # Eq. 20
1444
- corr = wp.cross(a_p, a_c)
1445
- ncorr = wp.normalize(corr)
1446
-
1447
- angular_relaxation = 0.2
1448
- # angular_correction(
1449
- # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1450
- # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1451
- lambda_n = compute_angular_correction_3d(
1452
- corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1453
- )
1454
- lambda_n *= angular_relaxation
1455
- ang_delta_p -= lambda_n * ncorr
1456
- ang_delta_c += lambda_n * ncorr
1457
-
1458
- # limit joint angles (Alg. 3)
1459
- pi = 3.14159265359
1460
- two_pi = 2.0 * pi
1461
- if limit_lower > -two_pi or limit_upper < two_pi:
1462
- # find a perpendicular vector to joint axis
1463
- a = axis
1464
- # https://math.stackexchange.com/a/3582461
1465
- g = wp.sign(a[2])
1466
- h = a[2] + g
1467
- b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1468
- c = wp.normalize(wp.cross(a, b))
1469
- # b = c # TODO verify
1470
-
1471
- # joint axis
1472
- n = wp.quat_rotate(q_p, a)
1473
- # the axes n1 and n2 are aligned with the two bodies
1474
- n1 = wp.quat_rotate(q_p, b)
1475
- n2 = wp.quat_rotate(q_c, b)
1476
-
1477
- phi = wp.asin(wp.dot(wp.cross(n1, n2), n))
1478
- # print("phi")
1479
- # print(phi)
1480
- if wp.dot(n1, n2) < 0.0:
1481
- phi = pi - phi
1482
- if phi > pi:
1483
- phi -= two_pi
1484
- if phi < -pi:
1485
- phi += two_pi
1486
- if phi < limit_lower or phi > limit_upper:
1487
- phi = wp.clamp(phi, limit_lower, limit_upper)
1488
- # print("clamped phi")
1489
- # print(phi)
1490
- # rot = wp.quat(phi, n[0], n[1], n[2])
1491
- # rot = wp.quat(n, phi)
1492
- rot = wp.quat_from_axis_angle(n, phi)
1493
- n1 = wp.quat_rotate(rot, n1)
1494
- corr = wp.cross(n1, n2)
1495
- # print("corr")
1496
- # print(corr)
1497
- # TODO expose
1498
- # angular_alpha_tilde = 0.0001 / dt / dt
1499
- # angular_relaxation = 0.5
1500
- # TODO fix this constraint
1501
- # angular_correction(
1502
- # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1503
- # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1504
- lambda_n = compute_angular_correction_3d(
1505
- corr,
1506
- inertial_q_p,
1507
- inertial_q_c,
1508
- m_inv_p,
1509
- m_inv_c,
1510
- I_inv_p,
1511
- I_inv_c,
1512
- angular_alpha_tilde,
1513
- damping,
1514
- dt,
1515
- )
1516
- lambda_n *= angular_relaxation
1517
- ncorr = wp.normalize(corr)
1518
- ang_delta_p -= lambda_n * ncorr
1519
- ang_delta_c += lambda_n * ncorr
1520
-
1521
- # handle joint targets
1522
- target_ke = joint_target_ke[axis_start]
1523
- # target_kd = joint_target_kd[axis_start]
1524
- target = joint_target[axis_start]
1525
- if target_ke > 0.0:
1526
- # find a perpendicular vector to joint axis
1527
- a = axis
1528
- # https://math.stackexchange.com/a/3582461
1529
- g = wp.sign(a[2])
1530
- h = a[2] + g
1531
- b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1532
- c = wp.normalize(wp.cross(a, b))
1533
- b = c
1534
-
1535
- q = wp.quat_from_axis_angle(a_p, target)
1536
- b_target = wp.quat_rotate(q, wp.quat_rotate(q_p, b))
1537
- b2 = wp.quat_rotate(q_c, b)
1538
- # Eq. 21
1539
- d_target = wp.cross(b_target, b2)
1540
-
1541
- target_compliance = 1.0 / target_ke # / dt / dt
1542
- # angular_correction(
1543
- # d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1544
- # target_compliance, angular_relaxation, deltas, id_p, id_c)
1545
- lambda_n = compute_angular_correction_3d(
1546
- d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, target_compliance, damping, dt
1547
- )
1548
- lambda_n *= angular_relaxation
1549
- ncorr = wp.normalize(d_target)
1550
- # TODO fix
1551
- ang_delta_p -= lambda_n * ncorr
1552
- ang_delta_c += lambda_n * ncorr
1553
-
1554
- if (type == wp.sim.JOINT_FIXED) or (type == wp.sim.JOINT_PRISMATIC):
1555
- # align the mutual orientations of the two bodies
1556
- # Eq. 18-19
1557
- q = q_p * wp.quat_inverse(q_c)
1558
- corr = -2.0 * wp.vec3(q[0], q[1], q[2])
1559
- # angular_correction(
1560
- # -corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1561
- # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1562
- lambda_n = compute_angular_correction_3d(
1563
- corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1564
- )
1565
- lambda_n *= angular_relaxation
1566
- ncorr = wp.normalize(corr)
1567
- ang_delta_p -= lambda_n * ncorr
1568
- ang_delta_c += lambda_n * ncorr
1569
-
1570
- # handle positional constraints
1571
-
1572
- # joint connection points
1573
- x_p = wp.transform_get_translation(X_wp)
1574
- x_c = wp.transform_get_translation(X_wc)
1575
-
1576
- # compute error between the joint attachment points on both bodies
1577
- # delta x is the difference of point r_2 minus point r_1 (Fig. 3)
1578
- dx = x_c - x_p
1579
-
1580
- # rotate the error vector into the joint frame
1581
- q_dx = q_p
1582
- # q_dx = q_c
1583
- # q_dx = wp.transform_get_rotation(pose_p)
1584
- dx = wp.quat_rotate_inv(q_dx, dx)
1585
-
1586
- lower_pos_limits = wp.vec3(0.0)
1587
- upper_pos_limits = wp.vec3(0.0)
1588
- if type == wp.sim.JOINT_PRISMATIC:
1589
- lower_pos_limits = axis * limit_lower
1590
- upper_pos_limits = axis * limit_upper
1591
-
1592
- # compute linear constraint violations
1593
- corr = wp.vec3(0.0)
1594
- zero = wp.vec3(0.0)
1595
- corr -= vec_leaky_min(zero, upper_pos_limits - dx)
1596
- corr -= vec_leaky_max(zero, lower_pos_limits - dx)
1597
-
1598
- # if (type == wp.sim.JOINT_PRISMATIC):
1599
- # if mode == JOINT_MODE_TARGET_POSITION:
1600
- # target = wp.clamp(target, limit_lower, limit_upper)
1601
- # if target_ke > 0.0:
1602
- # err = dx - target * axis
1603
- # compliance = 1.0 / target_ke
1604
- # damping = axis_damping[dim]
1605
- # elif mode == JOINT_MODE_TARGET_VELOCITY:
1606
- # if target_ke > 0.0:
1607
- # err = (derr - target) * dt
1608
- # compliance = 1.0 / target_ke
1609
- # damping = axis_damping[dim]
1610
-
1611
- # rotate correction vector into world frame
1612
- corr = wp.quat_rotate(q_dx, corr)
1613
-
1614
- lambda_in = 0.0
1615
- linear_alpha = joint_linear_compliance[tid]
1616
- lambda_n = compute_linear_correction_3d(
1617
- corr, r_p, r_c, pose_p, pose_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, lambda_in, linear_alpha, damping, dt
1618
- )
1619
- lambda_n *= linear_relaxation
1620
- n = wp.normalize(corr)
1621
-
1622
- lin_delta_p -= n * lambda_n
1623
- lin_delta_c += n * lambda_n
1624
- ang_delta_p -= wp.cross(r_p, n) * lambda_n
1625
- ang_delta_c += wp.cross(r_c, n) * lambda_n
1626
-
1627
- if id_p >= 0:
1628
- wp.atomic_add(deltas, id_p, wp.spatial_vector(ang_delta_p, lin_delta_p))
1629
- if id_c >= 0:
1630
- wp.atomic_add(deltas, id_c, wp.spatial_vector(ang_delta_c, lin_delta_c))
1631
-
1632
-
1633
- @wp.kernel
1634
- def solve_body_joints(
1635
- body_q: wp.array(dtype=wp.transform),
1636
- body_qd: wp.array(dtype=wp.spatial_vector),
1637
- body_com: wp.array(dtype=wp.vec3),
1638
- body_inv_m: wp.array(dtype=float),
1639
- body_inv_I: wp.array(dtype=wp.mat33),
1640
- joint_type: wp.array(dtype=int),
1641
- joint_enabled: wp.array(dtype=int),
1642
- joint_parent: wp.array(dtype=int),
1643
- joint_child: wp.array(dtype=int),
1644
- joint_X_p: wp.array(dtype=wp.transform),
1645
- joint_X_c: wp.array(dtype=wp.transform),
1646
- joint_limit_lower: wp.array(dtype=float),
1647
- joint_limit_upper: wp.array(dtype=float),
1648
- joint_axis_start: wp.array(dtype=int),
1649
- joint_axis_dim: wp.array(dtype=int, ndim=2),
1650
- joint_axis_mode: wp.array(dtype=int),
1651
- joint_axis: wp.array(dtype=wp.vec3),
1652
- joint_act: wp.array(dtype=float),
1653
- joint_target_ke: wp.array(dtype=float),
1654
- joint_target_kd: wp.array(dtype=float),
1655
- joint_linear_compliance: wp.array(dtype=float),
1656
- joint_angular_compliance: wp.array(dtype=float),
1657
- angular_relaxation: float,
1658
- linear_relaxation: float,
1659
- dt: float,
1660
- deltas: wp.array(dtype=wp.spatial_vector),
1661
- ):
1662
- tid = wp.tid()
1663
- type = joint_type[tid]
1664
-
1665
- if joint_enabled[tid] == 0:
1666
- return
1667
- if type == wp.sim.JOINT_FREE:
1668
- return
1669
- # if type == wp.sim.JOINT_FIXED:
1670
- # return
1671
- # if type == wp.sim.JOINT_REVOLUTE:
1672
- # return
1673
- # if type == wp.sim.JOINT_PRISMATIC:
1674
- # return
1675
- # if type == wp.sim.JOINT_BALL:
1676
- # return
1677
-
1678
- # rigid body indices of the child and parent
1679
- id_c = joint_child[tid]
1680
- id_p = joint_parent[tid]
1681
-
1682
- X_pj = joint_X_p[tid]
1683
- X_cj = joint_X_c[tid]
1684
-
1685
- X_wp = X_pj
1686
- m_inv_p = 0.0
1687
- I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1688
- pose_p = X_pj
1689
- com_p = wp.vec3(0.0)
1690
- vel_p = wp.vec3(0.0)
1691
- omega_p = wp.vec3(0.0)
1692
- # parent transform and moment arm
1693
- if id_p >= 0:
1694
- pose_p = body_q[id_p]
1695
- X_wp = pose_p * X_wp
1696
- com_p = body_com[id_p]
1697
- m_inv_p = body_inv_m[id_p]
1698
- I_inv_p = body_inv_I[id_p]
1699
- vel_p = wp.spatial_bottom(body_qd[id_p])
1700
- omega_p = wp.spatial_top(body_qd[id_p])
1701
-
1702
- # child transform and moment arm
1703
- pose_c = body_q[id_c]
1704
- X_wc = pose_c * X_cj
1705
- com_c = body_com[id_c]
1706
- m_inv_c = body_inv_m[id_c]
1707
- I_inv_c = body_inv_I[id_c]
1708
- vel_c = wp.spatial_bottom(body_qd[id_c])
1709
- omega_c = wp.spatial_top(body_qd[id_c])
1710
-
1711
- if m_inv_p == 0.0 and m_inv_c == 0.0:
1712
- # connection between two immovable bodies
1713
- return
1714
-
1715
- # accumulate constraint deltas
1716
- lin_delta_p = wp.vec3(0.0)
1717
- ang_delta_p = wp.vec3(0.0)
1718
- lin_delta_c = wp.vec3(0.0)
1719
- ang_delta_c = wp.vec3(0.0)
1720
-
1721
- rel_pose = wp.transform_inverse(X_wp) * X_wc
1722
- rel_p = wp.transform_get_translation(rel_pose)
1723
-
1724
- # joint connection points
1725
- # x_p = wp.transform_get_translation(X_wp)
1726
- x_c = wp.transform_get_translation(X_wc)
1727
-
1728
- linear_compliance = joint_linear_compliance[tid]
1729
- angular_compliance = joint_angular_compliance[tid]
1730
-
1731
- axis_start = joint_axis_start[tid]
1732
- lin_axis_count = joint_axis_dim[tid, 0]
1733
- ang_axis_count = joint_axis_dim[tid, 1]
1734
-
1735
- world_com_p = wp.transform_point(pose_p, com_p)
1736
- world_com_c = wp.transform_point(pose_c, com_c)
1737
-
1738
- # handle positional constraints
1739
- if type == wp.sim.JOINT_DISTANCE:
1740
- r_p = wp.transform_get_translation(X_wp) - world_com_p
1741
- r_c = wp.transform_get_translation(X_wc) - world_com_c
1742
- lower = joint_limit_lower[axis_start]
1743
- upper = joint_limit_upper[axis_start]
1744
- if lower < 0.0 and upper < 0.0:
1745
- # no limits
1746
- return
1747
- d = wp.length(rel_p)
1748
- err = 0.0
1749
- if lower >= 0.0 and d < lower:
1750
- err = d - lower
1751
- # use a more descriptive direction vector for the constraint
1752
- # in case the joint parent and child anchors are very close
1753
- rel_p = err * wp.normalize(world_com_c - world_com_p)
1754
- elif upper >= 0.0 and d > upper:
1755
- err = d - upper
1756
-
1757
- if wp.abs(err) > 1e-9:
1758
- # compute gradients
1759
- linear_c = rel_p
1760
- linear_p = -linear_c
1761
- r_c = x_c - world_com_c
1762
- angular_p = -wp.cross(r_p, linear_c)
1763
- angular_c = wp.cross(r_c, linear_c)
1764
- # constraint time derivative
1765
- derr = (
1766
- wp.dot(linear_p, vel_p)
1767
- + wp.dot(linear_c, vel_c)
1768
- + wp.dot(angular_p, omega_p)
1769
- + wp.dot(angular_c, omega_c)
1770
- )
1771
- lambda_in = 0.0
1772
- compliance = linear_compliance
1773
- ke = joint_target_ke[axis_start]
1774
- if ke > 0.0:
1775
- compliance = 1.0 / ke
1776
- damping = joint_target_kd[axis_start]
1777
- d_lambda = compute_positional_correction(
1778
- err,
1779
- derr,
1780
- pose_p,
1781
- pose_c,
1782
- m_inv_p,
1783
- m_inv_c,
1784
- I_inv_p,
1785
- I_inv_c,
1786
- linear_p,
1787
- linear_c,
1788
- angular_p,
1789
- angular_c,
1790
- lambda_in,
1791
- compliance,
1792
- damping,
1793
- dt,
1794
- )
1795
-
1796
- lin_delta_p += linear_p * (d_lambda * linear_relaxation)
1797
- ang_delta_p += angular_p * (d_lambda * angular_relaxation)
1798
- lin_delta_c += linear_c * (d_lambda * linear_relaxation)
1799
- ang_delta_c += angular_c * (d_lambda * angular_relaxation)
1800
-
1801
- else:
1802
- # compute joint target, stiffness, damping
1803
- ke_sum = float(0.0)
1804
- axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1805
- axis_mode = wp.vec3i(0, 0, 0)
1806
- axis_target_ke_kd = wp.mat33(0.0)
1807
- # avoid a for loop here since local variables would need to be modified which is not yet differentiable
1808
- if lin_axis_count > 0:
1809
- axis = joint_axis[axis_start]
1810
- lo_temp = axis * joint_limit_lower[axis_start]
1811
- up_temp = axis * joint_limit_upper[axis_start]
1812
- axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
1813
- mode = joint_axis_mode[axis_start]
1814
- if mode != JOINT_MODE_FORCE: # position or velocity target
1815
- ke = joint_target_ke[axis_start]
1816
- kd = joint_target_kd[axis_start]
1817
- target = joint_act[axis_start]
1818
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1819
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1820
- ke_sum += ke
1821
- if lin_axis_count > 1:
1822
- axis_idx = axis_start + 1
1823
- axis = joint_axis[axis_idx]
1824
- lower = joint_limit_lower[axis_idx]
1825
- upper = joint_limit_upper[axis_idx]
1826
- axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1827
- mode = joint_axis_mode[axis_idx]
1828
- if mode != JOINT_MODE_FORCE: # position or velocity target
1829
- ke = joint_target_ke[axis_idx]
1830
- kd = joint_target_kd[axis_idx]
1831
- target = joint_act[axis_idx]
1832
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1833
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1834
- ke_sum += ke
1835
- if lin_axis_count > 2:
1836
- axis_idx = axis_start + 2
1837
- axis = joint_axis[axis_idx]
1838
- lower = joint_limit_lower[axis_idx]
1839
- upper = joint_limit_upper[axis_idx]
1840
- axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1841
- mode = joint_axis_mode[axis_idx]
1842
- if mode != JOINT_MODE_FORCE: # position or velocity target
1843
- ke = joint_target_ke[axis_idx]
1844
- kd = joint_target_kd[axis_idx]
1845
- target = joint_act[axis_idx]
1846
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1847
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1848
- ke_sum += ke
1849
-
1850
- axis_target = axis_target_ke_kd[0]
1851
- axis_stiffness = axis_target_ke_kd[1]
1852
- axis_damping = axis_target_ke_kd[2]
1853
- if ke_sum > 0.0:
1854
- axis_target /= ke_sum
1855
- axis_limits_lower = wp.spatial_top(axis_limits)
1856
- axis_limits_upper = wp.spatial_bottom(axis_limits)
1857
-
1858
- frame_p = wp.quat_to_matrix(wp.transform_get_rotation(X_wp))
1859
- # note that x_c appearing in both is correct
1860
- r_p = x_c - world_com_p
1861
- r_c = x_c - wp.transform_point(pose_c, com_c)
1862
-
1863
- # for loop will be unrolled, so we can modify local variables
1864
- for dim in range(3):
1865
- e = rel_p[dim]
1866
- mode = axis_mode[dim]
1867
-
1868
- # compute gradients
1869
- linear_c = wp.vec3(frame_p[0, dim], frame_p[1, dim], frame_p[2, dim])
1870
- linear_p = -linear_c
1871
- angular_p = -wp.cross(r_p, linear_c)
1872
- angular_c = wp.cross(r_c, linear_c)
1873
- # constraint time derivative
1874
- derr = (
1875
- wp.dot(linear_p, vel_p)
1876
- + wp.dot(linear_c, vel_c)
1877
- + wp.dot(angular_p, omega_p)
1878
- + wp.dot(angular_c, omega_c)
1879
- )
1880
-
1881
- err = 0.0
1882
- compliance = linear_compliance
1883
- damping = 0.0
1884
- # consider joint limits irrespective of axis mode
1885
- lower = axis_limits_lower[dim]
1886
- upper = axis_limits_upper[dim]
1887
- if e < lower:
1888
- err = e - lower
1889
- elif e > upper:
1890
- err = e - upper
1891
- else:
1892
- target = axis_target[dim]
1893
- if mode == JOINT_MODE_TARGET_POSITION:
1894
- target = wp.clamp(target, lower, upper)
1895
- if axis_stiffness[dim] > 0.0:
1896
- err = e - target
1897
- compliance = 1.0 / axis_stiffness[dim]
1898
- damping = axis_damping[dim]
1899
- elif mode == JOINT_MODE_TARGET_VELOCITY:
1900
- if axis_stiffness[dim] > 0.0:
1901
- err = (derr - target) * dt
1902
- compliance = 1.0 / axis_stiffness[dim]
1903
- damping = axis_damping[dim]
1904
- derr = 0.0
1905
-
1906
- if wp.abs(err) > 1e-9:
1907
- lambda_in = 0.0
1908
- d_lambda = compute_positional_correction(
1909
- err,
1910
- derr,
1911
- pose_p,
1912
- pose_c,
1913
- m_inv_p,
1914
- m_inv_c,
1915
- I_inv_p,
1916
- I_inv_c,
1917
- linear_p,
1918
- linear_c,
1919
- angular_p,
1920
- angular_c,
1921
- lambda_in,
1922
- compliance,
1923
- damping,
1924
- dt,
1925
- )
1926
-
1927
- lin_delta_p += linear_p * (d_lambda * linear_relaxation)
1928
- ang_delta_p += angular_p * (d_lambda * angular_relaxation)
1929
- lin_delta_c += linear_c * (d_lambda * linear_relaxation)
1930
- ang_delta_c += angular_c * (d_lambda * angular_relaxation)
1931
-
1932
- if (
1933
- type == wp.sim.JOINT_FIXED
1934
- or type == wp.sim.JOINT_PRISMATIC
1935
- or type == wp.sim.JOINT_REVOLUTE
1936
- or type == wp.sim.JOINT_UNIVERSAL
1937
- or type == wp.sim.JOINT_COMPOUND
1938
- or type == wp.sim.JOINT_D6
1939
- ):
1940
- # handle angular constraints
1941
-
1942
- # local joint rotations
1943
- q_p = wp.transform_get_rotation(X_wp)
1944
- q_c = wp.transform_get_rotation(X_wc)
1945
-
1946
- # make quats lie in same hemisphere
1947
- if wp.dot(q_p, q_c) < 0.0:
1948
- q_c *= -1.0
1949
-
1950
- rel_q = wp.quat_inverse(q_p) * q_c
1951
-
1952
- qtwist = wp.normalize(wp.quat(rel_q[0], 0.0, 0.0, rel_q[3]))
1953
- qswing = rel_q * wp.quat_inverse(qtwist)
1954
-
1955
- # decompose to a compound rotation each axis
1956
- s = wp.sqrt(rel_q[0] * rel_q[0] + rel_q[3] * rel_q[3])
1957
- invs = 1.0 / s
1958
- invscube = invs * invs * invs
1959
-
1960
- # handle axis-angle joints
1961
-
1962
- # rescale twist from quaternion space to angular
1963
- err_0 = 2.0 * wp.asin(wp.clamp(qtwist[0], -1.0, 1.0))
1964
- err_1 = qswing[1]
1965
- err_2 = qswing[2]
1966
- # analytic gradients of swing-twist decomposition
1967
- grad_0 = wp.quat(invs - rel_q[0] * rel_q[0] * invscube, 0.0, 0.0, -(rel_q[3] * rel_q[0]) * invscube)
1968
- grad_1 = wp.quat(
1969
- -rel_q[3] * (rel_q[3] * rel_q[2] + rel_q[0] * rel_q[1]) * invscube,
1970
- rel_q[3] * invs,
1971
- -rel_q[0] * invs,
1972
- rel_q[0] * (rel_q[3] * rel_q[2] + rel_q[0] * rel_q[1]) * invscube,
1973
- )
1974
- grad_2 = wp.quat(
1975
- rel_q[3] * (rel_q[3] * rel_q[1] - rel_q[0] * rel_q[2]) * invscube,
1976
- rel_q[0] * invs,
1977
- rel_q[3] * invs,
1978
- rel_q[0] * (rel_q[2] * rel_q[0] - rel_q[3] * rel_q[1]) * invscube,
1979
- )
1980
- grad_0 *= 2.0 / wp.abs(qtwist[3])
1981
- # grad_0 *= 2.0 / wp.sqrt(1.0-qtwist[0]*qtwist[0]) # derivative of asin(x) = 1/sqrt(1-x^2)
1982
-
1983
- # rescale swing
1984
- swing_sq = qswing[3] * qswing[3]
1985
- # if swing axis magnitude close to zero vector, just treat in quaternion space
1986
- angularEps = 1.0e-4
1987
- if swing_sq + angularEps < 1.0:
1988
- d = wp.sqrt(1.0 - qswing[3] * qswing[3])
1989
- theta = 2.0 * wp.acos(wp.clamp(qswing[3], -1.0, 1.0))
1990
- scale = theta / d
1991
-
1992
- err_1 *= scale
1993
- err_2 *= scale
1994
-
1995
- grad_1 *= scale
1996
- grad_2 *= scale
1997
-
1998
- errs = wp.vec3(err_0, err_1, err_2)
1999
- grad_x = wp.vec3(grad_0[0], grad_1[0], grad_2[0])
2000
- grad_y = wp.vec3(grad_0[1], grad_1[1], grad_2[1])
2001
- grad_z = wp.vec3(grad_0[2], grad_1[2], grad_2[2])
2002
- grad_w = wp.vec3(grad_0[3], grad_1[3], grad_2[3])
2003
-
2004
- # compute joint target, stiffness, damping
2005
- ke_sum = float(0.0)
2006
- axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2007
- axis_mode = wp.vec3i(0, 0, 0)
2008
- axis_target_ke_kd = wp.mat33(0.0)
2009
- # avoid a for loop here since local variables would need to be modified which is not yet differentiable
2010
- if ang_axis_count > 0:
2011
- axis_idx = axis_start + lin_axis_count
2012
- axis = joint_axis[axis_idx]
2013
- lo_temp = axis * joint_limit_lower[axis_idx]
2014
- up_temp = axis * joint_limit_upper[axis_idx]
2015
- axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
2016
- mode = joint_axis_mode[axis_idx]
2017
- if mode != JOINT_MODE_FORCE: # position or velocity target
2018
- ke = joint_target_ke[axis_idx]
2019
- kd = joint_target_kd[axis_idx]
2020
- target = joint_act[axis_idx]
2021
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2022
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2023
- ke_sum += ke
2024
- if ang_axis_count > 1:
2025
- axis_idx = axis_start + lin_axis_count + 1
2026
- axis = joint_axis[axis_idx]
2027
- lower = joint_limit_lower[axis_idx]
2028
- upper = joint_limit_upper[axis_idx]
2029
- axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
2030
- mode = joint_axis_mode[axis_idx]
2031
- if mode != JOINT_MODE_FORCE: # position or velocity target
2032
- ke = joint_target_ke[axis_idx]
2033
- kd = joint_target_kd[axis_idx]
2034
- target = joint_act[axis_idx]
2035
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2036
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2037
- ke_sum += ke
2038
- if ang_axis_count > 2:
2039
- axis_idx = axis_start + lin_axis_count + 2
2040
- axis = joint_axis[axis_idx]
2041
- lower = joint_limit_lower[axis_idx]
2042
- upper = joint_limit_upper[axis_idx]
2043
- axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
2044
- mode = joint_axis_mode[axis_idx]
2045
- if mode != JOINT_MODE_FORCE: # position or velocity target
2046
- ke = joint_target_ke[axis_idx]
2047
- kd = joint_target_kd[axis_idx]
2048
- target = joint_act[axis_idx]
2049
- axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2050
- axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2051
- ke_sum += ke
2052
-
2053
- axis_target = axis_target_ke_kd[0]
2054
- axis_stiffness = axis_target_ke_kd[1]
2055
- axis_damping = axis_target_ke_kd[2]
2056
- if ke_sum > 0.0:
2057
- axis_target /= ke_sum
2058
- axis_limits_lower = wp.spatial_top(axis_limits)
2059
- axis_limits_upper = wp.spatial_bottom(axis_limits)
2060
-
2061
- # if type == wp.sim.JOINT_D6:
2062
- # wp.printf("axis_target: %f %f %f\t axis_stiffness: %f %f %f\t axis_damping: %f %f %f\t axis_limits_lower: %f %f %f \t axis_limits_upper: %f %f %f\n",
2063
- # axis_target[0], axis_target[1], axis_target[2],
2064
- # axis_stiffness[0], axis_stiffness[1], axis_stiffness[2],
2065
- # axis_damping[0], axis_damping[1], axis_damping[2],
2066
- # axis_limits_lower[0], axis_limits_lower[1], axis_limits_lower[2],
2067
- # axis_limits_upper[0], axis_limits_upper[1], axis_limits_upper[2])
2068
- # # wp.printf("wp.sqrt(1.0-qtwist[0]*qtwist[0]) = %f\n", wp.sqrt(1.0-qtwist[0]*qtwist[0]))
2069
-
2070
- for dim in range(3):
2071
- e = errs[dim]
2072
- mode = axis_mode[dim]
2073
-
2074
- # analytic gradients of swing-twist decomposition
2075
- grad = wp.quat(grad_x[dim], grad_y[dim], grad_z[dim], grad_w[dim])
2076
-
2077
- quat_c = 0.5 * q_p * grad * wp.quat_inverse(q_c)
2078
- angular_c = wp.vec3(quat_c[0], quat_c[1], quat_c[2])
2079
- angular_p = -angular_c
2080
- # time derivative of the constraint
2081
- derr = wp.dot(angular_p, omega_p) + wp.dot(angular_c, omega_c)
2082
-
2083
- err = 0.0
2084
- compliance = angular_compliance
2085
- damping = 0.0
2086
-
2087
- # consider joint limits irrespective of mode
2088
- lower = axis_limits_lower[dim]
2089
- upper = axis_limits_upper[dim]
2090
- if e < lower:
2091
- err = e - lower
2092
- elif e > upper:
2093
- err = e - upper
2094
- else:
2095
- target = axis_target[dim]
2096
- if mode == JOINT_MODE_TARGET_POSITION:
2097
- target = wp.clamp(target, lower, upper)
2098
- if axis_stiffness[dim] > 0.0:
2099
- err = e - target
2100
- compliance = 1.0 / axis_stiffness[dim]
2101
- damping = axis_damping[dim]
2102
- elif mode == JOINT_MODE_TARGET_VELOCITY:
2103
- if axis_stiffness[dim] > 0.0:
2104
- err = (derr - target) * dt
2105
- compliance = 1.0 / axis_stiffness[dim]
2106
- damping = axis_damping[dim]
2107
- derr = 0.0
2108
-
2109
- d_lambda = (
2110
- compute_angular_correction(
2111
- err, derr, pose_p, pose_c, I_inv_p, I_inv_c, angular_p, angular_c, 0.0, compliance, damping, dt
2112
- )
2113
- * angular_relaxation
2114
- )
2115
-
2116
- # update deltas
2117
- ang_delta_p += angular_p * d_lambda
2118
- ang_delta_c += angular_c * d_lambda
2119
-
2120
- if id_p >= 0:
2121
- wp.atomic_add(deltas, id_p, wp.spatial_vector(ang_delta_p, lin_delta_p))
2122
- if id_c >= 0:
2123
- wp.atomic_add(deltas, id_c, wp.spatial_vector(ang_delta_c, lin_delta_c))
2124
-
2125
-
2126
- @wp.func
2127
- def compute_contact_constraint_delta(
2128
- err: float,
2129
- tf_a: wp.transform,
2130
- tf_b: wp.transform,
2131
- m_inv_a: float,
2132
- m_inv_b: float,
2133
- I_inv_a: wp.mat33,
2134
- I_inv_b: wp.mat33,
2135
- linear_a: wp.vec3,
2136
- linear_b: wp.vec3,
2137
- angular_a: wp.vec3,
2138
- angular_b: wp.vec3,
2139
- relaxation: float,
2140
- dt: float,
2141
- ) -> float:
2142
- denom = 0.0
2143
- denom += wp.length_sq(linear_a) * m_inv_a
2144
- denom += wp.length_sq(linear_b) * m_inv_b
2145
-
2146
- q1 = wp.transform_get_rotation(tf_a)
2147
- q2 = wp.transform_get_rotation(tf_b)
2148
-
2149
- # Eq. 2-3 (make sure to project into the frame of the body)
2150
- rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2151
- rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2152
-
2153
- denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2154
- denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2155
-
2156
- delta_lambda = -err
2157
- if denom > 0.0:
2158
- delta_lambda /= dt * denom
2159
-
2160
- return delta_lambda * relaxation
2161
-
2162
-
2163
- @wp.func
2164
- def compute_positional_correction(
2165
- err: float,
2166
- derr: float,
2167
- tf_a: wp.transform,
2168
- tf_b: wp.transform,
2169
- m_inv_a: float,
2170
- m_inv_b: float,
2171
- I_inv_a: wp.mat33,
2172
- I_inv_b: wp.mat33,
2173
- linear_a: wp.vec3,
2174
- linear_b: wp.vec3,
2175
- angular_a: wp.vec3,
2176
- angular_b: wp.vec3,
2177
- lambda_in: float,
2178
- compliance: float,
2179
- damping: float,
2180
- dt: float,
2181
- ) -> float:
2182
- denom = 0.0
2183
- denom += wp.length_sq(linear_a) * m_inv_a
2184
- denom += wp.length_sq(linear_b) * m_inv_b
2185
-
2186
- q1 = wp.transform_get_rotation(tf_a)
2187
- q2 = wp.transform_get_rotation(tf_b)
2188
-
2189
- # Eq. 2-3 (make sure to project into the frame of the body)
2190
- rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2191
- rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2192
-
2193
- denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2194
- denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2195
-
2196
- alpha = compliance
2197
- gamma = compliance * damping
2198
-
2199
- delta_lambda = -(err + alpha * lambda_in + gamma * derr)
2200
- if denom + alpha > 0.0:
2201
- delta_lambda /= (dt + gamma) * denom + alpha / dt
2202
-
2203
- return delta_lambda
2204
-
2205
-
2206
- @wp.func
2207
- def compute_angular_correction(
2208
- err: float,
2209
- derr: float,
2210
- tf_a: wp.transform,
2211
- tf_b: wp.transform,
2212
- I_inv_a: wp.mat33,
2213
- I_inv_b: wp.mat33,
2214
- angular_a: wp.vec3,
2215
- angular_b: wp.vec3,
2216
- lambda_in: float,
2217
- compliance: float,
2218
- damping: float,
2219
- dt: float,
2220
- ) -> float:
2221
- denom = 0.0
2222
-
2223
- q1 = wp.transform_get_rotation(tf_a)
2224
- q2 = wp.transform_get_rotation(tf_b)
2225
-
2226
- # Eq. 2-3 (make sure to project into the frame of the body)
2227
- rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2228
- rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2229
-
2230
- denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2231
- denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2232
-
2233
- alpha = compliance
2234
- gamma = compliance * damping
2235
-
2236
- delta_lambda = -(err + alpha * lambda_in + gamma * derr)
2237
- if denom + alpha > 0.0:
2238
- delta_lambda /= (dt + gamma) * denom + alpha / dt
2239
-
2240
- return delta_lambda
2241
-
2242
-
2243
- @wp.kernel
2244
- def solve_body_contact_positions(
2245
- body_q: wp.array(dtype=wp.transform),
2246
- body_qd: wp.array(dtype=wp.spatial_vector),
2247
- body_com: wp.array(dtype=wp.vec3),
2248
- body_m_inv: wp.array(dtype=float),
2249
- body_I_inv: wp.array(dtype=wp.mat33),
2250
- shape_body: wp.array(dtype=int),
2251
- contact_count: wp.array(dtype=int),
2252
- contact_point0: wp.array(dtype=wp.vec3),
2253
- contact_point1: wp.array(dtype=wp.vec3),
2254
- contact_offset0: wp.array(dtype=wp.vec3),
2255
- contact_offset1: wp.array(dtype=wp.vec3),
2256
- contact_normal: wp.array(dtype=wp.vec3),
2257
- contact_thickness: wp.array(dtype=float),
2258
- contact_shape0: wp.array(dtype=int),
2259
- contact_shape1: wp.array(dtype=int),
2260
- shape_materials: ModelShapeMaterials,
2261
- relaxation: float,
2262
- dt: float,
2263
- contact_torsional_friction: float,
2264
- contact_rolling_friction: float,
2265
- # outputs
2266
- deltas: wp.array(dtype=wp.spatial_vector),
2267
- contact_inv_weight: wp.array(dtype=float),
2268
- ):
2269
- tid = wp.tid()
2270
-
2271
- count = contact_count[0]
2272
- if tid >= count:
2273
- return
2274
-
2275
- shape_a = contact_shape0[tid]
2276
- shape_b = contact_shape1[tid]
2277
- if shape_a == shape_b:
2278
- return
2279
- body_a = -1
2280
- if shape_a >= 0:
2281
- body_a = shape_body[shape_a]
2282
- body_b = -1
2283
- if shape_b >= 0:
2284
- body_b = shape_body[shape_b]
2285
- if body_a == body_b:
2286
- return
2287
-
2288
- # find body to world transform
2289
- X_wb_a = wp.transform_identity()
2290
- X_wb_b = wp.transform_identity()
2291
- if body_a >= 0:
2292
- X_wb_a = body_q[body_a]
2293
- if body_b >= 0:
2294
- X_wb_b = body_q[body_b]
2295
-
2296
- # compute body position in world space
2297
- bx_a = wp.transform_point(X_wb_a, contact_point0[tid])
2298
- bx_b = wp.transform_point(X_wb_b, contact_point1[tid])
2299
-
2300
- thickness = contact_thickness[tid]
2301
- n = -contact_normal[tid]
2302
- d = wp.dot(n, bx_b - bx_a) - thickness
2303
-
2304
- if d >= 0.0:
2305
- return
2306
-
2307
- m_inv_a = 0.0
2308
- m_inv_b = 0.0
2309
- I_inv_a = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2310
- I_inv_b = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2311
- # center of mass in body frame
2312
- com_a = wp.vec3(0.0)
2313
- com_b = wp.vec3(0.0)
2314
- # body to world transform
2315
- X_wb_a = wp.transform_identity()
2316
- X_wb_b = wp.transform_identity()
2317
- # angular velocities
2318
- omega_a = wp.vec3(0.0)
2319
- omega_b = wp.vec3(0.0)
2320
- # contact offset in body frame
2321
- offset_a = contact_offset0[tid]
2322
- offset_b = contact_offset1[tid]
2323
-
2324
- if body_a >= 0:
2325
- X_wb_a = body_q[body_a]
2326
- com_a = body_com[body_a]
2327
- m_inv_a = body_m_inv[body_a]
2328
- I_inv_a = body_I_inv[body_a]
2329
- omega_a = wp.spatial_top(body_qd[body_a])
2330
-
2331
- if body_b >= 0:
2332
- X_wb_b = body_q[body_b]
2333
- com_b = body_com[body_b]
2334
- m_inv_b = body_m_inv[body_b]
2335
- I_inv_b = body_I_inv[body_b]
2336
- omega_b = wp.spatial_top(body_qd[body_b])
2337
-
2338
- # use average contact material properties
2339
- mat_nonzero = 0
2340
- mu = 0.0
2341
- if shape_a >= 0:
2342
- mat_nonzero += 1
2343
- mu += shape_materials.mu[shape_a]
2344
- if shape_b >= 0:
2345
- mat_nonzero += 1
2346
- mu += shape_materials.mu[shape_b]
2347
- if mat_nonzero > 0:
2348
- mu /= float(mat_nonzero)
2349
-
2350
- r_a = bx_a - wp.transform_point(X_wb_a, com_a)
2351
- r_b = bx_b - wp.transform_point(X_wb_b, com_b)
2352
-
2353
- angular_a = -wp.cross(r_a, n)
2354
- angular_b = wp.cross(r_b, n)
2355
-
2356
- if contact_inv_weight:
2357
- if body_a >= 0:
2358
- wp.atomic_add(contact_inv_weight, body_a, 1.0)
2359
- if body_b >= 0:
2360
- wp.atomic_add(contact_inv_weight, body_b, 1.0)
2361
-
2362
- lambda_n = compute_contact_constraint_delta(
2363
- d, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, -n, n, angular_a, angular_b, relaxation, dt
2364
- )
2365
-
2366
- lin_delta_a = -n * lambda_n
2367
- lin_delta_b = n * lambda_n
2368
- ang_delta_a = angular_a * lambda_n
2369
- ang_delta_b = angular_b * lambda_n
2370
-
2371
- # linear friction
2372
- if mu > 0.0:
2373
- # add on displacement from surface offsets, this ensures we include any rotational effects due to thickness from feature
2374
- # need to use the current rotation to account for friction due to angular effects (e.g.: slipping contact)
2375
- bx_a += wp.transform_vector(X_wb_a, offset_a)
2376
- bx_b += wp.transform_vector(X_wb_b, offset_b)
2377
-
2378
- # update delta
2379
- delta = bx_b - bx_a
2380
- friction_delta = delta - wp.dot(n, delta) * n
2381
-
2382
- perp = wp.normalize(friction_delta)
2383
-
2384
- r_a = bx_a - wp.transform_point(X_wb_a, com_a)
2385
- r_b = bx_b - wp.transform_point(X_wb_b, com_b)
2386
-
2387
- angular_a = -wp.cross(r_a, perp)
2388
- angular_b = wp.cross(r_b, perp)
2389
-
2390
- err = wp.length(friction_delta)
2391
-
2392
- if err > 0.0:
2393
- lambda_fr = compute_contact_constraint_delta(
2394
- err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, -perp, perp, angular_a, angular_b, 1.0, dt
2395
- )
2396
-
2397
- # limit friction based on incremental normal force, good approximation to limiting on total force
2398
- lambda_fr = wp.max(lambda_fr, -lambda_n * mu)
2399
-
2400
- lin_delta_a -= perp * lambda_fr
2401
- lin_delta_b += perp * lambda_fr
2402
-
2403
- ang_delta_a += angular_a * lambda_fr
2404
- ang_delta_b += angular_b * lambda_fr
2405
-
2406
- torsional_friction = mu * contact_torsional_friction
2407
-
2408
- delta_omega = omega_b - omega_a
2409
-
2410
- if torsional_friction > 0.0:
2411
- err = wp.dot(delta_omega, n) * dt
2412
-
2413
- if wp.abs(err) > 0.0:
2414
- lin = wp.vec3(0.0)
2415
- lambda_torsion = compute_contact_constraint_delta(
2416
- err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, lin, lin, -n, n, 1.0, dt
2417
- )
2418
-
2419
- lambda_torsion = wp.clamp(lambda_torsion, -lambda_n * torsional_friction, lambda_n * torsional_friction)
2420
-
2421
- ang_delta_a -= n * lambda_torsion
2422
- ang_delta_b += n * lambda_torsion
2423
-
2424
- rolling_friction = mu * contact_rolling_friction
2425
- if rolling_friction > 0.0:
2426
- delta_omega -= wp.dot(n, delta_omega) * n
2427
- err = wp.length(delta_omega) * dt
2428
- if err > 0.0:
2429
- lin = wp.vec3(0.0)
2430
- roll_n = wp.normalize(delta_omega)
2431
- lambda_roll = compute_contact_constraint_delta(
2432
- err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, lin, lin, -roll_n, roll_n, 1.0, dt
2433
- )
2434
-
2435
- lambda_roll = wp.max(lambda_roll, -lambda_n * rolling_friction)
2436
-
2437
- ang_delta_a -= roll_n * lambda_roll
2438
- ang_delta_b += roll_n * lambda_roll
2439
-
2440
- if body_a >= 0:
2441
- wp.atomic_add(deltas, body_a, wp.spatial_vector(ang_delta_a, lin_delta_a))
2442
- if body_b >= 0:
2443
- wp.atomic_add(deltas, body_b, wp.spatial_vector(ang_delta_b, lin_delta_b))
2444
-
2445
-
2446
- @wp.kernel
2447
- def update_body_velocities(
2448
- poses: wp.array(dtype=wp.transform),
2449
- poses_prev: wp.array(dtype=wp.transform),
2450
- body_com: wp.array(dtype=wp.vec3),
2451
- dt: float,
2452
- qd_out: wp.array(dtype=wp.spatial_vector),
2453
- ):
2454
- tid = wp.tid()
2455
-
2456
- pose = poses[tid]
2457
- pose_prev = poses_prev[tid]
2458
-
2459
- x = wp.transform_get_translation(pose)
2460
- x_prev = wp.transform_get_translation(pose_prev)
2461
-
2462
- q = wp.transform_get_rotation(pose)
2463
- q_prev = wp.transform_get_rotation(pose_prev)
2464
-
2465
- # Update body velocities according to Alg. 2
2466
- # XXX we consider the body COM as the origin of the body frame
2467
- x_com = x + wp.quat_rotate(q, body_com[tid])
2468
- x_com_prev = x_prev + wp.quat_rotate(q_prev, body_com[tid])
2469
-
2470
- # XXX consider the velocity of the COM
2471
- v = (x_com - x_com_prev) / dt
2472
- dq = q * wp.quat_inverse(q_prev)
2473
-
2474
- omega = 2.0 / dt * wp.vec3(dq[0], dq[1], dq[2])
2475
- if dq[3] < 0.0:
2476
- omega = -omega
2477
-
2478
- qd_out[tid] = wp.spatial_vector(omega, v)
2479
-
2480
-
2481
- @wp.kernel
2482
- def apply_rigid_restitution(
2483
- body_q: wp.array(dtype=wp.transform),
2484
- body_qd: wp.array(dtype=wp.spatial_vector),
2485
- body_q_prev: wp.array(dtype=wp.transform),
2486
- body_qd_prev: wp.array(dtype=wp.spatial_vector),
2487
- body_com: wp.array(dtype=wp.vec3),
2488
- body_m_inv: wp.array(dtype=float),
2489
- body_I_inv: wp.array(dtype=wp.mat33),
2490
- shape_body: wp.array(dtype=int),
2491
- contact_count: wp.array(dtype=int),
2492
- contact_normal: wp.array(dtype=wp.vec3),
2493
- contact_shape0: wp.array(dtype=int),
2494
- contact_shape1: wp.array(dtype=int),
2495
- shape_materials: ModelShapeMaterials,
2496
- contact_point0: wp.array(dtype=wp.vec3),
2497
- contact_point1: wp.array(dtype=wp.vec3),
2498
- contact_offset0: wp.array(dtype=wp.vec3),
2499
- contact_offset1: wp.array(dtype=wp.vec3),
2500
- contact_thickness: wp.array(dtype=float),
2501
- contact_inv_weight: wp.array(dtype=float),
2502
- gravity: wp.vec3,
2503
- dt: float,
2504
- # outputs
2505
- deltas: wp.array(dtype=wp.spatial_vector),
2506
- ):
2507
- tid = wp.tid()
2508
-
2509
- count = contact_count[0]
2510
- if tid >= count:
2511
- return
2512
- shape_a = contact_shape0[tid]
2513
- shape_b = contact_shape1[tid]
2514
- if shape_a == shape_b:
2515
- return
2516
- body_a = -1
2517
- body_b = -1
2518
-
2519
- # use average contact material properties
2520
- mat_nonzero = 0
2521
- restitution = 0.0
2522
- if shape_a >= 0:
2523
- mat_nonzero += 1
2524
- restitution += shape_materials.restitution[shape_a]
2525
- body_a = shape_body[shape_a]
2526
- if shape_b >= 0:
2527
- mat_nonzero += 1
2528
- restitution += shape_materials.restitution[shape_b]
2529
- body_b = shape_body[shape_b]
2530
- if mat_nonzero > 0:
2531
- restitution /= float(mat_nonzero)
2532
- if body_a == body_b:
2533
- return
2534
-
2535
- m_inv_a = 0.0
2536
- m_inv_b = 0.0
2537
- I_inv_a = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2538
- I_inv_b = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2539
- # body to world transform
2540
- X_wb_a_prev = wp.transform_identity()
2541
- X_wb_b_prev = wp.transform_identity()
2542
- # center of mass in body frame
2543
- com_a = wp.vec3(0.0)
2544
- com_b = wp.vec3(0.0)
2545
- # previous velocity at contact points
2546
- v_a = wp.vec3(0.0)
2547
- v_b = wp.vec3(0.0)
2548
- # new velocity at contact points
2549
- v_a_new = wp.vec3(0.0)
2550
- v_b_new = wp.vec3(0.0)
2551
- # inverse mass used to compute the impulse
2552
- inv_mass = 0.0
2553
-
2554
- if body_a >= 0:
2555
- X_wb_a_prev = body_q_prev[body_a]
2556
- # X_wb_a = body_q[body_a]
2557
- m_inv_a = body_m_inv[body_a]
2558
- I_inv_a = body_I_inv[body_a]
2559
- com_a = body_com[body_a]
2560
-
2561
- if body_b >= 0:
2562
- X_wb_b_prev = body_q_prev[body_b]
2563
- # X_wb_b = body_q[body_b]
2564
- m_inv_b = body_m_inv[body_b]
2565
- I_inv_b = body_I_inv[body_b]
2566
- com_b = body_com[body_b]
2567
-
2568
- # compute body position in world space
2569
- bx_a = wp.transform_point(X_wb_a_prev, contact_point0[tid] + contact_offset0[tid])
2570
- bx_b = wp.transform_point(X_wb_b_prev, contact_point1[tid] + contact_offset1[tid])
2571
-
2572
- thickness = contact_thickness[tid]
2573
- n = contact_normal[tid]
2574
- d = -wp.dot(n, bx_b - bx_a) - thickness
2575
- if d >= 0.0:
2576
- return
2577
-
2578
- r_a = bx_a - wp.transform_point(X_wb_a_prev, com_a)
2579
- r_b = bx_b - wp.transform_point(X_wb_b_prev, com_b)
2580
-
2581
- rxn_a = wp.vec3(0.0)
2582
- rxn_b = wp.vec3(0.0)
2583
- if body_a >= 0:
2584
- v_a = velocity_at_point(body_qd_prev[body_a], r_a) + gravity * dt
2585
- v_a_new = velocity_at_point(body_qd[body_a], r_a)
2586
- q_a = wp.transform_get_rotation(X_wb_a_prev)
2587
- rxn_a = wp.quat_rotate_inv(q_a, wp.cross(r_a, n))
2588
- # Eq. 2
2589
- inv_mass_a = m_inv_a + wp.dot(rxn_a, I_inv_a * rxn_a)
2590
- # if contact_inv_weight:
2591
- # if contact_inv_weight[body_a] > 0.0:
2592
- # inv_mass_a *= contact_inv_weight[body_a]
2593
- inv_mass += inv_mass_a
2594
- if body_b >= 0:
2595
- v_b = velocity_at_point(body_qd_prev[body_b], r_b) + gravity * dt
2596
- v_b_new = velocity_at_point(body_qd[body_b], r_b)
2597
- q_b = wp.transform_get_rotation(X_wb_b_prev)
2598
- rxn_b = wp.quat_rotate_inv(q_b, wp.cross(r_b, n))
2599
- # Eq. 3
2600
- inv_mass_b = m_inv_b + wp.dot(rxn_b, I_inv_b * rxn_b)
2601
- # if contact_inv_weight:
2602
- # if contact_inv_weight[body_b] > 0.0:
2603
- # inv_mass_b *= contact_inv_weight[body_b]
2604
- inv_mass += inv_mass_b
2605
-
2606
- if inv_mass == 0.0:
2607
- return
2608
-
2609
- # Eq. 29
2610
- rel_vel_old = wp.dot(n, v_a - v_b)
2611
- rel_vel_new = wp.dot(n, v_a_new - v_b_new)
2612
-
2613
- if rel_vel_old >= 0.0:
2614
- return
2615
-
2616
- # Eq. 34
2617
- dv = (-rel_vel_new - restitution * rel_vel_old) / inv_mass
2618
-
2619
- # Eq. 33
2620
- if body_a >= 0:
2621
- dv_a = dv
2622
- # if contact_inv_weight:
2623
- # if contact_inv_weight[body_a] > 0.0:
2624
- # dv_a *= contact_inv_weight[body_a]
2625
- q_a = wp.transform_get_rotation(X_wb_a_prev)
2626
- dq = wp.quat_rotate(q_a, I_inv_a * rxn_a * dv_a)
2627
- wp.atomic_add(deltas, body_a, wp.spatial_vector(dq, n * m_inv_a * dv_a))
2628
-
2629
- if body_b >= 0:
2630
- dv_b = -dv
2631
- # if contact_inv_weight:
2632
- # if contact_inv_weight[body_b] > 0.0:
2633
- # dv_b *= contact_inv_weight[body_b]
2634
- q_b = wp.transform_get_rotation(X_wb_b_prev)
2635
- dq = wp.quat_rotate(q_b, I_inv_b * rxn_b * dv_b)
2636
- wp.atomic_add(deltas, body_b, wp.spatial_vector(dq, n * m_inv_b * dv_b))
2637
-
2638
-
2639
- class XPBDIntegrator(Integrator):
2640
- """An implicit integrator using eXtended Position-Based Dynamics (XPBD) for rigid and soft body simulation.
2641
-
2642
- References:
2643
- - Miles Macklin, Matthias Müller, and Nuttapong Chentanez. 2016. XPBD: position-based simulation of compliant constrained dynamics. In Proceedings of the 9th International Conference on Motion in Games (MIG '16). Association for Computing Machinery, New York, NY, USA, 49-54. https://doi.org/10.1145/2994258.2994272
2644
- - Matthias Müller, Miles Macklin, Nuttapong Chentanez, Stefan Jeschke, and Tae-Yong Kim. 2020. Detailed rigid body simulation with extended position based dynamics. In Proceedings of the ACM SIGGRAPH/Eurographics Symposium on Computer Animation (SCA '20). Eurographics Association, Goslar, DEU, Article 10, 1-12. https://doi.org/10.1111/cgf.14105
2645
-
2646
- After constructing :class:`Model`, :class:`State`, and :class:`Control` (optional) objects, this time-integrator
2647
- may be used to advance the simulation state forward in time.
2648
-
2649
- Example
2650
- -------
2651
-
2652
- .. code-block:: python
2653
-
2654
- integrator = wp.XPBDIntegrator()
2655
-
2656
- # simulation loop
2657
- for i in range(100):
2658
- state = integrator.simulate(model, state_in, state_out, dt, control)
2659
-
2660
- """
2661
-
2662
- def __init__(
2663
- self,
2664
- iterations=2,
2665
- soft_body_relaxation=0.9,
2666
- soft_contact_relaxation=0.9,
2667
- joint_linear_relaxation=0.7,
2668
- joint_angular_relaxation=0.4,
2669
- rigid_contact_relaxation=0.8,
2670
- rigid_contact_con_weighting=True,
2671
- angular_damping=0.0,
2672
- enable_restitution=False,
2673
- ):
2674
- self.iterations = iterations
2675
-
2676
- self.soft_body_relaxation = soft_body_relaxation
2677
- self.soft_contact_relaxation = soft_contact_relaxation
2678
-
2679
- self.joint_linear_relaxation = joint_linear_relaxation
2680
- self.joint_angular_relaxation = joint_angular_relaxation
2681
-
2682
- self.rigid_contact_relaxation = rigid_contact_relaxation
2683
- self.rigid_contact_con_weighting = rigid_contact_con_weighting
2684
-
2685
- self.angular_damping = angular_damping
2686
-
2687
- self.enable_restitution = enable_restitution
2688
-
2689
- self.compute_body_velocity_from_position_delta = False
2690
-
2691
- # helper variables to track constraint resolution vars
2692
- self._particle_delta_counter = 0
2693
- self._body_delta_counter = 0
2694
-
2695
- def apply_particle_deltas(
2696
- self,
2697
- model: Model,
2698
- state_in: State,
2699
- state_out: State,
2700
- particle_deltas: wp.array,
2701
- dt: float,
2702
- ):
2703
- if state_in.requires_grad:
2704
- particle_q = state_out.particle_q
2705
- # allocate new particle arrays so gradients can be tracked correctly without overwriting
2706
- new_particle_q = wp.empty_like(state_out.particle_q)
2707
- new_particle_qd = wp.empty_like(state_out.particle_qd)
2708
- self._particle_delta_counter += 1
2709
- else:
2710
- if self._particle_delta_counter == 0:
2711
- particle_q = state_out.particle_q
2712
- new_particle_q = state_in.particle_q
2713
- new_particle_qd = state_in.particle_qd
2714
- else:
2715
- particle_q = state_in.particle_q
2716
- new_particle_q = state_out.particle_q
2717
- new_particle_qd = state_out.particle_qd
2718
- self._particle_delta_counter = 1 - self._particle_delta_counter
2719
-
2720
- wp.launch(
2721
- kernel=apply_particle_deltas,
2722
- dim=model.particle_count,
2723
- inputs=[
2724
- self.particle_q_init,
2725
- particle_q,
2726
- model.particle_flags,
2727
- particle_deltas,
2728
- dt,
2729
- model.particle_max_velocity,
2730
- ],
2731
- outputs=[new_particle_q, new_particle_qd],
2732
- device=model.device,
2733
- )
2734
-
2735
- if state_in.requires_grad:
2736
- state_out.particle_q = new_particle_q
2737
- state_out.particle_qd = new_particle_qd
2738
-
2739
- return new_particle_q, new_particle_qd
2740
-
2741
- def apply_body_deltas(
2742
- self,
2743
- model: Model,
2744
- state_in: State,
2745
- state_out: State,
2746
- body_deltas: wp.array,
2747
- dt: float,
2748
- rigid_contact_inv_weight: wp.array = None,
2749
- ):
2750
- with wp.ScopedTimer("apply_body_deltas", False):
2751
- if state_in.requires_grad:
2752
- body_q = state_out.body_q
2753
- body_qd = state_out.body_qd
2754
- new_body_q = wp.clone(body_q)
2755
- new_body_qd = wp.clone(body_qd)
2756
- self._body_delta_counter += 1
2757
- else:
2758
- if self._body_delta_counter == 0:
2759
- body_q = state_out.body_q
2760
- body_qd = state_out.body_qd
2761
- new_body_q = state_in.body_q
2762
- new_body_qd = state_in.body_qd
2763
- else:
2764
- body_q = state_in.body_q
2765
- body_qd = state_in.body_qd
2766
- new_body_q = state_out.body_q
2767
- new_body_qd = state_out.body_qd
2768
- self._body_delta_counter = 1 - self._body_delta_counter
2769
-
2770
- wp.launch(
2771
- kernel=apply_body_deltas,
2772
- dim=model.body_count,
2773
- inputs=[
2774
- body_q,
2775
- body_qd,
2776
- model.body_com,
2777
- model.body_inertia,
2778
- model.body_inv_mass,
2779
- model.body_inv_inertia,
2780
- body_deltas,
2781
- rigid_contact_inv_weight,
2782
- dt,
2783
- ],
2784
- outputs=[
2785
- new_body_q,
2786
- new_body_qd,
2787
- ],
2788
- device=model.device,
2789
- )
2790
-
2791
- if state_in.requires_grad:
2792
- state_out.body_q = new_body_q
2793
- state_out.body_qd = new_body_qd
2794
-
2795
- return new_body_q, new_body_qd
2796
-
2797
- def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
2798
- requires_grad = state_in.requires_grad
2799
- self._particle_delta_counter = 0
2800
- self._body_delta_counter = 0
2801
-
2802
- particle_q = None
2803
- particle_qd = None
2804
- particle_deltas = None
2805
-
2806
- body_q = None
2807
- body_qd = None
2808
- body_deltas = None
2809
-
2810
- rigid_contact_inv_weight = None
2811
-
2812
- if model.rigid_contact_max > 0:
2813
- if self.rigid_contact_con_weighting:
2814
- rigid_contact_inv_weight = wp.zeros_like(model.rigid_contact_thickness)
2815
- rigid_contact_inv_weight_init = None
2816
-
2817
- if control is None:
2818
- control = model.control(clone_variables=False)
2819
-
2820
- with wp.ScopedTimer("simulate", False):
2821
- if model.particle_count:
2822
- particle_q = state_out.particle_q
2823
- particle_qd = state_out.particle_qd
2824
-
2825
- self.particle_q_init = wp.clone(state_in.particle_q)
2826
- if self.enable_restitution:
2827
- self.particle_qd_init = wp.clone(state_in.particle_qd)
2828
- particle_deltas = wp.empty_like(state_out.particle_qd)
2829
-
2830
- self.integrate_particles(model, state_in, state_out, dt)
2831
-
2832
- if model.body_count:
2833
- body_q = state_out.body_q
2834
- body_qd = state_out.body_qd
2835
-
2836
- if self.compute_body_velocity_from_position_delta or self.enable_restitution:
2837
- body_q_init = wp.clone(state_in.body_q)
2838
- body_qd_init = wp.clone(state_in.body_qd)
2839
-
2840
- body_deltas = wp.empty_like(state_out.body_qd)
2841
-
2842
- if model.joint_count:
2843
- wp.launch(
2844
- kernel=apply_joint_actions,
2845
- dim=model.joint_count,
2846
- inputs=[
2847
- state_in.body_q,
2848
- model.body_com,
2849
- model.joint_type,
2850
- model.joint_parent,
2851
- model.joint_child,
2852
- model.joint_X_p,
2853
- model.joint_X_c,
2854
- model.joint_axis_start,
2855
- model.joint_axis_dim,
2856
- model.joint_axis,
2857
- model.joint_axis_mode,
2858
- control.joint_act,
2859
- ],
2860
- outputs=[state_in.body_f],
2861
- device=model.device,
2862
- )
2863
-
2864
- self.integrate_bodies(model, state_in, state_out, dt, self.angular_damping)
2865
-
2866
- spring_constraint_lambdas = None
2867
- if model.spring_count:
2868
- spring_constraint_lambdas = wp.empty_like(model.spring_rest_length)
2869
- edge_constraint_lambdas = None
2870
- if model.edge_count:
2871
- edge_constraint_lambdas = wp.empty_like(model.edge_rest_angle)
2872
-
2873
- for i in range(self.iterations):
2874
- with wp.ScopedTimer(f"iteration_{i}", False):
2875
- if model.body_count:
2876
- if requires_grad and i > 0:
2877
- body_deltas = wp.zeros_like(body_deltas)
2878
- else:
2879
- body_deltas.zero_()
2880
-
2881
- if model.particle_count:
2882
- if requires_grad and i > 0:
2883
- particle_deltas = wp.zeros_like(particle_deltas)
2884
- else:
2885
- particle_deltas.zero_()
2886
-
2887
- # particle ground contact
2888
- if model.ground:
2889
- wp.launch(
2890
- kernel=solve_particle_ground_contacts,
2891
- dim=model.particle_count,
2892
- inputs=[
2893
- particle_q,
2894
- particle_qd,
2895
- model.particle_inv_mass,
2896
- model.particle_radius,
2897
- model.particle_flags,
2898
- model.soft_contact_ke,
2899
- model.soft_contact_kd,
2900
- model.soft_contact_kf,
2901
- model.soft_contact_mu,
2902
- model.ground_plane,
2903
- dt,
2904
- self.soft_contact_relaxation,
2905
- ],
2906
- outputs=[particle_deltas],
2907
- device=model.device,
2908
- )
2909
-
2910
- # particle-rigid body contacts (besides ground plane)
2911
- if model.shape_count > 1:
2912
- wp.launch(
2913
- kernel=solve_particle_shape_contacts,
2914
- dim=model.soft_contact_max,
2915
- inputs=[
2916
- particle_q,
2917
- particle_qd,
2918
- model.particle_inv_mass,
2919
- model.particle_radius,
2920
- model.particle_flags,
2921
- body_q,
2922
- body_qd,
2923
- model.body_com,
2924
- model.body_inv_mass,
2925
- model.body_inv_inertia,
2926
- model.shape_body,
2927
- model.shape_materials,
2928
- model.soft_contact_mu,
2929
- model.particle_adhesion,
2930
- model.soft_contact_count,
2931
- model.soft_contact_particle,
2932
- model.soft_contact_shape,
2933
- model.soft_contact_body_pos,
2934
- model.soft_contact_body_vel,
2935
- model.soft_contact_normal,
2936
- model.soft_contact_max,
2937
- dt,
2938
- self.soft_contact_relaxation,
2939
- ],
2940
- # outputs
2941
- outputs=[particle_deltas, body_deltas],
2942
- device=model.device,
2943
- )
2944
-
2945
- if model.particle_max_radius > 0.0 and model.particle_count > 1:
2946
- # assert model.particle_grid.reserved, "model.particle_grid must be built, see HashGrid.build()"
2947
- wp.launch(
2948
- kernel=solve_particle_particle_contacts,
2949
- dim=model.particle_count,
2950
- inputs=[
2951
- model.particle_grid.id,
2952
- particle_q,
2953
- particle_qd,
2954
- model.particle_inv_mass,
2955
- model.particle_radius,
2956
- model.particle_flags,
2957
- model.particle_mu,
2958
- model.particle_cohesion,
2959
- model.particle_max_radius,
2960
- dt,
2961
- self.soft_contact_relaxation,
2962
- ],
2963
- outputs=[particle_deltas],
2964
- device=model.device,
2965
- )
2966
-
2967
- # distance constraints
2968
- if model.spring_count:
2969
- spring_constraint_lambdas.zero_()
2970
- wp.launch(
2971
- kernel=solve_springs,
2972
- dim=model.spring_count,
2973
- inputs=[
2974
- particle_q,
2975
- particle_qd,
2976
- model.particle_inv_mass,
2977
- model.spring_indices,
2978
- model.spring_rest_length,
2979
- model.spring_stiffness,
2980
- model.spring_damping,
2981
- dt,
2982
- spring_constraint_lambdas,
2983
- ],
2984
- outputs=[particle_deltas],
2985
- device=model.device,
2986
- )
2987
-
2988
- # bending constraints
2989
- if model.edge_count:
2990
- edge_constraint_lambdas.zero_()
2991
- wp.launch(
2992
- kernel=bending_constraint,
2993
- dim=model.edge_count,
2994
- inputs=[
2995
- particle_q,
2996
- particle_qd,
2997
- model.particle_inv_mass,
2998
- model.edge_indices,
2999
- model.edge_rest_angle,
3000
- model.edge_bending_properties,
3001
- dt,
3002
- edge_constraint_lambdas,
3003
- ],
3004
- outputs=[particle_deltas],
3005
- device=model.device,
3006
- )
3007
-
3008
- # tetrahedral FEM
3009
- if model.tet_count:
3010
- wp.launch(
3011
- kernel=solve_tetrahedra,
3012
- dim=model.tet_count,
3013
- inputs=[
3014
- particle_q,
3015
- particle_qd,
3016
- model.particle_inv_mass,
3017
- model.tet_indices,
3018
- model.tet_poses,
3019
- model.tet_activations,
3020
- model.tet_materials,
3021
- dt,
3022
- self.soft_body_relaxation,
3023
- ],
3024
- outputs=[particle_deltas],
3025
- device=model.device,
3026
- )
3027
-
3028
- particle_q, particle_qd = self.apply_particle_deltas(
3029
- model, state_in, state_out, particle_deltas, dt
3030
- )
3031
-
3032
- # handle rigid bodies
3033
- # ----------------------------
3034
-
3035
- if model.joint_count:
3036
- # wp.launch(
3037
- # kernel=solve_simple_body_joints,
3038
- # dim=model.joint_count,
3039
- # inputs=[
3040
- # body_q,
3041
- # body_qd,
3042
- # model.body_com,
3043
- # model.body_inv_mass,
3044
- # model.body_inv_inertia,
3045
- # model.joint_type,
3046
- # model.joint_enabled,
3047
- # model.joint_parent,
3048
- # model.joint_child,
3049
- # model.joint_X_p,
3050
- # model.joint_X_c,
3051
- # model.joint_limit_lower,
3052
- # model.joint_limit_upper,
3053
- # model.joint_axis_start,
3054
- # model.joint_axis_dim,
3055
- # model.joint_axis_mode,
3056
- # model.joint_axis,
3057
- # control.joint_target,
3058
- # model.joint_target_ke,
3059
- # model.joint_target_kd,
3060
- # model.joint_linear_compliance,
3061
- # model.joint_angular_compliance,
3062
- # self.joint_angular_relaxation,
3063
- # self.joint_linear_relaxation,
3064
- # dt,
3065
- # ],
3066
- # outputs=[body_deltas],
3067
- # device=model.device,
3068
- # )
3069
-
3070
- wp.launch(
3071
- kernel=solve_body_joints,
3072
- dim=model.joint_count,
3073
- inputs=[
3074
- body_q,
3075
- body_qd,
3076
- model.body_com,
3077
- model.body_inv_mass,
3078
- model.body_inv_inertia,
3079
- model.joint_type,
3080
- model.joint_enabled,
3081
- model.joint_parent,
3082
- model.joint_child,
3083
- model.joint_X_p,
3084
- model.joint_X_c,
3085
- model.joint_limit_lower,
3086
- model.joint_limit_upper,
3087
- model.joint_axis_start,
3088
- model.joint_axis_dim,
3089
- model.joint_axis_mode,
3090
- model.joint_axis,
3091
- control.joint_act,
3092
- model.joint_target_ke,
3093
- model.joint_target_kd,
3094
- model.joint_linear_compliance,
3095
- model.joint_angular_compliance,
3096
- self.joint_angular_relaxation,
3097
- self.joint_linear_relaxation,
3098
- dt,
3099
- ],
3100
- outputs=[body_deltas],
3101
- device=model.device,
3102
- )
3103
-
3104
- body_q, body_qd = self.apply_body_deltas(model, state_in, state_out, body_deltas, dt)
3105
-
3106
- # Solve rigid contact constraints
3107
- if model.rigid_contact_max and (
3108
- (model.ground and model.shape_ground_contact_pair_count) or model.shape_contact_pair_count
3109
- ):
3110
- if self.rigid_contact_con_weighting:
3111
- rigid_contact_inv_weight.zero_()
3112
- body_deltas.zero_()
3113
-
3114
- wp.launch(
3115
- kernel=solve_body_contact_positions,
3116
- dim=model.rigid_contact_max,
3117
- inputs=[
3118
- body_q,
3119
- body_qd,
3120
- model.body_com,
3121
- model.body_inv_mass,
3122
- model.body_inv_inertia,
3123
- model.shape_body,
3124
- model.rigid_contact_count,
3125
- model.rigid_contact_point0,
3126
- model.rigid_contact_point1,
3127
- model.rigid_contact_offset0,
3128
- model.rigid_contact_offset1,
3129
- model.rigid_contact_normal,
3130
- model.rigid_contact_thickness,
3131
- model.rigid_contact_shape0,
3132
- model.rigid_contact_shape1,
3133
- model.shape_materials,
3134
- self.rigid_contact_relaxation,
3135
- dt,
3136
- model.rigid_contact_torsional_friction,
3137
- model.rigid_contact_rolling_friction,
3138
- ],
3139
- outputs=[
3140
- body_deltas,
3141
- rigid_contact_inv_weight,
3142
- ],
3143
- device=model.device,
3144
- )
3145
-
3146
- # if model.rigid_contact_count.numpy()[0] > 0:
3147
- # print("rigid_contact_count:", model.rigid_contact_count.numpy().flatten())
3148
- # # print("rigid_active_contact_distance:", rigid_active_contact_distance.numpy().flatten())
3149
- # # print("rigid_active_contact_point0:", rigid_active_contact_point0.numpy().flatten())
3150
- # # print("rigid_active_contact_point1:", rigid_active_contact_point1.numpy().flatten())
3151
- # print("body_deltas:", body_deltas.numpy().flatten())
3152
-
3153
- # print(rigid_active_contact_distance.numpy().flatten())
3154
-
3155
- if self.enable_restitution and i == 0:
3156
- # remember contact constraint weighting from the first iteration
3157
- if self.rigid_contact_con_weighting:
3158
- rigid_contact_inv_weight_init = wp.clone(rigid_contact_inv_weight)
3159
- else:
3160
- rigid_contact_inv_weight_init = None
3161
-
3162
- body_q, body_qd = self.apply_body_deltas(
3163
- model, state_in, state_out, body_deltas, dt, rigid_contact_inv_weight
3164
- )
3165
-
3166
- if model.particle_count:
3167
- if particle_q.ptr != state_out.particle_q.ptr:
3168
- state_out.particle_q.assign(particle_q)
3169
- state_out.particle_qd.assign(particle_qd)
3170
-
3171
- if model.body_count:
3172
- if body_q.ptr != state_out.body_q.ptr:
3173
- state_out.body_q.assign(body_q)
3174
- state_out.body_qd.assign(body_qd)
3175
-
3176
- # update body velocities from position changes
3177
- if self.compute_body_velocity_from_position_delta and model.body_count and not requires_grad:
3178
- # causes gradient issues (probably due to numerical problems
3179
- # when computing velocities from position changes)
3180
- if requires_grad:
3181
- out_body_qd = wp.clone(state_out.body_qd)
3182
- else:
3183
- out_body_qd = state_out.body_qd
3184
-
3185
- # update body velocities
3186
- wp.launch(
3187
- kernel=update_body_velocities,
3188
- dim=model.body_count,
3189
- inputs=[state_out.body_q, body_q_init, model.body_com, dt],
3190
- outputs=[out_body_qd],
3191
- device=model.device,
3192
- )
3193
-
3194
- if self.enable_restitution:
3195
- if model.particle_count:
3196
- wp.launch(
3197
- kernel=apply_particle_shape_restitution,
3198
- dim=model.particle_count,
3199
- inputs=[
3200
- particle_q,
3201
- particle_qd,
3202
- self.particle_q_init,
3203
- self.particle_qd_init,
3204
- model.particle_inv_mass,
3205
- model.particle_radius,
3206
- model.particle_flags,
3207
- body_q,
3208
- body_qd,
3209
- model.body_com,
3210
- model.body_inv_mass,
3211
- model.body_inv_inertia,
3212
- model.shape_body,
3213
- model.shape_materials,
3214
- model.particle_adhesion,
3215
- model.soft_contact_restitution,
3216
- model.soft_contact_count,
3217
- model.soft_contact_particle,
3218
- model.soft_contact_shape,
3219
- model.soft_contact_body_pos,
3220
- model.soft_contact_body_vel,
3221
- model.soft_contact_normal,
3222
- model.soft_contact_max,
3223
- dt,
3224
- self.soft_contact_relaxation,
3225
- ],
3226
- outputs=[state_out.particle_qd],
3227
- device=model.device,
3228
- )
3229
- if model.ground:
3230
- wp.launch(
3231
- kernel=apply_particle_ground_restitution,
3232
- dim=model.particle_count,
3233
- inputs=[
3234
- particle_q,
3235
- particle_qd,
3236
- self.particle_q_init,
3237
- self.particle_qd_init,
3238
- model.particle_inv_mass,
3239
- model.particle_radius,
3240
- model.particle_flags,
3241
- model.particle_adhesion,
3242
- model.soft_contact_restitution,
3243
- model.ground_plane,
3244
- dt,
3245
- self.soft_contact_relaxation,
3246
- ],
3247
- outputs=[state_out.particle_qd],
3248
- device=model.device,
3249
- )
3250
-
3251
- if model.body_count:
3252
- body_deltas.zero_()
3253
- wp.launch(
3254
- kernel=apply_rigid_restitution,
3255
- dim=model.rigid_contact_max,
3256
- inputs=[
3257
- state_out.body_q,
3258
- state_out.body_qd,
3259
- body_q_init,
3260
- body_qd_init,
3261
- model.body_com,
3262
- model.body_inv_mass,
3263
- model.body_inv_inertia,
3264
- model.shape_body,
3265
- model.rigid_contact_count,
3266
- model.rigid_contact_normal,
3267
- model.rigid_contact_shape0,
3268
- model.rigid_contact_shape1,
3269
- model.shape_materials,
3270
- model.rigid_contact_point0,
3271
- model.rigid_contact_point1,
3272
- model.rigid_contact_offset0,
3273
- model.rigid_contact_offset1,
3274
- model.rigid_contact_thickness,
3275
- rigid_contact_inv_weight_init,
3276
- model.gravity,
3277
- dt,
3278
- ],
3279
- outputs=[
3280
- body_deltas,
3281
- ],
3282
- device=model.device,
3283
- )
3284
-
3285
- wp.launch(
3286
- kernel=apply_body_delta_velocities,
3287
- dim=model.body_count,
3288
- inputs=[
3289
- body_deltas,
3290
- ],
3291
- outputs=[state_out.body_qd],
3292
- device=model.device,
3293
- )
3294
-
3295
- return state_out