warp-lang 0.9.0__py3-none-win_amd64.whl → 0.11.0__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 (315) hide show
  1. warp/__init__.py +15 -7
  2. warp/__init__.pyi +1 -0
  3. warp/bin/warp-clang.dll +0 -0
  4. warp/bin/warp.dll +0 -0
  5. warp/build.py +22 -443
  6. warp/build_dll.py +384 -0
  7. warp/builtins.py +998 -488
  8. warp/codegen.py +1307 -739
  9. warp/config.py +5 -3
  10. warp/constants.py +6 -0
  11. warp/context.py +1291 -548
  12. warp/dlpack.py +31 -31
  13. warp/fabric.py +326 -0
  14. warp/fem/__init__.py +27 -0
  15. warp/fem/cache.py +389 -0
  16. warp/fem/dirichlet.py +181 -0
  17. warp/fem/domain.py +263 -0
  18. warp/fem/field/__init__.py +101 -0
  19. warp/fem/field/field.py +149 -0
  20. warp/fem/field/nodal_field.py +299 -0
  21. warp/fem/field/restriction.py +21 -0
  22. warp/fem/field/test.py +181 -0
  23. warp/fem/field/trial.py +183 -0
  24. warp/fem/geometry/__init__.py +19 -0
  25. warp/fem/geometry/closest_point.py +70 -0
  26. warp/fem/geometry/deformed_geometry.py +271 -0
  27. warp/fem/geometry/element.py +744 -0
  28. warp/fem/geometry/geometry.py +186 -0
  29. warp/fem/geometry/grid_2d.py +373 -0
  30. warp/fem/geometry/grid_3d.py +435 -0
  31. warp/fem/geometry/hexmesh.py +953 -0
  32. warp/fem/geometry/partition.py +376 -0
  33. warp/fem/geometry/quadmesh_2d.py +532 -0
  34. warp/fem/geometry/tetmesh.py +840 -0
  35. warp/fem/geometry/trimesh_2d.py +577 -0
  36. warp/fem/integrate.py +1616 -0
  37. warp/fem/operator.py +191 -0
  38. warp/fem/polynomial.py +213 -0
  39. warp/fem/quadrature/__init__.py +2 -0
  40. warp/fem/quadrature/pic_quadrature.py +245 -0
  41. warp/fem/quadrature/quadrature.py +294 -0
  42. warp/fem/space/__init__.py +292 -0
  43. warp/fem/space/basis_space.py +489 -0
  44. warp/fem/space/collocated_function_space.py +105 -0
  45. warp/fem/space/dof_mapper.py +236 -0
  46. warp/fem/space/function_space.py +145 -0
  47. warp/fem/space/grid_2d_function_space.py +267 -0
  48. warp/fem/space/grid_3d_function_space.py +306 -0
  49. warp/fem/space/hexmesh_function_space.py +352 -0
  50. warp/fem/space/partition.py +350 -0
  51. warp/fem/space/quadmesh_2d_function_space.py +369 -0
  52. warp/fem/space/restriction.py +160 -0
  53. warp/fem/space/shape/__init__.py +15 -0
  54. warp/fem/space/shape/cube_shape_function.py +738 -0
  55. warp/fem/space/shape/shape_function.py +103 -0
  56. warp/fem/space/shape/square_shape_function.py +611 -0
  57. warp/fem/space/shape/tet_shape_function.py +567 -0
  58. warp/fem/space/shape/triangle_shape_function.py +429 -0
  59. warp/fem/space/tetmesh_function_space.py +292 -0
  60. warp/fem/space/topology.py +295 -0
  61. warp/fem/space/trimesh_2d_function_space.py +221 -0
  62. warp/fem/types.py +77 -0
  63. warp/fem/utils.py +495 -0
  64. warp/native/array.h +164 -55
  65. warp/native/builtin.h +150 -174
  66. warp/native/bvh.cpp +75 -328
  67. warp/native/bvh.cu +406 -23
  68. warp/native/bvh.h +37 -45
  69. warp/native/clang/clang.cpp +136 -24
  70. warp/native/crt.cpp +1 -76
  71. warp/native/crt.h +111 -104
  72. warp/native/cuda_crt.h +1049 -0
  73. warp/native/cuda_util.cpp +15 -3
  74. warp/native/cuda_util.h +3 -1
  75. warp/native/cutlass/tools/library/scripts/conv2d_operation.py +463 -0
  76. warp/native/cutlass/tools/library/scripts/conv3d_operation.py +321 -0
  77. warp/native/cutlass/tools/library/scripts/gemm_operation.py +988 -0
  78. warp/native/cutlass/tools/library/scripts/generator.py +4625 -0
  79. warp/native/cutlass/tools/library/scripts/library.py +799 -0
  80. warp/native/cutlass/tools/library/scripts/manifest.py +402 -0
  81. warp/native/cutlass/tools/library/scripts/pycutlass/docs/source/conf.py +96 -0
  82. warp/native/cutlass/tools/library/scripts/pycutlass/profile/conv/conv2d_f16_sm80.py +106 -0
  83. warp/native/cutlass/tools/library/scripts/pycutlass/profile/gemm/gemm_f32_sm80.py +91 -0
  84. warp/native/cutlass/tools/library/scripts/pycutlass/setup.py +80 -0
  85. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/__init__.py +48 -0
  86. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/arguments.py +118 -0
  87. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/c_types.py +241 -0
  88. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/compiler.py +432 -0
  89. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/conv2d_operation.py +631 -0
  90. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/epilogue.py +1026 -0
  91. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/frontend.py +104 -0
  92. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/gemm_operation.py +1276 -0
  93. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/library.py +744 -0
  94. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/memory_manager.py +74 -0
  95. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/operation.py +110 -0
  96. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/parser.py +619 -0
  97. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/reduction_operation.py +398 -0
  98. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/tensor_ref.py +70 -0
  99. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/__init__.py +4 -0
  100. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/conv2d_testbed.py +646 -0
  101. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/gemm_grouped_testbed.py +235 -0
  102. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/gemm_testbed.py +557 -0
  103. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/profiler.py +70 -0
  104. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/type_hint.py +39 -0
  105. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/__init__.py +1 -0
  106. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/device.py +76 -0
  107. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/reference_model.py +255 -0
  108. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/__init__.py +0 -0
  109. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +201 -0
  110. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +177 -0
  111. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +98 -0
  112. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +95 -0
  113. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_few_channels_f16nhwc_f16nhwc_f16nhwc_tensor_op_f32_sm80.py +163 -0
  114. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_fixed_channels_f16nhwc_f16nhwc_f16nhwc_tensor_op_f32_sm80.py +187 -0
  115. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +309 -0
  116. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +54 -0
  117. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +96 -0
  118. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +107 -0
  119. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_strided_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +253 -0
  120. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +97 -0
  121. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +242 -0
  122. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +96 -0
  123. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +107 -0
  124. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/run_all_tests.py +10 -0
  125. warp/native/cutlass/tools/library/scripts/pycutlass/test/frontend/test_frontend.py +146 -0
  126. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/__init__.py +0 -0
  127. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_bf16_sm80.py +96 -0
  128. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f16_sm80.py +447 -0
  129. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f32_sm80.py +146 -0
  130. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f64_sm80.py +102 -0
  131. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_grouped_sm80.py +203 -0
  132. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_s8_sm80.py +229 -0
  133. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/run_all_tests.py +9 -0
  134. warp/native/cutlass/tools/library/scripts/pycutlass/test/unit/test_sm80.py +453 -0
  135. warp/native/cutlass/tools/library/scripts/rank_2k_operation.py +398 -0
  136. warp/native/cutlass/tools/library/scripts/rank_k_operation.py +387 -0
  137. warp/native/cutlass/tools/library/scripts/rt.py +796 -0
  138. warp/native/cutlass/tools/library/scripts/symm_operation.py +400 -0
  139. warp/native/cutlass/tools/library/scripts/trmm_operation.py +407 -0
  140. warp/native/cutlass_gemm.cu +5 -3
  141. warp/native/exports.h +1240 -949
  142. warp/native/fabric.h +228 -0
  143. warp/native/hashgrid.cpp +4 -4
  144. warp/native/hashgrid.h +22 -2
  145. warp/native/initializer_array.h +2 -2
  146. warp/native/intersect.h +22 -7
  147. warp/native/intersect_adj.h +8 -8
  148. warp/native/intersect_tri.h +13 -16
  149. warp/native/marching.cu +157 -161
  150. warp/native/mat.h +119 -19
  151. warp/native/matnn.h +2 -2
  152. warp/native/mesh.cpp +108 -83
  153. warp/native/mesh.cu +243 -6
  154. warp/native/mesh.h +1547 -458
  155. warp/native/nanovdb/NanoVDB.h +1 -1
  156. warp/native/noise.h +272 -329
  157. warp/native/quat.h +51 -8
  158. warp/native/rand.h +45 -35
  159. warp/native/range.h +6 -2
  160. warp/native/reduce.cpp +157 -0
  161. warp/native/reduce.cu +348 -0
  162. warp/native/runlength_encode.cpp +62 -0
  163. warp/native/runlength_encode.cu +46 -0
  164. warp/native/scan.cu +11 -13
  165. warp/native/scan.h +1 -0
  166. warp/native/solid_angle.h +442 -0
  167. warp/native/sort.cpp +13 -0
  168. warp/native/sort.cu +9 -1
  169. warp/native/sparse.cpp +338 -0
  170. warp/native/sparse.cu +545 -0
  171. warp/native/spatial.h +2 -2
  172. warp/native/temp_buffer.h +30 -0
  173. warp/native/vec.h +126 -24
  174. warp/native/volume.h +120 -0
  175. warp/native/warp.cpp +658 -53
  176. warp/native/warp.cu +660 -68
  177. warp/native/warp.h +112 -12
  178. warp/optim/__init__.py +1 -0
  179. warp/optim/linear.py +922 -0
  180. warp/optim/sgd.py +92 -0
  181. warp/render/render_opengl.py +392 -152
  182. warp/render/render_usd.py +11 -11
  183. warp/sim/__init__.py +2 -2
  184. warp/sim/articulation.py +385 -185
  185. warp/sim/collide.py +21 -8
  186. warp/sim/import_mjcf.py +297 -106
  187. warp/sim/import_urdf.py +389 -210
  188. warp/sim/import_usd.py +198 -97
  189. warp/sim/inertia.py +17 -18
  190. warp/sim/integrator_euler.py +14 -8
  191. warp/sim/integrator_xpbd.py +161 -19
  192. warp/sim/model.py +795 -291
  193. warp/sim/optimizer.py +2 -6
  194. warp/sim/render.py +65 -3
  195. warp/sim/utils.py +3 -0
  196. warp/sparse.py +1227 -0
  197. warp/stubs.py +665 -223
  198. warp/tape.py +66 -15
  199. warp/tests/__main__.py +3 -6
  200. warp/tests/assets/curlnoise_golden.npy +0 -0
  201. warp/tests/assets/pnoise_golden.npy +0 -0
  202. warp/tests/assets/torus.usda +105 -105
  203. warp/tests/{test_class_kernel.py → aux_test_class_kernel.py} +9 -1
  204. warp/tests/aux_test_conditional_unequal_types_kernels.py +21 -0
  205. warp/tests/{test_dependent.py → aux_test_dependent.py} +2 -2
  206. warp/tests/{test_reference.py → aux_test_reference.py} +1 -1
  207. warp/tests/aux_test_unresolved_func.py +14 -0
  208. warp/tests/aux_test_unresolved_symbol.py +14 -0
  209. warp/tests/disabled_kinematics.py +239 -0
  210. warp/tests/run_coverage_serial.py +31 -0
  211. warp/tests/test_adam.py +103 -106
  212. warp/tests/test_arithmetic.py +128 -74
  213. warp/tests/test_array.py +1497 -211
  214. warp/tests/test_array_reduce.py +150 -0
  215. warp/tests/test_atomic.py +64 -28
  216. warp/tests/test_bool.py +99 -0
  217. warp/tests/test_builtins_resolution.py +1292 -0
  218. warp/tests/test_bvh.py +75 -43
  219. warp/tests/test_closest_point_edge_edge.py +54 -57
  220. warp/tests/test_codegen.py +233 -128
  221. warp/tests/test_compile_consts.py +28 -20
  222. warp/tests/test_conditional.py +108 -24
  223. warp/tests/test_copy.py +10 -12
  224. warp/tests/test_ctypes.py +112 -88
  225. warp/tests/test_dense.py +21 -14
  226. warp/tests/test_devices.py +98 -0
  227. warp/tests/test_dlpack.py +136 -108
  228. warp/tests/test_examples.py +277 -0
  229. warp/tests/test_fabricarray.py +955 -0
  230. warp/tests/test_fast_math.py +15 -11
  231. warp/tests/test_fem.py +1271 -0
  232. warp/tests/test_fp16.py +53 -19
  233. warp/tests/test_func.py +187 -74
  234. warp/tests/test_generics.py +194 -49
  235. warp/tests/test_grad.py +180 -116
  236. warp/tests/test_grad_customs.py +176 -0
  237. warp/tests/test_hash_grid.py +52 -37
  238. warp/tests/test_import.py +10 -23
  239. warp/tests/test_indexedarray.py +577 -24
  240. warp/tests/test_intersect.py +18 -9
  241. warp/tests/test_large.py +141 -0
  242. warp/tests/test_launch.py +251 -15
  243. warp/tests/test_lerp.py +64 -65
  244. warp/tests/test_linear_solvers.py +154 -0
  245. warp/tests/test_lvalue.py +493 -0
  246. warp/tests/test_marching_cubes.py +12 -13
  247. warp/tests/test_mat.py +508 -2778
  248. warp/tests/test_mat_lite.py +115 -0
  249. warp/tests/test_mat_scalar_ops.py +2889 -0
  250. warp/tests/test_math.py +103 -9
  251. warp/tests/test_matmul.py +305 -69
  252. warp/tests/test_matmul_lite.py +410 -0
  253. warp/tests/test_mesh.py +71 -14
  254. warp/tests/test_mesh_query_aabb.py +41 -25
  255. warp/tests/test_mesh_query_point.py +325 -34
  256. warp/tests/test_mesh_query_ray.py +39 -22
  257. warp/tests/test_mlp.py +30 -22
  258. warp/tests/test_model.py +92 -89
  259. warp/tests/test_modules_lite.py +39 -0
  260. warp/tests/test_multigpu.py +88 -114
  261. warp/tests/test_noise.py +12 -11
  262. warp/tests/test_operators.py +16 -20
  263. warp/tests/test_options.py +11 -11
  264. warp/tests/test_pinned.py +17 -18
  265. warp/tests/test_print.py +32 -11
  266. warp/tests/test_quat.py +275 -129
  267. warp/tests/test_rand.py +18 -16
  268. warp/tests/test_reload.py +38 -34
  269. warp/tests/test_rounding.py +50 -43
  270. warp/tests/test_runlength_encode.py +190 -0
  271. warp/tests/test_smoothstep.py +9 -11
  272. warp/tests/test_snippet.py +143 -0
  273. warp/tests/test_sparse.py +460 -0
  274. warp/tests/test_spatial.py +276 -243
  275. warp/tests/test_streams.py +110 -85
  276. warp/tests/test_struct.py +331 -85
  277. warp/tests/test_tape.py +39 -21
  278. warp/tests/test_torch.py +118 -89
  279. warp/tests/test_transient_module.py +12 -13
  280. warp/tests/test_types.py +614 -0
  281. warp/tests/test_utils.py +494 -0
  282. warp/tests/test_vec.py +354 -1987
  283. warp/tests/test_vec_lite.py +73 -0
  284. warp/tests/test_vec_scalar_ops.py +2099 -0
  285. warp/tests/test_volume.py +457 -293
  286. warp/tests/test_volume_write.py +124 -134
  287. warp/tests/unittest_serial.py +35 -0
  288. warp/tests/unittest_suites.py +341 -0
  289. warp/tests/unittest_utils.py +568 -0
  290. warp/tests/unused_test_misc.py +71 -0
  291. warp/tests/{test_debug.py → walkthough_debug.py} +3 -17
  292. warp/thirdparty/appdirs.py +36 -45
  293. warp/thirdparty/unittest_parallel.py +549 -0
  294. warp/torch.py +72 -30
  295. warp/types.py +1744 -713
  296. warp/utils.py +360 -350
  297. warp_lang-0.11.0.dist-info/LICENSE.md +36 -0
  298. warp_lang-0.11.0.dist-info/METADATA +238 -0
  299. warp_lang-0.11.0.dist-info/RECORD +332 -0
  300. {warp_lang-0.9.0.dist-info → warp_lang-0.11.0.dist-info}/WHEEL +1 -1
  301. warp/bin/warp-clang.exp +0 -0
  302. warp/bin/warp-clang.lib +0 -0
  303. warp/bin/warp.exp +0 -0
  304. warp/bin/warp.lib +0 -0
  305. warp/tests/test_all.py +0 -215
  306. warp/tests/test_array_scan.py +0 -60
  307. warp/tests/test_base.py +0 -208
  308. warp/tests/test_unresolved_func.py +0 -7
  309. warp/tests/test_unresolved_symbol.py +0 -7
  310. warp_lang-0.9.0.dist-info/METADATA +0 -20
  311. warp_lang-0.9.0.dist-info/RECORD +0 -177
  312. /warp/tests/{test_compile_consts_dummy.py → aux_test_compile_consts_dummy.py} +0 -0
  313. /warp/tests/{test_reference_reference.py → aux_test_reference_reference.py} +0 -0
  314. /warp/tests/{test_square.py → aux_test_square.py} +0 -0
  315. {warp_lang-0.9.0.dist-info → warp_lang-0.11.0.dist-info}/top_level.txt +0 -0
warp/sim/import_urdf.py CHANGED
@@ -5,104 +5,23 @@
5
5
  # distribution of this software and related documentation without an express
6
6
  # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
7
 
8
+ import os
9
+ import xml.etree.ElementTree as ET
10
+ from typing import Union
8
11
 
9
- try:
10
- import urdfpy
11
- except:
12
- pass
13
-
14
- import math
15
12
  import numpy as np
16
13
 
17
14
  import warp as wp
18
15
  from warp.sim.model import Mesh
19
16
 
20
17
 
21
- def urdf_add_collision(builder, link, collisions, density, shape_ke, shape_kd, shape_kf, shape_mu, shape_restitution):
22
- # add geometry
23
- for collision in collisions:
24
- origin = urdfpy.matrix_to_xyz_rpy(collision.origin)
25
-
26
- pos = origin[0:3]
27
- rot = wp.quatf(*wp.quat_rpy(*origin[3:6]))
28
-
29
- geo = collision.geometry
30
-
31
- if geo.box:
32
- builder.add_shape_box(
33
- body=link,
34
- pos=pos,
35
- rot=rot,
36
- hx=geo.box.size[0] * 0.5,
37
- hy=geo.box.size[1] * 0.5,
38
- hz=geo.box.size[2] * 0.5,
39
- density=density,
40
- ke=shape_ke,
41
- kd=shape_kd,
42
- kf=shape_kf,
43
- mu=shape_mu,
44
- restitution=shape_restitution,
45
- )
46
-
47
- if geo.sphere:
48
- builder.add_shape_sphere(
49
- body=link,
50
- pos=pos,
51
- rot=rot,
52
- radius=geo.sphere.radius,
53
- density=density,
54
- ke=shape_ke,
55
- kd=shape_kd,
56
- kf=shape_kf,
57
- mu=shape_mu,
58
- restitution=shape_restitution,
59
- )
60
-
61
- if geo.cylinder:
62
- # cylinders in URDF are aligned with z-axis, while Warp uses y-axis
63
- r = wp.quat_from_axis_angle((1.0, 0.0, 0.0), math.pi * 0.5)
64
-
65
- builder.add_shape_capsule(
66
- body=link,
67
- pos=pos,
68
- rot=wp.mul(rot, r),
69
- radius=geo.cylinder.radius,
70
- half_height=geo.cylinder.length * 0.5,
71
- density=density,
72
- ke=shape_ke,
73
- kd=shape_kd,
74
- kf=shape_kf,
75
- mu=shape_mu,
76
- restitution=shape_restitution,
77
- )
78
-
79
- if geo.mesh:
80
- for m in geo.mesh.meshes:
81
- faces = list(np.array(m.faces).astype("int").flatten())
82
- vertices = np.array(m.vertices, dtype=np.float32).reshape((-1, 3))
83
- if geo.mesh.scale is not None:
84
- vertices *= geo.mesh.scale
85
- mesh = Mesh(vertices, faces)
86
- builder.add_shape_mesh(
87
- body=link,
88
- pos=pos,
89
- rot=rot,
90
- mesh=mesh,
91
- density=density,
92
- ke=shape_ke,
93
- kd=shape_kd,
94
- kf=shape_kf,
95
- mu=shape_mu,
96
- restitution=shape_restitution,
97
- )
98
-
99
-
100
18
  def parse_urdf(
101
- filename,
19
+ urdf_filename,
102
20
  builder,
103
- xform,
21
+ xform=wp.transform(),
104
22
  floating=False,
105
- density=0.0,
23
+ base_joint: Union[dict, str] = None,
24
+ density=1000.0,
106
25
  stiffness=100.0,
107
26
  damping=10.0,
108
27
  armature=0.0,
@@ -111,12 +30,189 @@ def parse_urdf(
111
30
  shape_kf=1.0e2,
112
31
  shape_mu=0.25,
113
32
  shape_restitution=0.5,
33
+ shape_thickness=0.0,
114
34
  limit_ke=100.0,
115
35
  limit_kd=10.0,
36
+ scale=1.0,
116
37
  parse_visuals_as_colliders=False,
117
38
  enable_self_collisions=True,
39
+ ignore_inertial_definitions=True,
40
+ ensure_nonstatic_links=True,
41
+ static_link_mass=1e-2,
42
+ collapse_fixed_joints=False,
118
43
  ):
119
- robot = urdfpy.URDF.load(filename)
44
+ """
45
+ Parses a URDF file and adds the bodies and joints to the given ModelBuilder.
46
+
47
+ Args:
48
+ urdf_filename (str): The filename of the URDF file to parse.
49
+ builder (ModelBuilder): The :class:`ModelBuilder` to add the bodies and joints to.
50
+ xform (:ref:`transform <transform>`): The transform to apply to the root body.
51
+ floating (bool): If True, the root body is a free joint. If False, the root body is connected via a fixed joint to the world, unless a `base_joint` is defined.
52
+ base_joint (Union[str, dict]): The joint by which the root body is connected to the world. This can be either a string defining the joint axes of a D6 joint with comma-separated positional and angular axis names (e.g. "px,py,rz" for a D6 joint with linear axes in x, y and an angular axis in z) or a dict with joint parameters (see :meth:`ModelBuilder.add_joint`).
53
+ density (float): The density of the shapes in kg/m^3 which will be used to calculate the body mass and inertia.
54
+ stiffness (float): The stiffness of the joints.
55
+ damping (float): The damping of the joints.
56
+ armature (float): The armature of the joints (bias to add to the inertia diagonals that may stabilize the simulation).
57
+ shape_ke (float): The stiffness of the shape contacts (used by SemiImplicitIntegrator).
58
+ shape_kd (float): The damping of the shape contacts (used by SemiImplicitIntegrator).
59
+ shape_kf (float): The friction stiffness of the shape contacts (used by SemiImplicitIntegrator).
60
+ shape_mu (float): The friction coefficient of the shape contacts.
61
+ shape_restitution (float): The restitution coefficient of the shape contacts.
62
+ shape_thickness (float): The thickness to add to the shape geometry.
63
+ limit_ke (float): The stiffness of the joint limits (used by SemiImplicitIntegrator).
64
+ limit_kd (float): The damping of the joint limits (used by SemiImplicitIntegrator).
65
+ scale (float): The scaling factor to apply to the imported mechanism.
66
+ parse_visuals_as_colliders (bool): If True, the geometry defined under the `<visual>` tags is used for collision handling instead of the `<collision>` geometries.
67
+ enable_self_collisions (bool): If True, self-collisions are enabled.
68
+ ignore_inertial_definitions (bool): If True, the inertial parameters defined in the URDF are ignored and the inertia is calculated from the shape geometry.
69
+ ensure_nonstatic_links (bool): If True, links with zero mass are given a small mass (see `static_link_mass`) to ensure they are dynamic.
70
+ static_link_mass (float): The mass to assign to links with zero mass (if `ensure_nonstatic_links` is set to True).
71
+ collapse_fixed_joints (bool): If True, fixed joints are removed and the respective bodies are merged.
72
+ """
73
+
74
+ file = ET.parse(urdf_filename)
75
+ root = file.getroot()
76
+
77
+ def parse_transform(element):
78
+ if element is None or element.find("origin") is None:
79
+ return wp.transform()
80
+ origin = element.find("origin")
81
+ xyz = origin.get("xyz") or "0 0 0"
82
+ rpy = origin.get("rpy") or "0 0 0"
83
+ xyz = [float(x) * scale for x in xyz.split()]
84
+ rpy = [float(x) for x in rpy.split()]
85
+ return wp.transform(xyz, wp.quat_rpy(*rpy))
86
+
87
+ def parse_shapes(link, collisions, density, incoming_xform=None):
88
+ # add geometry
89
+ for collision in collisions:
90
+ geo = collision.find("geometry")
91
+ if geo is None:
92
+ continue
93
+
94
+ tf = parse_transform(collision)
95
+ if incoming_xform is not None:
96
+ tf = incoming_xform * tf
97
+
98
+ for box in geo.findall("box"):
99
+ size = box.get("size") or "1 1 1"
100
+ size = [float(x) for x in size.split()]
101
+ builder.add_shape_box(
102
+ body=link,
103
+ pos=wp.vec3(tf.p),
104
+ rot=wp.quat(tf.q),
105
+ hx=size[0] * 0.5 * scale,
106
+ hy=size[1] * 0.5 * scale,
107
+ hz=size[2] * 0.5 * scale,
108
+ density=density,
109
+ ke=shape_ke,
110
+ kd=shape_kd,
111
+ kf=shape_kf,
112
+ mu=shape_mu,
113
+ restitution=shape_restitution,
114
+ thickness=shape_thickness,
115
+ )
116
+
117
+ for sphere in geo.findall("sphere"):
118
+ builder.add_shape_sphere(
119
+ body=link,
120
+ pos=wp.vec3(tf.p),
121
+ rot=wp.quat(tf.q),
122
+ radius=float(sphere.get("radius") or "1") * scale,
123
+ density=density,
124
+ ke=shape_ke,
125
+ kd=shape_kd,
126
+ kf=shape_kf,
127
+ mu=shape_mu,
128
+ restitution=shape_restitution,
129
+ thickness=shape_thickness,
130
+ )
131
+
132
+ for cylinder in geo.findall("cylinder"):
133
+ builder.add_shape_capsule(
134
+ body=link,
135
+ pos=wp.vec3(tf.p),
136
+ rot=wp.quat(tf.q),
137
+ radius=float(cylinder.get("radius") or "1") * scale,
138
+ half_height=float(cylinder.get("length") or "1") * 0.5 * scale,
139
+ density=density,
140
+ ke=shape_ke,
141
+ kd=shape_kd,
142
+ kf=shape_kf,
143
+ mu=shape_mu,
144
+ up_axis=2, # cylinders in URDF are aligned with z-axis
145
+ restitution=shape_restitution,
146
+ thickness=shape_thickness,
147
+ )
148
+
149
+ for mesh in geo.findall("mesh"):
150
+ filename = mesh.get("filename")
151
+ if filename is None:
152
+ continue
153
+ if filename.startswith("http://") or filename.startswith("https://"):
154
+ # download mesh
155
+ import shutil
156
+ import tempfile
157
+
158
+ import requests
159
+
160
+ with tempfile.TemporaryDirectory() as tmpdir:
161
+ # get filename extension
162
+ extension = os.path.splitext(filename)[1]
163
+ tmpfile = os.path.join(tmpdir, "mesh" + extension)
164
+ with requests.get(filename, stream=True) as r:
165
+ with open(tmpfile, "wb") as f:
166
+ shutil.copyfileobj(r.raw, f)
167
+ filename = tmpfile
168
+ else:
169
+ filename = os.path.join(os.path.dirname(urdf_filename), filename)
170
+ if not os.path.exists(filename):
171
+ wp.utils.warn(f"Warning: mesh file {filename} does not exist")
172
+ continue
173
+
174
+ import trimesh
175
+
176
+ m = trimesh.load_mesh(filename)
177
+ scaling = mesh.get("scale") or "1 1 1"
178
+ scaling = np.array([float(x) * scale for x in scaling.split()])
179
+ if hasattr(m, "geometry"):
180
+ # multiple meshes are contained in a scene
181
+ for geom in m.geometry.values():
182
+ vertices = np.array(geom.vertices, dtype=np.float32) * scaling
183
+ faces = np.array(geom.faces.flatten(), dtype=np.int32)
184
+ mesh = Mesh(vertices, faces)
185
+ builder.add_shape_mesh(
186
+ body=link,
187
+ pos=wp.vec3(tf.p),
188
+ rot=wp.quat(tf.q),
189
+ mesh=mesh,
190
+ density=density,
191
+ ke=shape_ke,
192
+ kd=shape_kd,
193
+ kf=shape_kf,
194
+ mu=shape_mu,
195
+ restitution=shape_restitution,
196
+ thickness=shape_thickness,
197
+ )
198
+ else:
199
+ # a single mesh
200
+ vertices = np.array(m.vertices, dtype=np.float32) * scaling
201
+ faces = np.array(m.faces.flatten(), dtype=np.int32)
202
+ mesh = Mesh(vertices, faces)
203
+ builder.add_shape_mesh(
204
+ body=link,
205
+ pos=tf.p,
206
+ rot=tf.q,
207
+ mesh=mesh,
208
+ density=density,
209
+ ke=shape_ke,
210
+ kd=shape_kd,
211
+ kf=shape_kf,
212
+ mu=shape_mu,
213
+ restitution=shape_restitution,
214
+ thickness=shape_thickness,
215
+ )
120
216
 
121
217
  # maps from link name -> link index
122
218
  link_index = {}
@@ -125,27 +221,162 @@ def parse_urdf(
125
221
 
126
222
  start_shape_count = len(builder.shape_geo_type)
127
223
 
128
- # import inertial properties from URDF if density is zero
129
- if density == 0.0:
130
- com = urdfpy.matrix_to_xyz_rpy(robot.base_link.inertial.origin)[0:3]
131
- I_m = robot.base_link.inertial.inertia
132
- m = robot.base_link.inertial.mass
133
- else:
134
- com = np.zeros(3)
135
- I_m = np.zeros((3, 3))
136
- m = 0.0
224
+ # add links
225
+ for i, urdf_link in enumerate(root.findall("link")):
226
+ if parse_visuals_as_colliders:
227
+ colliders = urdf_link.findall("visual")
228
+ else:
229
+ colliders = urdf_link.findall("collision")
137
230
 
138
- if parse_visuals_as_colliders:
139
- colliders = robot.links[0].visuals
140
- else:
141
- colliders = robot.links[0].collisions
231
+ name = urdf_link.get("name")
232
+ link = builder.add_body(origin=wp.transform_identity(), armature=armature, name=name)
142
233
 
143
- # add base
144
- if floating:
145
- root = builder.add_body(
146
- origin=wp.transform_identity(), armature=armature, com=com, I_m=I_m, m=m, name=robot.base_link.name
147
- )
234
+ # add ourselves to the index
235
+ link_index[name] = link
236
+
237
+ parse_shapes(link, colliders, density=density)
238
+ m = builder.body_mass[link]
239
+ if not ignore_inertial_definitions and urdf_link.find("inertial") is not None:
240
+ # overwrite inertial parameters if defined
241
+ inertial = urdf_link.find("inertial")
242
+ inertial_frame = parse_transform(inertial)
243
+ com = inertial_frame.p
244
+ I_m = np.zeros((3, 3))
245
+ I_m[0, 0] = float(inertial.find("inertia").get("ixx") or "0") * scale**2
246
+ I_m[1, 1] = float(inertial.find("inertia").get("iyy") or "0") * scale**2
247
+ I_m[2, 2] = float(inertial.find("inertia").get("izz") or "0") * scale**2
248
+ I_m[0, 1] = float(inertial.find("inertia").get("ixy") or "0") * scale**2
249
+ I_m[0, 2] = float(inertial.find("inertia").get("ixz") or "0") * scale**2
250
+ I_m[1, 2] = float(inertial.find("inertia").get("iyz") or "0") * scale**2
251
+ I_m[1, 0] = I_m[0, 1]
252
+ I_m[2, 0] = I_m[0, 2]
253
+ I_m[2, 1] = I_m[1, 2]
254
+ rot = wp.quat_to_matrix(inertial_frame.q)
255
+ I_m = rot @ wp.mat33(I_m)
256
+ m = float(inertial.find("mass").get("value") or "0")
257
+ builder.body_mass[link] = m
258
+ builder.body_inv_mass[link] = 1.0 / m
259
+ builder.body_com[link] = com
260
+ builder.body_inertia[link] = I_m
261
+ builder.body_inv_inertia[link] = wp.inverse(I_m)
262
+ if m == 0.0 and ensure_nonstatic_links:
263
+ # set the mass to something nonzero to ensure the body is dynamic
264
+ m = static_link_mass
265
+ # cube with side length 0.5
266
+ I_m = wp.mat33(np.eye(3)) * m / 12.0 * (0.5 * scale) ** 2 * 2.0
267
+ builder.body_mass[link] = m
268
+ builder.body_inv_mass[link] = 1.0 / m
269
+ builder.body_inertia[link] = I_m
270
+ builder.body_inv_inertia[link] = wp.inverse(I_m)
148
271
 
272
+ end_shape_count = len(builder.shape_geo_type)
273
+
274
+ # find joints per body
275
+ body_children = {name: [] for name in link_index.keys()}
276
+ # mapping from parent, child link names to joint
277
+ parent_child_joint = {}
278
+
279
+ joints = []
280
+ for joint in root.findall("joint"):
281
+ parent = joint.find("parent").get("link")
282
+ child = joint.find("child").get("link")
283
+ body_children[parent].append(child)
284
+ joint_data = {
285
+ "name": joint.get("name"),
286
+ "parent": parent,
287
+ "child": child,
288
+ "type": joint.get("type"),
289
+ "origin": parse_transform(joint),
290
+ "damping": damping,
291
+ "friction": 0.0,
292
+ "limit_lower": -1.0e6,
293
+ "limit_upper": 1.0e6,
294
+ }
295
+ if joint.find("axis") is not None:
296
+ joint_data["axis"] = joint.find("axis").get("xyz")
297
+ joint_data["axis"] = np.array([float(x) for x in joint_data["axis"].split()])
298
+ if joint.find("dynamics") is not None:
299
+ dynamics = joint.find("dynamics")
300
+ joint_data["damping"] = float(dynamics.get("damping") or str(damping))
301
+ joint_data["friction"] = float(dynamics.get("friction") or "0")
302
+ if joint.find("limit") is not None:
303
+ limit = joint.find("limit")
304
+ joint_data["limit_lower"] = float(limit.get("lower") or "-1e6")
305
+ joint_data["limit_upper"] = float(limit.get("upper") or "1e6")
306
+ if joint.find("mimic") is not None:
307
+ mimic = joint.find("mimic")
308
+ joint_data["mimic_joint"] = mimic.get("joint")
309
+ joint_data["mimic_multiplier"] = float(mimic.get("multiplier") or "1")
310
+ joint_data["mimic_offset"] = float(mimic.get("offset") or "0")
311
+
312
+ parent_child_joint[(parent, child)] = joint_data
313
+ joints.append(joint_data)
314
+
315
+ # topological sorting of joints because the FK solver will resolve body transforms
316
+ # in joint order and needs the parent link transform to be resolved before the child
317
+ visited = {name: False for name in link_index.keys()}
318
+ sorted_joints = []
319
+
320
+ # depth-first search
321
+ def dfs(joint):
322
+ link = joint["child"]
323
+ if visited[link]:
324
+ return
325
+ visited[link] = True
326
+
327
+ for child in body_children[link]:
328
+ if not visited[child]:
329
+ dfs(parent_child_joint[(link, child)])
330
+
331
+ sorted_joints.insert(0, joint)
332
+
333
+ # start DFS from each unvisited joint
334
+ for joint in joints:
335
+ if not visited[joint["parent"]]:
336
+ dfs(joint)
337
+
338
+ # add base joint
339
+ if len(sorted_joints) > 0:
340
+ base_link_name = sorted_joints[0]["parent"]
341
+ else:
342
+ base_link_name = next(iter(link_index.keys()))
343
+ root = link_index[base_link_name]
344
+ if base_joint is not None:
345
+ # in case of a given base joint, the position is applied first, the rotation only
346
+ # after the base joint itself to not rotate its axis
347
+ base_parent_xform = wp.transform(xform.p, wp.quat_identity())
348
+ base_child_xform = wp.transform((0.0, 0.0, 0.0), wp.quat_inverse(xform.q))
349
+ if isinstance(base_joint, str):
350
+ axes = base_joint.lower().split(",")
351
+ axes = [ax.strip() for ax in axes]
352
+ linear_axes = [ax[-1] for ax in axes if ax[0] in {"l", "p"}]
353
+ angular_axes = [ax[-1] for ax in axes if ax[0] in {"a", "r"}]
354
+ axes = {
355
+ "x": [1.0, 0.0, 0.0],
356
+ "y": [0.0, 1.0, 0.0],
357
+ "z": [0.0, 0.0, 1.0],
358
+ }
359
+ builder.add_joint_d6(
360
+ linear_axes=[wp.sim.JointAxis(axes[a]) for a in linear_axes],
361
+ angular_axes=[wp.sim.JointAxis(axes[a]) for a in angular_axes],
362
+ parent_xform=base_parent_xform,
363
+ child_xform=base_child_xform,
364
+ parent=-1,
365
+ child=root,
366
+ name="base_joint",
367
+ )
368
+ elif isinstance(base_joint, dict):
369
+ base_joint["parent"] = -1
370
+ base_joint["child"] = root
371
+ base_joint["parent_xform"] = base_parent_xform
372
+ base_joint["child_xform"] = base_child_xform
373
+ base_joint["name"] = "base_joint"
374
+ builder.add_joint(**base_joint)
375
+ else:
376
+ raise ValueError(
377
+ "base_joint must be a comma-separated string of joint axes or a dict with joint parameters"
378
+ )
379
+ elif floating:
149
380
  builder.add_joint_free(root, name="floating_base")
150
381
 
151
382
  # set dofs to transform
@@ -159,148 +390,96 @@ def parse_urdf(
159
390
  builder.joint_q[start + 4] = xform.q[1]
160
391
  builder.joint_q[start + 5] = xform.q[2]
161
392
  builder.joint_q[start + 6] = xform.q[3]
162
- # make sure we do not reset inertia to zero if density is zero where we use the inertia from the URDF
163
- actual_density = 1.0 if m > 0.0 and density == 0.0 else density
164
- urdf_add_collision(
165
- builder, root, colliders, actual_density, shape_ke, shape_kd, shape_kf, shape_mu, shape_restitution
166
- )
167
-
168
393
  else:
169
- root = builder.add_body(origin=wp.transform_identity(), name=robot.base_link.name)
170
394
  builder.add_joint_fixed(-1, root, parent_xform=xform, name="fixed_base")
171
- urdf_add_collision(builder, root, colliders, 0.0, shape_ke, shape_kd, shape_kf, shape_mu, shape_restitution)
172
-
173
- link_index[robot.links[0].name] = root
174
-
175
- # add children
176
- for joint in robot.joints:
177
- parent = root
178
- if joint.parent in link_index:
179
- parent = link_index[joint.parent]
180
-
181
- origin = urdfpy.matrix_to_xyz_rpy(joint.origin)
182
- pos = origin[0:3]
183
- rot = wp.quat_rpy(*origin[3:6])
184
-
185
- lower = -1.0e3
186
- upper = 1.0e3
187
- joint_damping = damping
188
-
189
- # limits
190
- if joint.limit:
191
- if joint.limit.lower != None:
192
- lower = joint.limit.lower
193
- if joint.limit.upper != None:
194
- upper = joint.limit.upper
195
-
196
- # overwrite damping if defined in URDF
197
- if joint.dynamics:
198
- if joint.dynamics.damping:
199
- joint_damping = joint.dynamics.damping
200
-
201
- if density == 0.0:
202
- com = urdfpy.matrix_to_xyz_rpy(robot.link_map[joint.child].inertial.origin)[0:3]
203
- I_m = robot.link_map[joint.child].inertial.inertia
204
- m = robot.link_map[joint.child].inertial.mass
205
- else:
206
- com = np.zeros(3)
207
- I_m = np.zeros((3, 3))
208
- m = 0.0
209
395
 
210
- # add link
211
- link = builder.add_body(
212
- origin=wp.transform_identity(), armature=armature, com=com, I_m=I_m, m=m, name=joint.parent
213
- )
396
+ # add joints, in topological order starting from root body
397
+ for joint in sorted_joints:
398
+ parent = link_index[joint["parent"]]
399
+ child = link_index[joint["child"]]
400
+ if child == -1:
401
+ # we skipped the insertion of the child body
402
+ continue
403
+
404
+ lower = joint["limit_lower"]
405
+ upper = joint["limit_upper"]
406
+ joint_damping = joint["damping"]
214
407
 
215
- parent_xform = wp.transform(pos, rot)
408
+ parent_xform = joint["origin"]
216
409
  child_xform = wp.transform_identity()
217
410
 
218
411
  joint_mode = wp.sim.JOINT_MODE_LIMIT
219
412
  if stiffness > 0.0:
220
413
  joint_mode = wp.sim.JOINT_MODE_TARGET_POSITION
221
414
 
222
- if joint.joint_type == "revolute" or joint.joint_type == "continuous":
415
+ joint_params = dict(
416
+ parent=parent,
417
+ child=child,
418
+ parent_xform=parent_xform,
419
+ child_xform=child_xform,
420
+ name=joint["name"],
421
+ )
422
+
423
+ if joint["type"] == "revolute" or joint["type"] == "continuous":
223
424
  builder.add_joint_revolute(
224
- parent,
225
- link,
226
- parent_xform,
227
- child_xform,
228
- joint.axis,
425
+ axis=joint["axis"],
229
426
  target_ke=stiffness,
230
427
  target_kd=joint_damping,
231
428
  limit_lower=lower,
232
429
  limit_upper=upper,
233
430
  limit_ke=limit_ke,
234
431
  limit_kd=limit_kd,
235
- name=joint.name,
236
432
  mode=joint_mode,
433
+ **joint_params,
237
434
  )
238
- elif joint.joint_type == "prismatic":
435
+ elif joint["type"] == "prismatic":
239
436
  builder.add_joint_prismatic(
240
- parent,
241
- link,
242
- parent_xform,
243
- child_xform,
244
- joint.axis,
437
+ axis=joint["axis"],
245
438
  target_ke=stiffness,
246
439
  target_kd=joint_damping,
247
- limit_lower=lower,
248
- limit_upper=upper,
440
+ limit_lower=lower * scale,
441
+ limit_upper=upper * scale,
249
442
  limit_ke=limit_ke,
250
443
  limit_kd=limit_kd,
251
- name=joint.name,
252
444
  mode=joint_mode,
445
+ **joint_params,
253
446
  )
254
- elif joint.joint_type == "fixed":
255
- builder.add_joint_fixed(parent, link, parent_xform, child_xform, name=joint.name)
256
- elif joint.joint_type == "floating":
257
- builder.add_joint_free(parent, link, parent_xform, child_xform, name=joint.name)
258
- elif joint.joint_type == "planar":
447
+ elif joint["type"] == "fixed":
448
+ builder.add_joint_fixed(**joint_params)
449
+ elif joint["type"] == "floating":
450
+ builder.add_joint_free(**joint_params)
451
+ elif joint["type"] == "planar":
259
452
  # find plane vectors perpendicular to axis
260
- axis = np.array(joint.axis)
453
+ axis = np.array(joint["axis"])
261
454
  axis /= np.linalg.norm(axis)
262
- if abs(axis[0]) > 0.1:
263
- orthogonal_vector = np.array([1, 0, 0])
264
- elif abs(axis[1]) > 0.1:
265
- orthogonal_vector = np.array([0, 1, 0])
266
- else:
267
- orthogonal_vector = np.array([0, 0, 1])
268
- plane_vector1 = np.cross(axis, orthogonal_vector)
269
- plane_vector2 = np.cross(axis, plane_vector1)
455
+
456
+ # create helper vector that is not parallel to the axis
457
+ helper = np.array([1, 0, 0]) if np.allclose(axis, [0, 1, 0]) else np.array([0, 1, 0])
458
+
459
+ u = np.cross(helper, axis)
460
+ u /= np.linalg.norm(u)
461
+
462
+ v = np.cross(axis, u)
463
+ v /= np.linalg.norm(v)
464
+
270
465
  builder.add_joint_d6(
271
- parent,
272
- link,
273
466
  linear_axes=[
274
467
  wp.sim.JointAxis(
275
- plane_vector1, limit_lower=lower, limit_upper=upper, limit_ke=limit_ke, limit_kd=limit_kd
468
+ u, limit_lower=lower * scale, limit_upper=upper * scale, limit_ke=limit_ke, limit_kd=limit_kd
276
469
  ),
277
470
  wp.sim.JointAxis(
278
- plane_vector2, limit_lower=lower, limit_upper=upper, limit_ke=limit_ke, limit_kd=limit_kd
471
+ v, limit_lower=lower * scale, limit_upper=upper * scale, limit_ke=limit_ke, limit_kd=limit_kd
279
472
  ),
280
473
  ],
281
- parent_xform=parent_xform,
282
- child_xform=child_xform,
283
- name=joint.name,
474
+ **joint_params,
284
475
  )
285
476
  else:
286
- raise Exception("Unsupported joint type: " + joint.joint_type)
287
-
288
- if parse_visuals_as_colliders:
289
- child_colliders = robot.link_map[joint.child].visuals
290
- else:
291
- child_colliders = robot.link_map[joint.child].collisions
292
-
293
- # add collisions
294
- urdf_add_collision(
295
- builder, link, child_colliders, density, shape_ke, shape_kd, shape_kf, shape_mu, shape_restitution
296
- )
297
-
298
- # add ourselves to the index
299
- link_index[joint.child] = link
300
-
301
- end_shape_count = len(builder.shape_geo_type)
477
+ raise Exception("Unsupported joint type: " + joint["type"])
302
478
 
303
479
  if not enable_self_collisions:
304
480
  for i in range(start_shape_count, end_shape_count):
305
481
  for j in range(i + 1, end_shape_count):
306
482
  builder.shape_collision_filter_pairs.add((i, j))
483
+
484
+ if collapse_fixed_joints:
485
+ builder.collapse_fixed_joints()