warp-lang 0.15.0__py3-none-manylinux2014_x86_64.whl → 1.0.0__py3-none-manylinux2014_x86_64.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 (80) hide show
  1. warp/__init__.py +1 -0
  2. warp/codegen.py +7 -3
  3. warp/config.py +2 -1
  4. warp/constants.py +3 -0
  5. warp/context.py +44 -21
  6. warp/examples/assets/bunny.usd +0 -0
  7. warp/examples/assets/cartpole.urdf +110 -0
  8. warp/examples/assets/crazyflie.usd +0 -0
  9. warp/examples/assets/cube.usda +42 -0
  10. warp/examples/assets/nv_ant.xml +92 -0
  11. warp/examples/assets/nv_humanoid.xml +183 -0
  12. warp/examples/assets/quadruped.urdf +268 -0
  13. warp/examples/assets/rocks.nvdb +0 -0
  14. warp/examples/assets/rocks.usd +0 -0
  15. warp/examples/assets/sphere.usda +56 -0
  16. warp/examples/assets/torus.usda +105 -0
  17. warp/examples/core/example_dem.py +6 -6
  18. warp/examples/core/example_fluid.py +3 -3
  19. warp/examples/core/example_graph_capture.py +3 -6
  20. warp/examples/optim/example_bounce.py +9 -8
  21. warp/examples/optim/example_cloth_throw.py +12 -8
  22. warp/examples/optim/example_diffray.py +10 -12
  23. warp/examples/optim/example_drone.py +31 -14
  24. warp/examples/optim/example_spring_cage.py +10 -15
  25. warp/examples/optim/example_trajectory.py +7 -24
  26. warp/examples/sim/example_cartpole.py +3 -9
  27. warp/examples/sim/example_cloth.py +10 -10
  28. warp/examples/sim/example_granular.py +3 -3
  29. warp/examples/sim/example_granular_collision_sdf.py +9 -4
  30. warp/examples/sim/example_jacobian_ik.py +0 -10
  31. warp/examples/sim/example_particle_chain.py +4 -4
  32. warp/examples/sim/example_quadruped.py +15 -11
  33. warp/examples/sim/example_rigid_chain.py +13 -8
  34. warp/examples/sim/example_rigid_contact.py +4 -4
  35. warp/examples/sim/example_rigid_force.py +7 -7
  36. warp/examples/sim/example_rigid_soft_contact.py +4 -4
  37. warp/examples/sim/example_soft_body.py +3 -3
  38. warp/jax.py +45 -0
  39. warp/jax_experimental.py +339 -0
  40. warp/render/render_opengl.py +188 -95
  41. warp/render/render_usd.py +34 -10
  42. warp/sim/__init__.py +13 -4
  43. warp/sim/articulation.py +4 -5
  44. warp/sim/collide.py +320 -175
  45. warp/sim/import_mjcf.py +25 -30
  46. warp/sim/import_urdf.py +94 -63
  47. warp/sim/import_usd.py +51 -36
  48. warp/sim/inertia.py +3 -2
  49. warp/sim/integrator.py +233 -0
  50. warp/sim/integrator_euler.py +447 -469
  51. warp/sim/integrator_featherstone.py +1991 -0
  52. warp/sim/integrator_xpbd.py +1420 -640
  53. warp/sim/model.py +741 -487
  54. warp/sim/particles.py +2 -1
  55. warp/sim/render.py +18 -2
  56. warp/sim/utils.py +222 -11
  57. warp/stubs.py +1 -0
  58. warp/tape.py +6 -9
  59. warp/tests/test_examples.py +87 -20
  60. warp/tests/test_grad_customs.py +122 -0
  61. warp/tests/test_jax.py +254 -0
  62. warp/tests/test_options.py +13 -53
  63. warp/tests/test_quat.py +23 -0
  64. warp/tests/test_snippet.py +2 -0
  65. warp/tests/test_utils.py +31 -26
  66. warp/tests/test_verify_fp.py +65 -0
  67. warp/tests/unittest_suites.py +4 -0
  68. warp/utils.py +50 -1
  69. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/METADATA +1 -1
  70. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/RECORD +73 -64
  71. warp/examples/env/__init__.py +0 -0
  72. warp/examples/env/env_ant.py +0 -61
  73. warp/examples/env/env_cartpole.py +0 -63
  74. warp/examples/env/env_humanoid.py +0 -65
  75. warp/examples/env/env_usd.py +0 -97
  76. warp/examples/env/environment.py +0 -526
  77. warp/sim/optimizer.py +0 -138
  78. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/LICENSE.md +0 -0
  79. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/WHEEL +0 -0
  80. {warp_lang-0.15.0.dist-info → warp_lang-1.0.0.dist-info}/top_level.txt +0 -0
@@ -6,15 +6,18 @@
6
6
  # license agreement from NVIDIA CORPORATION is strictly prohibited.
7
7
 
8
8
  import warp as wp
9
+ from .integrator import Integrator
9
10
  from .model import (
11
+ Model,
12
+ State,
13
+ Control,
10
14
  PARTICLE_FLAG_ACTIVE,
11
15
  ModelShapeMaterials,
12
16
  JOINT_MODE_TARGET_POSITION,
13
17
  JOINT_MODE_TARGET_VELOCITY,
14
- JOINT_MODE_LIMIT,
18
+ JOINT_MODE_FORCE,
15
19
  )
16
- from .utils import velocity_at_point, vec_min, vec_max, vec_abs
17
- from .integrator_euler import integrate_bodies, integrate_particles
20
+ from .utils import velocity_at_point, vec_min, vec_max, vec_abs, vec_leaky_min, vec_leaky_max
18
21
 
19
22
 
20
23
  @wp.kernel
@@ -61,18 +64,106 @@ def solve_particle_ground_contacts(
61
64
  lambda_f = wp.max(mu * lambda_n, 0.0 - wp.length(vt) * dt)
62
65
  delta_f = wp.normalize(vt) * lambda_f
63
66
 
64
- wp.atomic_add(delta, tid, (delta_f - delta_n) / wi * relaxation)
67
+ wp.atomic_add(delta, tid, (delta_f - delta_n) * relaxation)
65
68
 
66
69
 
67
70
  @wp.kernel
68
- def apply_soft_restitution_ground(
71
+ def apply_particle_shape_restitution(
69
72
  particle_x_new: wp.array(dtype=wp.vec3),
70
73
  particle_v_new: wp.array(dtype=wp.vec3),
71
74
  particle_x_old: wp.array(dtype=wp.vec3),
72
75
  particle_v_old: wp.array(dtype=wp.vec3),
73
- invmass: wp.array(dtype=float),
76
+ particle_invmass: wp.array(dtype=float),
77
+ particle_radius: wp.array(dtype=float),
78
+ particle_flags: wp.array(dtype=wp.uint32),
79
+ body_q: wp.array(dtype=wp.transform),
80
+ body_qd: wp.array(dtype=wp.spatial_vector),
81
+ body_com: wp.array(dtype=wp.vec3),
82
+ body_m_inv: wp.array(dtype=float),
83
+ body_I_inv: wp.array(dtype=wp.mat33),
84
+ shape_body: wp.array(dtype=int),
85
+ shape_materials: ModelShapeMaterials,
86
+ particle_ka: float,
87
+ restitution: float,
88
+ contact_count: wp.array(dtype=int),
89
+ contact_particle: wp.array(dtype=int),
90
+ contact_shape: wp.array(dtype=int),
91
+ contact_body_pos: wp.array(dtype=wp.vec3),
92
+ contact_body_vel: wp.array(dtype=wp.vec3),
93
+ contact_normal: wp.array(dtype=wp.vec3),
94
+ contact_max: int,
95
+ dt: float,
96
+ relaxation: float,
97
+ particle_v_out: wp.array(dtype=wp.vec3),
98
+ ):
99
+ tid = wp.tid()
100
+
101
+ count = min(contact_max, contact_count[0])
102
+ if tid >= count:
103
+ return
104
+
105
+ shape_index = contact_shape[tid]
106
+ body_index = shape_body[shape_index]
107
+ particle_index = contact_particle[tid]
108
+
109
+ if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
110
+ return
111
+
112
+ # x_new = particle_x_new[particle_index]
113
+ v_new = particle_v_new[particle_index]
114
+ px = particle_x_old[particle_index]
115
+ v_old = particle_v_old[particle_index]
116
+
117
+ X_wb = wp.transform_identity()
118
+ X_com = wp.vec3()
119
+
120
+ if body_index >= 0:
121
+ X_wb = body_q[body_index]
122
+ X_com = body_com[body_index]
123
+
124
+ # body position in world space
125
+ bx = wp.transform_point(X_wb, contact_body_pos[tid])
126
+ r = bx - wp.transform_point(X_wb, X_com)
127
+
128
+ n = contact_normal[tid]
129
+ c = wp.dot(n, px - bx) - particle_radius[particle_index]
130
+
131
+ if c > particle_ka:
132
+ return
133
+
134
+ rel_vel_old = wp.dot(n, v_old)
135
+ rel_vel_new = wp.dot(n, v_new)
136
+
137
+ if rel_vel_old < 0.0:
138
+ # dv = -n * wp.max(-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0), 0.0)
139
+ dv = n * (-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0))
140
+
141
+ # compute inverse masses
142
+ # w1 = particle_invmass[particle_index]
143
+ # w2 = 0.0
144
+ # if body_index >= 0:
145
+ # angular = wp.cross(r, n)
146
+ # q = wp.transform_get_rotation(X_wb)
147
+ # rot_angular = wp.quat_rotate_inv(q, angular)
148
+ # I_inv = body_I_inv[body_index]
149
+ # w2 = body_m_inv[body_index] + wp.dot(rot_angular, I_inv * rot_angular)
150
+ # denom = w1 + w2
151
+ # if denom == 0.0:
152
+ # return
153
+
154
+ wp.atomic_add(particle_v_out, tid, dv)
155
+
156
+
157
+ @wp.kernel
158
+ def apply_particle_ground_restitution(
159
+ particle_x_new: wp.array(dtype=wp.vec3),
160
+ particle_v_new: wp.array(dtype=wp.vec3),
161
+ particle_x_old: wp.array(dtype=wp.vec3),
162
+ particle_v_old: wp.array(dtype=wp.vec3),
163
+ particle_invmass: wp.array(dtype=float),
74
164
  particle_radius: wp.array(dtype=float),
75
165
  particle_flags: wp.array(dtype=wp.uint32),
166
+ particle_ka: float,
76
167
  restitution: float,
77
168
  ground: wp.array(dtype=float),
78
169
  dt: float,
@@ -83,26 +174,26 @@ def apply_soft_restitution_ground(
83
174
  if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
84
175
  return
85
176
 
86
- wi = invmass[tid]
177
+ wi = particle_invmass[tid]
87
178
  if wi == 0.0:
88
179
  return
89
180
 
90
- # x_new = particle_x_new[tid]
91
- v_new = particle_v_new[tid]
92
- x_old = particle_x_old[tid]
181
+ x = particle_x_old[tid]
93
182
  v_old = particle_v_old[tid]
183
+ v_new = particle_v_new[tid]
94
184
 
95
185
  n = wp.vec3(ground[0], ground[1], ground[2])
96
- c = wp.dot(n, x_old) + ground[3] - particle_radius[tid]
186
+ c = wp.dot(n, x) + ground[3] - particle_radius[tid]
97
187
 
98
- if c > 0.0:
188
+ if c > particle_ka:
99
189
  return
100
190
 
101
- rel_vel_old = wp.dot(n, v_old)
102
- rel_vel_new = wp.dot(n, v_new)
103
- dv = n * wp.max(-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0), 0.0)
191
+ vn = wp.dot(n, v_old)
192
+ vn_new = wp.dot(n, v_new)
104
193
 
105
- wp.atomic_add(particle_v_out, tid, dv / wi * relaxation)
194
+ if vn < 0.0:
195
+ dv = n * (-vn_new + wp.max(-restitution * vn, 0.0))
196
+ wp.atomic_add(particle_v_out, tid, dv)
106
197
 
107
198
 
108
199
  @wp.kernel
@@ -209,7 +300,7 @@ def solve_particle_shape_contacts(
209
300
  delta_f = wp.normalize(vt) * lambda_f
210
301
  delta_total = (delta_f - delta_n) / denom * relaxation
211
302
 
212
- wp.atomic_add(delta, particle_index, delta_total)
303
+ wp.atomic_add(delta, particle_index, w1 * delta_total)
213
304
 
214
305
  if body_index >= 0:
215
306
  delta_t = wp.cross(r, delta_total)
@@ -280,7 +371,7 @@ def solve_particle_particle_contacts(
280
371
  delta_f = wp.normalize(vt) * lambda_f
281
372
  delta += (delta_f - delta_n) / denom
282
373
 
283
- wp.atomic_add(deltas, i, delta * relaxation)
374
+ wp.atomic_add(deltas, i, delta * w1 * relaxation)
284
375
 
285
376
 
286
377
  @wp.kernel
@@ -334,11 +425,11 @@ def solve_springs(
334
425
  if denom <= 0.0 or ke <= 0.0 or kd < 0.0:
335
426
  return
336
427
 
337
- alpha= 1.0 / (ke * dt * dt)
428
+ alpha = 1.0 / (ke * dt * dt)
338
429
  gamma = kd / (ke * dt)
339
430
 
340
- 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...
341
- dlambda = -1.0 * (c + alpha* lambdas[tid] + gamma * grad_c_dot_v) / ((1.0 + gamma) * denom + alpha)
431
+ 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...
432
+ dlambda = -1.0 * (c + alpha * lambdas[tid] + gamma * grad_c_dot_v) / ((1.0 + gamma) * denom + alpha)
342
433
 
343
434
  dxi = wi * dlambda * grad_c_xi
344
435
  dxj = wj * dlambda * grad_c_xj
@@ -420,7 +511,12 @@ def bending_constraint(
420
511
  grad_x3 = (n1 * wp.dot(x1 - x4, e_hat) + n2 * wp.dot(x2 - x4, e_hat)) * derivative_flip
421
512
  grad_x4 = (n1 * wp.dot(x3 - x1, e_hat) + n2 * wp.dot(x3 - x2, e_hat)) * derivative_flip
422
513
  c = angle - rest_angle
423
- denominator = (w1 * wp.length_sq(grad_x1) + w2 * wp.length_sq(grad_x2) + w3 * wp.length_sq(grad_x3) + w4 * wp.length_sq(grad_x4))
514
+ denominator = (
515
+ w1 * wp.length_sq(grad_x1)
516
+ + w2 * wp.length_sq(grad_x2)
517
+ + w3 * wp.length_sq(grad_x3)
518
+ + w4 * wp.length_sq(grad_x4)
519
+ )
424
520
 
425
521
  # Note strict inequality for damping -- 0 damping is ok
426
522
  if denominator <= 0.0 or ke <= 0.0 or kd < 0.0:
@@ -448,6 +544,184 @@ def bending_constraint(
448
544
 
449
545
  @wp.kernel
450
546
  def solve_tetrahedra(
547
+ x: wp.array(dtype=wp.vec3),
548
+ v: wp.array(dtype=wp.vec3),
549
+ inv_mass: wp.array(dtype=float),
550
+ indices: wp.array(dtype=int, ndim=2),
551
+ rest_matrix: wp.array(dtype=wp.mat33),
552
+ activation: wp.array(dtype=float),
553
+ materials: wp.array(dtype=float, ndim=2),
554
+ dt: float,
555
+ relaxation: float,
556
+ delta: wp.array(dtype=wp.vec3),
557
+ ):
558
+ tid = wp.tid()
559
+
560
+ i = indices[tid, 0]
561
+ j = indices[tid, 1]
562
+ k = indices[tid, 2]
563
+ l = indices[tid, 3]
564
+
565
+ act = activation[tid]
566
+
567
+ k_mu = materials[tid, 0]
568
+ k_lambda = materials[tid, 1]
569
+ k_damp = materials[tid, 2]
570
+
571
+ x0 = x[i]
572
+ x1 = x[j]
573
+ x2 = x[k]
574
+ x3 = x[l]
575
+
576
+ v0 = v[i]
577
+ v1 = v[j]
578
+ v2 = v[k]
579
+ v3 = v[l]
580
+
581
+ w0 = inv_mass[i]
582
+ w1 = inv_mass[j]
583
+ w2 = inv_mass[k]
584
+ w3 = inv_mass[l]
585
+
586
+ x10 = x1 - x0
587
+ x20 = x2 - x0
588
+ x30 = x3 - x0
589
+
590
+ v10 = v1 - v0
591
+ v20 = v2 - v0
592
+ v30 = v3 - v0
593
+
594
+ Ds = wp.mat33(x10, x20, x30)
595
+ Dm = rest_matrix[tid]
596
+ inv_QT = wp.transpose(Dm)
597
+
598
+ inv_rest_volume = wp.determinant(Dm) * 6.0
599
+ rest_volume = 1.0 / inv_rest_volume
600
+
601
+ # F = Xs*Xm^-1
602
+ F = Ds * Dm
603
+
604
+ f1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
605
+ f2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
606
+ f3 = wp.vec3(F[0, 2], F[1, 2], F[2, 2])
607
+
608
+ tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
609
+
610
+ C = float(0.0)
611
+ dC = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
612
+ compliance = float(0.0)
613
+
614
+ stretching_compliance = relaxation
615
+ volume_compliance = relaxation
616
+
617
+ num_terms = 2
618
+ for term in range(0, num_terms):
619
+
620
+ if term == 0:
621
+ # deviatoric, stable
622
+ C = tr - 3.0
623
+ dC = F * 2.0
624
+ compliance = stretching_compliance
625
+ elif term == 1:
626
+ # volume conservation
627
+ C = wp.determinant(F) - 1.0
628
+ dC = wp.mat33(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))
629
+ compliance = volume_compliance
630
+
631
+ if C != 0.0:
632
+
633
+ dP = dC * inv_QT
634
+ grad1 = wp.vec3(dP[0][0], dP[1][0], dP[2][0])
635
+ grad2 = wp.vec3(dP[0][1], dP[1][1], dP[2][1])
636
+ grad3 = wp.vec3(dP[0][2], dP[1][2], dP[2][2])
637
+ grad0 = -grad1 - grad2 - grad3
638
+
639
+ w = (
640
+ wp.dot(grad0, grad0) * w0
641
+ + wp.dot(grad1, grad1) * w1
642
+ + wp.dot(grad2, grad2) * w2
643
+ + wp.dot(grad3, grad3) * w3
644
+ )
645
+
646
+ if w > 0.0:
647
+ alpha = compliance / dt / dt
648
+ if inv_rest_volume > 0.0:
649
+ alpha *= inv_rest_volume
650
+ dlambda = -C / (w + alpha)
651
+
652
+ wp.atomic_add(delta, i, w0 * dlambda * grad0)
653
+ wp.atomic_add(delta, j, w1 * dlambda * grad1)
654
+ wp.atomic_add(delta, k, w2 * dlambda * grad2)
655
+ wp.atomic_add(delta, l, w3 * dlambda * grad3)
656
+ # wp.atomic_add(particle.num_corr, id0, 1)
657
+ # wp.atomic_add(particle.num_corr, id1, 1)
658
+ # wp.atomic_add(particle.num_corr, id2, 1)
659
+ # wp.atomic_add(particle.num_corr, id3, 1)
660
+
661
+ # C_Spherical
662
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
663
+ # r_s_inv = 1.0/r_s
664
+ # C = r_s - wp.sqrt(3.0)
665
+ # dCdx = F*wp.transpose(Dm)*r_s_inv
666
+ # alpha = 1.0
667
+
668
+ # C_D
669
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
670
+ # C = r_s*r_s - 3.0
671
+ # dCdx = F*wp.transpose(Dm)*2.0
672
+ # alpha = 1.0
673
+
674
+ # grad1 = wp.vec3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0])
675
+ # grad2 = wp.vec3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1])
676
+ # grad3 = wp.vec3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2])
677
+ # grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0)
678
+
679
+ # denom = (
680
+ # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
681
+ # )
682
+ # multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume))
683
+
684
+ # delta0 = grad0 * multiplier
685
+ # delta1 = grad1 * multiplier
686
+ # delta2 = grad2 * multiplier
687
+ # delta3 = grad3 * multiplier
688
+
689
+ # # hydrostatic part
690
+ # J = wp.determinant(F)
691
+
692
+ # C_vol = J - alpha
693
+ # # dCdx = wp.mat33(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))*wp.transpose(Dm)
694
+
695
+ # # grad1 = wp.vec3(dCdx[0,0], dCdx[1,0], dCdx[2,0])
696
+ # # grad2 = wp.vec3(dCdx[0,1], dCdx[1,1], dCdx[2,1])
697
+ # # grad3 = wp.vec3(dCdx[0,2], dCdx[1,2], dCdx[2,2])
698
+ # # grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0)
699
+
700
+ # s = inv_rest_volume / 6.0
701
+ # grad1 = wp.cross(x20, x30) * s
702
+ # grad2 = wp.cross(x30, x10) * s
703
+ # grad3 = wp.cross(x10, x20) * s
704
+ # grad0 = -(grad1 + grad2 + grad3)
705
+
706
+ # denom = (
707
+ # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
708
+ # )
709
+ # multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume))
710
+
711
+ # delta0 += grad0 * multiplier
712
+ # delta1 += grad1 * multiplier
713
+ # delta2 += grad2 * multiplier
714
+ # delta3 += grad3 * multiplier
715
+
716
+ # # # apply forces
717
+ # # wp.atomic_sub(delta, i, delta0 * w0 * relaxation)
718
+ # # wp.atomic_sub(delta, j, delta1 * w1 * relaxation)
719
+ # # wp.atomic_sub(delta, k, delta2 * w2 * relaxation)
720
+ # # wp.atomic_sub(delta, l, delta3 * w3 * relaxation)
721
+
722
+
723
+ @wp.kernel
724
+ def solve_tetrahedra2(
451
725
  x: wp.array(dtype=wp.vec3),
452
726
  v: wp.array(dtype=wp.vec3),
453
727
  inv_mass: wp.array(dtype=float),
@@ -660,21 +934,22 @@ def apply_body_deltas(
660
934
 
661
935
  weight = 1.0
662
936
  if constraint_inv_weights:
663
- if constraint_inv_weights[tid] > 0.0:
664
- weight = 1.0 / constraint_inv_weights[tid]
937
+ inv_weight = constraint_inv_weights[tid]
938
+ if inv_weight > 0.0:
939
+ weight = 1.0 / inv_weight
665
940
 
666
941
  dp = wp.spatial_bottom(delta) * (inv_m * weight)
667
942
  dq = wp.spatial_top(delta) * weight
668
943
  dq = wp.quat_rotate(q0, inv_I * wp.quat_rotate_inv(q0, dq))
669
944
 
670
945
  # update orientation
671
- q1 = q0 + 0.5 * wp.quat(dq * dt * dt, 0.0) * q0
946
+ q1 = q0 + 0.5 * wp.quat(dq * dt, 0.0) * q0
672
947
  q1 = wp.normalize(q1)
673
948
 
674
949
  # update position
675
950
  com = body_com[tid]
676
951
  x_com = p0 + wp.quat_rotate(q0, com)
677
- p1 = x_com + dp * dt * dt
952
+ p1 = x_com + dp * dt
678
953
  p1 -= wp.quat_rotate(q1, com)
679
954
 
680
955
  q_out[tid] = wp.transform(p1, q1)
@@ -683,23 +958,28 @@ def apply_body_deltas(
683
958
  w0 = wp.spatial_top(qd_in[tid])
684
959
 
685
960
  # update linear and angular velocity
686
- v1 = v0 + dp * dt
961
+ v1 = v0 + dp
687
962
  # angular part (compute in body frame)
688
- wb = wp.quat_rotate_inv(q0, w0 + dq * dt)
963
+ wb = wp.quat_rotate_inv(q0, w0 + dq)
689
964
  tb = -wp.cross(wb, body_I[tid] * wb) # coriolis forces
690
965
  w1 = wp.quat_rotate(q0, wb + inv_I * tb * dt)
691
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
+
692
973
  qd_out[tid] = wp.spatial_vector(w1, v1)
693
974
 
694
975
 
695
976
  @wp.kernel
696
977
  def apply_body_delta_velocities(
697
- qd_in: wp.array(dtype=wp.spatial_vector),
698
978
  deltas: wp.array(dtype=wp.spatial_vector),
699
979
  qd_out: wp.array(dtype=wp.spatial_vector),
700
980
  ):
701
981
  tid = wp.tid()
702
- qd_out[tid] = qd_in[tid] + deltas[tid]
982
+ wp.atomic_add(qd_out, tid, deltas[tid])
703
983
 
704
984
 
705
985
  @wp.kernel
@@ -716,6 +996,7 @@ def apply_joint_torques(
716
996
  joint_axis_start: wp.array(dtype=int),
717
997
  joint_axis_dim: wp.array(dtype=int, ndim=2),
718
998
  joint_axis: wp.array(dtype=wp.vec3),
999
+ joint_axis_mode: wp.array(dtype=int),
719
1000
  joint_act: wp.array(dtype=float),
720
1001
  body_f: wp.array(dtype=wp.spatial_vector),
721
1002
  ):
@@ -753,9 +1034,9 @@ def apply_joint_torques(
753
1034
  com_c = body_com[id_c]
754
1035
  r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
755
1036
 
756
- # local joint rotations
757
- q_p = wp.transform_get_rotation(X_wp)
758
- q_c = wp.transform_get_rotation(X_wc)
1037
+ # # local joint rotations
1038
+ # q_p = wp.transform_get_rotation(X_wp)
1039
+ # q_c = wp.transform_get_rotation(X_wc)
759
1040
 
760
1041
  # joint properties (for 1D joints)
761
1042
  q_start = joint_q_start[tid]
@@ -770,15 +1051,19 @@ def apply_joint_torques(
770
1051
 
771
1052
  # handle angular constraints
772
1053
  if type == wp.sim.JOINT_REVOLUTE:
773
- axis = joint_axis[axis_start]
774
- act = joint_act[qd_start]
775
- a_p = wp.transform_vector(X_wp, axis)
776
- t_total += act * a_p
1054
+ mode = joint_axis_mode[axis_start]
1055
+ if mode == wp.sim.JOINT_MODE_FORCE:
1056
+ axis = joint_axis[axis_start]
1057
+ act = joint_act[qd_start]
1058
+ a_p = wp.transform_vector(X_wp, axis)
1059
+ t_total += act * a_p
777
1060
  elif type == wp.sim.JOINT_PRISMATIC:
778
- axis = joint_axis[axis_start]
779
- act = joint_act[qd_start]
780
- a_p = wp.transform_vector(X_wp, axis)
781
- f_total += act * a_p
1061
+ mode = joint_axis_mode[axis_start]
1062
+ if mode == wp.sim.JOINT_MODE_FORCE:
1063
+ axis = joint_axis[axis_start]
1064
+ act = joint_act[qd_start]
1065
+ a_p = wp.transform_vector(X_wp, axis)
1066
+ f_total += act * a_p
782
1067
  elif type == wp.sim.JOINT_COMPOUND:
783
1068
  # q_off = wp.transform_get_rotation(X_cj)
784
1069
  # q_pc = wp.quat_inverse(q_off)*wp.quat_inverse(q_p)*q_c*q_off
@@ -799,12 +1084,15 @@ def apply_joint_torques(
799
1084
  # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
800
1085
  # t_total += joint_act[qd_start+2] * wp.quat_rotate(q_w, axis_2)
801
1086
 
802
- axis_0 = joint_axis[axis_start + 0]
803
- axis_1 = joint_axis[axis_start + 1]
804
- axis_2 = joint_axis[axis_start + 2]
805
- t_total += joint_act[qd_start + 0] * wp.transform_vector(X_wp, axis_0)
806
- t_total += joint_act[qd_start + 1] * wp.transform_vector(X_wp, axis_1)
807
- t_total += joint_act[qd_start + 2] * wp.transform_vector(X_wp, axis_2)
1087
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1088
+ axis_0 = joint_axis[axis_start + 0]
1089
+ t_total += joint_act[qd_start + 0] * wp.transform_vector(X_wp, axis_0)
1090
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1091
+ axis_1 = joint_axis[axis_start + 1]
1092
+ t_total += joint_act[qd_start + 1] * wp.transform_vector(X_wp, axis_1)
1093
+ if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1094
+ axis_2 = joint_axis[axis_start + 2]
1095
+ t_total += joint_act[qd_start + 2] * wp.transform_vector(X_wp, axis_2)
808
1096
 
809
1097
  elif type == wp.sim.JOINT_UNIVERSAL:
810
1098
  # q_off = wp.transform_get_rotation(X_cj)
@@ -828,63 +1116,71 @@ def apply_joint_torques(
828
1116
  # t_total += joint_act[qd_start+0] * wp.quat_rotate(q_w, axis_0)
829
1117
  # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
830
1118
 
831
- axis_0 = joint_axis[axis_start + 0]
832
- axis_1 = joint_axis[axis_start + 1]
833
- t_total += joint_act[qd_start + 0] * wp.transform_vector(X_wp, axis_0)
834
- t_total += joint_act[qd_start + 1] * wp.transform_vector(X_wp, axis_1)
1119
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1120
+ axis_0 = joint_axis[axis_start + 0]
1121
+ t_total += joint_act[qd_start + 0] * wp.transform_vector(X_wp, axis_0)
1122
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1123
+ axis_1 = joint_axis[axis_start + 1]
1124
+ t_total += joint_act[qd_start + 1] * wp.transform_vector(X_wp, axis_1)
835
1125
 
836
1126
  elif type == wp.sim.JOINT_D6:
837
1127
  # unroll for loop to ensure joint actions remain differentiable
838
1128
  # (since differentiating through a dynamic for loop that updates a local variable is not supported)
839
1129
 
840
1130
  if lin_axis_count > 0:
841
- axis = joint_axis[axis_start + 0]
842
- act = joint_act[qd_start + 0]
843
- a_p = wp.transform_vector(X_wp, axis)
844
- f_total += act * a_p
1131
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1132
+ axis = joint_axis[axis_start + 0]
1133
+ act = joint_act[qd_start + 0]
1134
+ a_p = wp.transform_vector(X_wp, axis)
1135
+ f_total += act * a_p
845
1136
  if lin_axis_count > 1:
846
- axis = joint_axis[axis_start + 1]
847
- act = joint_act[qd_start + 1]
848
- a_p = wp.transform_vector(X_wp, axis)
849
- f_total += act * a_p
1137
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1138
+ axis = joint_axis[axis_start + 1]
1139
+ act = joint_act[qd_start + 1]
1140
+ a_p = wp.transform_vector(X_wp, axis)
1141
+ f_total += act * a_p
850
1142
  if lin_axis_count > 2:
851
- axis = joint_axis[axis_start + 2]
852
- act = joint_act[qd_start + 2]
853
- a_p = wp.transform_vector(X_wp, axis)
854
- f_total += act * a_p
1143
+ if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1144
+ axis = joint_axis[axis_start + 2]
1145
+ act = joint_act[qd_start + 2]
1146
+ a_p = wp.transform_vector(X_wp, axis)
1147
+ f_total += act * a_p
855
1148
 
856
1149
  if ang_axis_count > 0:
857
- axis = joint_axis[axis_start + lin_axis_count + 0]
858
- act = joint_act[qd_start + lin_axis_count + 0]
859
- a_p = wp.transform_vector(X_wp, axis)
860
- t_total += act * a_p
1150
+ if joint_axis_mode[axis_start + lin_axis_count + 0] == wp.sim.JOINT_MODE_FORCE:
1151
+ axis = joint_axis[axis_start + lin_axis_count + 0]
1152
+ act = joint_act[qd_start + lin_axis_count + 0]
1153
+ a_p = wp.transform_vector(X_wp, axis)
1154
+ t_total += act * a_p
861
1155
  if ang_axis_count > 1:
862
- axis = joint_axis[axis_start + lin_axis_count + 1]
863
- act = joint_act[qd_start + lin_axis_count + 1]
864
- a_p = wp.transform_vector(X_wp, axis)
865
- t_total += act * a_p
1156
+ if joint_axis_mode[axis_start + lin_axis_count + 1] == wp.sim.JOINT_MODE_FORCE:
1157
+ axis = joint_axis[axis_start + lin_axis_count + 1]
1158
+ act = joint_act[qd_start + lin_axis_count + 1]
1159
+ a_p = wp.transform_vector(X_wp, axis)
1160
+ t_total += act * a_p
866
1161
  if ang_axis_count > 2:
867
- axis = joint_axis[axis_start + lin_axis_count + 2]
868
- act = joint_act[qd_start + lin_axis_count + 2]
869
- a_p = wp.transform_vector(X_wp, axis)
870
- t_total += act * a_p
1162
+ if joint_axis_mode[axis_start + lin_axis_count + 2] == wp.sim.JOINT_MODE_FORCE:
1163
+ axis = joint_axis[axis_start + lin_axis_count + 2]
1164
+ act = joint_act[qd_start + lin_axis_count + 2]
1165
+ a_p = wp.transform_vector(X_wp, axis)
1166
+ t_total += act * a_p
871
1167
 
872
1168
  else:
873
1169
  print("joint type not handled in apply_joint_torques")
874
1170
 
875
1171
  # write forces
876
1172
  if id_p >= 0:
877
- wp.atomic_add(body_f, id_p, wp.spatial_vector(t_total + wp.cross(r_p, f_total), f_total))
878
- wp.atomic_sub(body_f, id_c, wp.spatial_vector(t_total + wp.cross(r_c, f_total), f_total))
1173
+ wp.atomic_sub(body_f, id_p, wp.spatial_vector(t_total + wp.cross(r_p, f_total), f_total))
1174
+ wp.atomic_add(body_f, id_c, wp.spatial_vector(t_total + wp.cross(r_c, f_total), f_total))
879
1175
 
880
1176
 
881
1177
  @wp.func
882
- def update_joint_axis_mode(mode: wp.uint8, axis: wp.vec3, input_axis_mode: wp.vec3ub):
1178
+ def update_joint_axis_mode(mode: wp.int32, axis: wp.vec3, input_axis_mode: wp.vec3i):
883
1179
  # update the 3D axis mode flags given the axis vector and mode of this axis
884
- mode_x = wp.max(wp.uint8(wp.nonzero(axis[0])) * mode, input_axis_mode[0])
885
- mode_y = wp.max(wp.uint8(wp.nonzero(axis[1])) * mode, input_axis_mode[1])
886
- mode_z = wp.max(wp.uint8(wp.nonzero(axis[2])) * mode, input_axis_mode[2])
887
- return wp.vec3ub(mode_x, mode_y, mode_z)
1180
+ mode_x = wp.max(wp.int32(wp.nonzero(axis[0])) * mode, input_axis_mode[0])
1181
+ mode_y = wp.max(wp.int32(wp.nonzero(axis[1])) * mode, input_axis_mode[1])
1182
+ mode_z = wp.max(wp.int32(wp.nonzero(axis[2])) * mode, input_axis_mode[2])
1183
+ return wp.vec3i(mode_x, mode_y, mode_z)
888
1184
 
889
1185
 
890
1186
  @wp.func
@@ -926,8 +1222,99 @@ def update_joint_axis_target_ke_kd(
926
1222
  )
927
1223
 
928
1224
 
1225
+ @wp.func
1226
+ def compute_linear_correction_3d(
1227
+ dx: wp.vec3,
1228
+ r1: wp.vec3,
1229
+ r2: wp.vec3,
1230
+ tf1: wp.transform,
1231
+ tf2: wp.transform,
1232
+ m_inv1: float,
1233
+ m_inv2: float,
1234
+ I_inv1: wp.mat33,
1235
+ I_inv2: wp.mat33,
1236
+ lambda_in: float,
1237
+ compliance: float,
1238
+ damping: float,
1239
+ dt: float,
1240
+ ) -> float:
1241
+
1242
+ c = wp.length(dx)
1243
+ if c == 0.0:
1244
+ # print("c == 0.0 in positional correction")
1245
+ return 0.0
1246
+
1247
+ n = wp.normalize(dx)
1248
+
1249
+ q1 = wp.transform_get_rotation(tf1)
1250
+ q2 = wp.transform_get_rotation(tf2)
1251
+
1252
+ # Eq. 2-3 (make sure to project into the frame of the body)
1253
+ r1xn = wp.quat_rotate_inv(q1, wp.cross(r1, n))
1254
+ r2xn = wp.quat_rotate_inv(q2, wp.cross(r2, n))
1255
+
1256
+ w1 = m_inv1 + wp.dot(r1xn, I_inv1 * r1xn)
1257
+ w2 = m_inv2 + wp.dot(r2xn, I_inv2 * r2xn)
1258
+ w = w1 + w2
1259
+ if w == 0.0:
1260
+ return 0.0
1261
+ alpha = compliance
1262
+ gamma = compliance * damping
1263
+
1264
+ # Eq. 4-5
1265
+ d_lambda = -c - alpha * lambda_in
1266
+ # TODO consider damping for velocity correction?
1267
+ # delta_lambda = -(err + alpha * lambda_in + gamma * derr)
1268
+ if w + alpha > 0.0:
1269
+ d_lambda /= w * (dt + gamma) + alpha / dt
1270
+
1271
+ return d_lambda
1272
+
1273
+
1274
+ @wp.func
1275
+ def compute_angular_correction_3d(
1276
+ corr: wp.vec3,
1277
+ q1: wp.quat,
1278
+ q2: wp.quat,
1279
+ m_inv1: float,
1280
+ m_inv2: float,
1281
+ I_inv1: wp.mat33,
1282
+ I_inv2: wp.mat33,
1283
+ alpha_tilde: float,
1284
+ # lambda_prev: float,
1285
+ relaxation: float,
1286
+ dt: float,
1287
+ ):
1288
+ # compute and apply the correction impulse for an angular constraint
1289
+ theta = wp.length(corr)
1290
+ if theta == 0.0:
1291
+ return 0.0
1292
+
1293
+ n = wp.normalize(corr)
1294
+
1295
+ # project variables to body rest frame as they are in local matrix
1296
+ n1 = wp.quat_rotate_inv(q1, n)
1297
+ n2 = wp.quat_rotate_inv(q2, n)
1298
+
1299
+ # Eq. 11-12
1300
+ w1 = wp.dot(n1, I_inv1 * n1)
1301
+ w2 = wp.dot(n2, I_inv2 * n2)
1302
+ w = w1 + w2
1303
+ if w == 0.0:
1304
+ return 0.0
1305
+
1306
+ # Eq. 13-14
1307
+ lambda_prev = 0.0
1308
+ d_lambda = (-theta - alpha_tilde * lambda_prev) / (w * dt + alpha_tilde / dt)
1309
+ # TODO consider lambda_prev?
1310
+ # p = d_lambda * n * relaxation
1311
+
1312
+ # Eq. 15-16
1313
+ return d_lambda
1314
+
1315
+
929
1316
  @wp.kernel
930
- def solve_body_joints(
1317
+ def solve_simple_body_joints(
931
1318
  body_q: wp.array(dtype=wp.transform),
932
1319
  body_qd: wp.array(dtype=wp.spatial_vector),
933
1320
  body_com: wp.array(dtype=wp.vec3),
@@ -943,7 +1330,7 @@ def solve_body_joints(
943
1330
  joint_limit_upper: wp.array(dtype=float),
944
1331
  joint_axis_start: wp.array(dtype=int),
945
1332
  joint_axis_dim: wp.array(dtype=int, ndim=2),
946
- joint_axis_mode: wp.array(dtype=wp.uint8),
1333
+ joint_axis_mode: wp.array(dtype=int),
947
1334
  joint_axis: wp.array(dtype=wp.vec3),
948
1335
  joint_target: wp.array(dtype=float),
949
1336
  joint_target_ke: wp.array(dtype=float),
@@ -958,7 +1345,17 @@ def solve_body_joints(
958
1345
  tid = wp.tid()
959
1346
  type = joint_type[tid]
960
1347
 
961
- if joint_enabled[tid] == 0 or type == wp.sim.JOINT_FREE:
1348
+ if joint_enabled[tid] == 0:
1349
+ return
1350
+ if type == wp.sim.JOINT_FREE:
1351
+ return
1352
+ if type == wp.sim.JOINT_COMPOUND:
1353
+ return
1354
+ if type == wp.sim.JOINT_UNIVERSAL:
1355
+ return
1356
+ if type == wp.sim.JOINT_DISTANCE:
1357
+ return
1358
+ if type == wp.sim.JOINT_D6:
962
1359
  return
963
1360
 
964
1361
  # rigid body indices of the child and parent
@@ -973,8 +1370,6 @@ def solve_body_joints(
973
1370
  I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
974
1371
  pose_p = X_pj
975
1372
  com_p = wp.vec3(0.0)
976
- vel_p = wp.vec3(0.0)
977
- omega_p = wp.vec3(0.0)
978
1373
  # parent transform and moment arm
979
1374
  if id_p >= 0:
980
1375
  pose_p = body_q[id_p]
@@ -982,8 +1377,7 @@ def solve_body_joints(
982
1377
  com_p = body_com[id_p]
983
1378
  m_inv_p = body_inv_m[id_p]
984
1379
  I_inv_p = body_inv_I[id_p]
985
- vel_p = wp.spatial_bottom(body_qd[id_p])
986
- omega_p = wp.spatial_top(body_qd[id_p])
1380
+ r_p = wp.transform_get_translation(X_wp) - wp.transform_point(pose_p, com_p)
987
1381
 
988
1382
  # child transform and moment arm
989
1383
  pose_c = body_q[id_c]
@@ -991,8 +1385,7 @@ def solve_body_joints(
991
1385
  com_c = body_com[id_c]
992
1386
  m_inv_c = body_inv_m[id_c]
993
1387
  I_inv_c = body_inv_I[id_c]
994
- vel_c = wp.spatial_bottom(body_qd[id_c])
995
- omega_c = wp.spatial_top(body_qd[id_c])
1388
+ r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
996
1389
 
997
1390
  if m_inv_p == 0.0 and m_inv_c == 0.0:
998
1391
  # connection between two immovable bodies
@@ -1013,32 +1406,358 @@ def solve_body_joints(
1013
1406
 
1014
1407
  linear_compliance = joint_linear_compliance[tid]
1015
1408
  angular_compliance = joint_angular_compliance[tid]
1409
+ damping = 0.0
1016
1410
 
1017
1411
  axis_start = joint_axis_start[tid]
1018
- lin_axis_count = joint_axis_dim[tid, 0]
1019
- ang_axis_count = joint_axis_dim[tid, 1]
1412
+ mode = joint_axis_mode[axis_start]
1020
1413
 
1021
- world_com_p = wp.transform_point(pose_p, com_p)
1022
- world_com_c = wp.transform_point(pose_c, com_c)
1414
+ # local joint rotations
1415
+ q_p = wp.transform_get_rotation(X_wp)
1416
+ q_c = wp.transform_get_rotation(X_wc)
1417
+ inertial_q_p = wp.transform_get_rotation(pose_p)
1418
+ inertial_q_c = wp.transform_get_rotation(pose_c)
1023
1419
 
1024
- # handle positional constraints
1025
- if type == wp.sim.JOINT_DISTANCE:
1026
- r_p = wp.transform_get_translation(X_wp) - world_com_p
1027
- r_c = wp.transform_get_translation(X_wc) - world_com_c
1028
- lower = joint_limit_lower[axis_start]
1029
- upper = joint_limit_upper[axis_start]
1030
- if lower < 0.0 and upper < 0.0:
1031
- # no limits
1032
- return
1033
- d = wp.length(rel_p)
1034
- err = 0.0
1035
- if lower >= 0.0 and d < lower:
1036
- err = d - lower
1037
- # use a more descriptive direction vector for the constraint
1038
- # in case the joint parent and child anchors are very close
1039
- rel_p = err * wp.normalize(world_com_c - world_com_p)
1040
- elif upper >= 0.0 and d > upper:
1041
- err = d - upper
1420
+ # joint properties (for 1D joints)
1421
+ axis = joint_axis[axis_start]
1422
+
1423
+ if type == wp.sim.JOINT_FIXED:
1424
+ limit_lower = 0.0
1425
+ limit_upper = 0.0
1426
+ else:
1427
+ limit_lower = joint_limit_lower[axis_start]
1428
+ limit_upper = joint_limit_upper[axis_start]
1429
+
1430
+ linear_alpha_tilde = linear_compliance / dt / dt
1431
+ angular_alpha_tilde = angular_compliance / dt / dt
1432
+
1433
+ # prevent division by zero
1434
+ # linear_alpha_tilde = wp.max(linear_alpha_tilde, 1e-6)
1435
+ # angular_alpha_tilde = wp.max(angular_alpha_tilde, 1e-6)
1436
+
1437
+ # accumulate constraint deltas
1438
+ lin_delta_p = wp.vec3(0.0)
1439
+ ang_delta_p = wp.vec3(0.0)
1440
+ lin_delta_c = wp.vec3(0.0)
1441
+ ang_delta_c = wp.vec3(0.0)
1442
+
1443
+ # handle angular constraints
1444
+ if type == wp.sim.JOINT_REVOLUTE:
1445
+ # align joint axes
1446
+ a_p = wp.quat_rotate(q_p, axis)
1447
+ a_c = wp.quat_rotate(q_c, axis)
1448
+ # Eq. 20
1449
+ corr = wp.cross(a_p, a_c)
1450
+ ncorr = wp.normalize(corr)
1451
+
1452
+ angular_relaxation = 0.2
1453
+ # angular_correction(
1454
+ # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1455
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1456
+ lambda_n = compute_angular_correction_3d(
1457
+ corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1458
+ )
1459
+ lambda_n *= angular_relaxation
1460
+ ang_delta_p -= lambda_n * ncorr
1461
+ ang_delta_c += lambda_n * ncorr
1462
+
1463
+ # limit joint angles (Alg. 3)
1464
+ pi = 3.14159265359
1465
+ two_pi = 2.0 * pi
1466
+ if limit_lower > -two_pi or limit_upper < two_pi:
1467
+ # find a perpendicular vector to joint axis
1468
+ a = axis
1469
+ # https://math.stackexchange.com/a/3582461
1470
+ g = wp.sign(a[2])
1471
+ h = a[2] + g
1472
+ b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1473
+ c = wp.normalize(wp.cross(a, b))
1474
+ # b = c # TODO verify
1475
+
1476
+ # joint axis
1477
+ n = wp.quat_rotate(q_p, a)
1478
+ # the axes n1 and n2 are aligned with the two bodies
1479
+ n1 = wp.quat_rotate(q_p, b)
1480
+ n2 = wp.quat_rotate(q_c, b)
1481
+
1482
+ phi = wp.asin(wp.dot(wp.cross(n1, n2), n))
1483
+ # print("phi")
1484
+ # print(phi)
1485
+ if wp.dot(n1, n2) < 0.0:
1486
+ phi = pi - phi
1487
+ if phi > pi:
1488
+ phi -= two_pi
1489
+ if phi < -pi:
1490
+ phi += two_pi
1491
+ if phi < limit_lower or phi > limit_upper:
1492
+ phi = wp.clamp(phi, limit_lower, limit_upper)
1493
+ # print("clamped phi")
1494
+ # print(phi)
1495
+ # rot = wp.quat(phi, n[0], n[1], n[2])
1496
+ # rot = wp.quat(n, phi)
1497
+ rot = wp.quat_from_axis_angle(n, phi)
1498
+ n1 = wp.quat_rotate(rot, n1)
1499
+ corr = wp.cross(n1, n2)
1500
+ # print("corr")
1501
+ # print(corr)
1502
+ # TODO expose
1503
+ # angular_alpha_tilde = 0.0001 / dt / dt
1504
+ # angular_relaxation = 0.5
1505
+ # TODO fix this constraint
1506
+ # angular_correction(
1507
+ # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1508
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1509
+ lambda_n = compute_angular_correction_3d(
1510
+ corr,
1511
+ inertial_q_p,
1512
+ inertial_q_c,
1513
+ m_inv_p,
1514
+ m_inv_c,
1515
+ I_inv_p,
1516
+ I_inv_c,
1517
+ angular_alpha_tilde,
1518
+ damping,
1519
+ dt,
1520
+ )
1521
+ lambda_n *= angular_relaxation
1522
+ ncorr = wp.normalize(corr)
1523
+ ang_delta_p -= lambda_n * ncorr
1524
+ ang_delta_c += lambda_n * ncorr
1525
+
1526
+ # handle joint targets
1527
+ target_ke = joint_target_ke[axis_start]
1528
+ target_kd = joint_target_kd[axis_start]
1529
+ target = joint_target[axis_start]
1530
+ if target_ke > 0.0:
1531
+ # find a perpendicular vector to joint axis
1532
+ a = axis
1533
+ # https://math.stackexchange.com/a/3582461
1534
+ g = wp.sign(a[2])
1535
+ h = a[2] + g
1536
+ b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1537
+ c = wp.normalize(wp.cross(a, b))
1538
+ b = c
1539
+
1540
+ q = wp.quat_from_axis_angle(a_p, target)
1541
+ b_target = wp.quat_rotate(q, wp.quat_rotate(q_p, b))
1542
+ b2 = wp.quat_rotate(q_c, b)
1543
+ # Eq. 21
1544
+ d_target = wp.cross(b_target, b2)
1545
+
1546
+ target_compliance = 1.0 / target_ke # / dt / dt
1547
+ # angular_correction(
1548
+ # d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1549
+ # target_compliance, angular_relaxation, deltas, id_p, id_c)
1550
+ lambda_n = compute_angular_correction_3d(
1551
+ d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, target_compliance, damping, dt
1552
+ )
1553
+ lambda_n *= angular_relaxation
1554
+ ncorr = wp.normalize(d_target)
1555
+ # TODO fix
1556
+ ang_delta_p -= lambda_n * ncorr
1557
+ ang_delta_c += lambda_n * ncorr
1558
+
1559
+ if (type == wp.sim.JOINT_FIXED) or (type == wp.sim.JOINT_PRISMATIC):
1560
+ # align the mutual orientations of the two bodies
1561
+ # Eq. 18-19
1562
+ q = q_p * wp.quat_inverse(q_c)
1563
+ corr = -2.0 * wp.vec3(q[0], q[1], q[2])
1564
+ # angular_correction(
1565
+ # -corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1566
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1567
+ lambda_n = compute_angular_correction_3d(
1568
+ corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1569
+ )
1570
+ lambda_n *= angular_relaxation
1571
+ ncorr = wp.normalize(corr)
1572
+ ang_delta_p -= lambda_n * ncorr
1573
+ ang_delta_c += lambda_n * ncorr
1574
+
1575
+ # handle positional constraints
1576
+
1577
+ # joint connection points
1578
+ x_p = wp.transform_get_translation(X_wp)
1579
+ x_c = wp.transform_get_translation(X_wc)
1580
+
1581
+ # compute error between the joint attachment points on both bodies
1582
+ # delta x is the difference of point r_2 minus point r_1 (Fig. 3)
1583
+ dx = x_c - x_p
1584
+
1585
+ # rotate the error vector into the joint frame
1586
+ q_dx = q_p
1587
+ # q_dx = q_c
1588
+ # q_dx = wp.transform_get_rotation(pose_p)
1589
+ dx = wp.quat_rotate_inv(q_dx, dx)
1590
+
1591
+ lower_pos_limits = wp.vec3(0.0)
1592
+ upper_pos_limits = wp.vec3(0.0)
1593
+ if type == wp.sim.JOINT_PRISMATIC:
1594
+ lower_pos_limits = axis * limit_lower
1595
+ upper_pos_limits = axis * limit_upper
1596
+
1597
+ # compute linear constraint violations
1598
+ corr = wp.vec3(0.0)
1599
+ zero = wp.vec3(0.0)
1600
+ corr -= vec_leaky_min(zero, upper_pos_limits - dx)
1601
+ corr -= vec_leaky_max(zero, lower_pos_limits - dx)
1602
+
1603
+ # if (type == wp.sim.JOINT_PRISMATIC):
1604
+ # if mode == JOINT_MODE_TARGET_POSITION:
1605
+ # target = wp.clamp(target, limit_lower, limit_upper)
1606
+ # if target_ke > 0.0:
1607
+ # err = dx - target * axis
1608
+ # compliance = 1.0 / target_ke
1609
+ # damping = axis_damping[dim]
1610
+ # elif mode == JOINT_MODE_TARGET_VELOCITY:
1611
+ # if target_ke > 0.0:
1612
+ # err = (derr - target) * dt
1613
+ # compliance = 1.0 / target_ke
1614
+ # damping = axis_damping[dim]
1615
+
1616
+ # rotate correction vector into world frame
1617
+ corr = wp.quat_rotate(q_dx, corr)
1618
+
1619
+ lambda_in = 0.0
1620
+ linear_alpha = joint_linear_compliance[tid]
1621
+ lambda_n = compute_linear_correction_3d(
1622
+ 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
1623
+ )
1624
+ lambda_n *= linear_relaxation
1625
+ n = wp.normalize(corr)
1626
+
1627
+ lin_delta_p -= n * lambda_n
1628
+ lin_delta_c += n * lambda_n
1629
+ ang_delta_p -= wp.cross(r_p, n) * lambda_n
1630
+ ang_delta_c += wp.cross(r_c, n) * lambda_n
1631
+
1632
+ if id_p >= 0:
1633
+ wp.atomic_add(deltas, id_p, wp.spatial_vector(ang_delta_p, lin_delta_p))
1634
+ if id_c >= 0:
1635
+ wp.atomic_add(deltas, id_c, wp.spatial_vector(ang_delta_c, lin_delta_c))
1636
+
1637
+
1638
+ @wp.kernel
1639
+ def solve_body_joints(
1640
+ body_q: wp.array(dtype=wp.transform),
1641
+ body_qd: wp.array(dtype=wp.spatial_vector),
1642
+ body_com: wp.array(dtype=wp.vec3),
1643
+ body_inv_m: wp.array(dtype=float),
1644
+ body_inv_I: wp.array(dtype=wp.mat33),
1645
+ joint_type: wp.array(dtype=int),
1646
+ joint_enabled: wp.array(dtype=int),
1647
+ joint_parent: wp.array(dtype=int),
1648
+ joint_child: wp.array(dtype=int),
1649
+ joint_X_p: wp.array(dtype=wp.transform),
1650
+ joint_X_c: wp.array(dtype=wp.transform),
1651
+ joint_limit_lower: wp.array(dtype=float),
1652
+ joint_limit_upper: wp.array(dtype=float),
1653
+ joint_axis_start: wp.array(dtype=int),
1654
+ joint_axis_dim: wp.array(dtype=int, ndim=2),
1655
+ joint_axis_mode: wp.array(dtype=int),
1656
+ joint_axis: wp.array(dtype=wp.vec3),
1657
+ joint_act: wp.array(dtype=float),
1658
+ joint_target_ke: wp.array(dtype=float),
1659
+ joint_target_kd: wp.array(dtype=float),
1660
+ joint_linear_compliance: wp.array(dtype=float),
1661
+ joint_angular_compliance: wp.array(dtype=float),
1662
+ angular_relaxation: float,
1663
+ linear_relaxation: float,
1664
+ dt: float,
1665
+ deltas: wp.array(dtype=wp.spatial_vector),
1666
+ ):
1667
+ tid = wp.tid()
1668
+ type = joint_type[tid]
1669
+
1670
+ if joint_enabled[tid] == 0:
1671
+ return
1672
+ if type == wp.sim.JOINT_FREE:
1673
+ return
1674
+ # if type == wp.sim.JOINT_FIXED:
1675
+ # return
1676
+ # if type == wp.sim.JOINT_REVOLUTE:
1677
+ # return
1678
+ # if type == wp.sim.JOINT_PRISMATIC:
1679
+ # return
1680
+ # if type == wp.sim.JOINT_BALL:
1681
+ # return
1682
+
1683
+ # rigid body indices of the child and parent
1684
+ id_c = joint_child[tid]
1685
+ id_p = joint_parent[tid]
1686
+
1687
+ X_pj = joint_X_p[tid]
1688
+ X_cj = joint_X_c[tid]
1689
+
1690
+ X_wp = X_pj
1691
+ m_inv_p = 0.0
1692
+ I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1693
+ pose_p = X_pj
1694
+ com_p = wp.vec3(0.0)
1695
+ vel_p = wp.vec3(0.0)
1696
+ omega_p = wp.vec3(0.0)
1697
+ # parent transform and moment arm
1698
+ if id_p >= 0:
1699
+ pose_p = body_q[id_p]
1700
+ X_wp = pose_p * X_wp
1701
+ com_p = body_com[id_p]
1702
+ m_inv_p = body_inv_m[id_p]
1703
+ I_inv_p = body_inv_I[id_p]
1704
+ vel_p = wp.spatial_bottom(body_qd[id_p])
1705
+ omega_p = wp.spatial_top(body_qd[id_p])
1706
+
1707
+ # child transform and moment arm
1708
+ pose_c = body_q[id_c]
1709
+ X_wc = pose_c * X_cj
1710
+ com_c = body_com[id_c]
1711
+ m_inv_c = body_inv_m[id_c]
1712
+ I_inv_c = body_inv_I[id_c]
1713
+ vel_c = wp.spatial_bottom(body_qd[id_c])
1714
+ omega_c = wp.spatial_top(body_qd[id_c])
1715
+
1716
+ if m_inv_p == 0.0 and m_inv_c == 0.0:
1717
+ # connection between two immovable bodies
1718
+ return
1719
+
1720
+ # accumulate constraint deltas
1721
+ lin_delta_p = wp.vec3(0.0)
1722
+ ang_delta_p = wp.vec3(0.0)
1723
+ lin_delta_c = wp.vec3(0.0)
1724
+ ang_delta_c = wp.vec3(0.0)
1725
+
1726
+ rel_pose = wp.transform_inverse(X_wp) * X_wc
1727
+ rel_p = wp.transform_get_translation(rel_pose)
1728
+
1729
+ # joint connection points
1730
+ # x_p = wp.transform_get_translation(X_wp)
1731
+ x_c = wp.transform_get_translation(X_wc)
1732
+
1733
+ linear_compliance = joint_linear_compliance[tid]
1734
+ angular_compliance = joint_angular_compliance[tid]
1735
+
1736
+ axis_start = joint_axis_start[tid]
1737
+ lin_axis_count = joint_axis_dim[tid, 0]
1738
+ ang_axis_count = joint_axis_dim[tid, 1]
1739
+
1740
+ world_com_p = wp.transform_point(pose_p, com_p)
1741
+ world_com_c = wp.transform_point(pose_c, com_c)
1742
+
1743
+ # handle positional constraints
1744
+ if type == wp.sim.JOINT_DISTANCE:
1745
+ r_p = wp.transform_get_translation(X_wp) - world_com_p
1746
+ r_c = wp.transform_get_translation(X_wc) - world_com_c
1747
+ lower = joint_limit_lower[axis_start]
1748
+ upper = joint_limit_upper[axis_start]
1749
+ if lower < 0.0 and upper < 0.0:
1750
+ # no limits
1751
+ return
1752
+ d = wp.length(rel_p)
1753
+ err = 0.0
1754
+ if lower >= 0.0 and d < lower:
1755
+ err = d - lower
1756
+ # use a more descriptive direction vector for the constraint
1757
+ # in case the joint parent and child anchors are very close
1758
+ rel_p = err * wp.normalize(world_com_c - world_com_p)
1759
+ elif upper >= 0.0 and d > upper:
1760
+ err = d - upper
1042
1761
 
1043
1762
  if wp.abs(err) > 1e-9:
1044
1763
  # compute gradients
@@ -1088,7 +1807,7 @@ def solve_body_joints(
1088
1807
  # compute joint target, stiffness, damping
1089
1808
  ke_sum = float(0.0)
1090
1809
  axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1091
- axis_mode = wp.vec3ub(wp.uint8(0), wp.uint8(0), wp.uint8(0))
1810
+ axis_mode = wp.vec3i(0, 0, 0)
1092
1811
  axis_target_ke_kd = wp.mat33(0.0)
1093
1812
  # avoid a for loop here since local variables would need to be modified which is not yet differentiable
1094
1813
  if lin_axis_count > 0:
@@ -1097,10 +1816,10 @@ def solve_body_joints(
1097
1816
  up_temp = axis * joint_limit_upper[axis_start]
1098
1817
  axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
1099
1818
  mode = joint_axis_mode[axis_start]
1100
- if mode != JOINT_MODE_LIMIT: # position or velocity target
1819
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1101
1820
  ke = joint_target_ke[axis_start]
1102
1821
  kd = joint_target_kd[axis_start]
1103
- target = joint_target[axis_start]
1822
+ target = joint_act[axis_start]
1104
1823
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1105
1824
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1106
1825
  ke_sum += ke
@@ -1111,10 +1830,10 @@ def solve_body_joints(
1111
1830
  upper = joint_limit_upper[axis_idx]
1112
1831
  axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1113
1832
  mode = joint_axis_mode[axis_idx]
1114
- if mode != JOINT_MODE_LIMIT: # position or velocity target
1833
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1115
1834
  ke = joint_target_ke[axis_idx]
1116
1835
  kd = joint_target_kd[axis_idx]
1117
- target = joint_target[axis_idx]
1836
+ target = joint_act[axis_idx]
1118
1837
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1119
1838
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1120
1839
  ke_sum += ke
@@ -1125,10 +1844,10 @@ def solve_body_joints(
1125
1844
  upper = joint_limit_upper[axis_idx]
1126
1845
  axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1127
1846
  mode = joint_axis_mode[axis_idx]
1128
- if mode != JOINT_MODE_LIMIT: # position or velocity target
1847
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1129
1848
  ke = joint_target_ke[axis_idx]
1130
1849
  kd = joint_target_kd[axis_idx]
1131
- target = joint_target[axis_idx]
1850
+ target = joint_act[axis_idx]
1132
1851
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1133
1852
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1134
1853
  ke_sum += ke
@@ -1172,12 +1891,8 @@ def solve_body_joints(
1172
1891
  upper = axis_limits_upper[dim]
1173
1892
  if e < lower:
1174
1893
  err = e - lower
1175
- compliance = linear_compliance
1176
- damping = 0.0
1177
1894
  elif e > upper:
1178
1895
  err = e - upper
1179
- compliance = linear_compliance
1180
- damping = 0.0
1181
1896
  else:
1182
1897
  target = axis_target[dim]
1183
1898
  if mode == JOINT_MODE_TARGET_POSITION:
@@ -1191,6 +1906,7 @@ def solve_body_joints(
1191
1906
  err = (derr - target) * dt
1192
1907
  compliance = 1.0 / axis_stiffness[dim]
1193
1908
  damping = axis_damping[dim]
1909
+ derr = 0.0
1194
1910
 
1195
1911
  if wp.abs(err) > 1e-9:
1196
1912
  lambda_in = 0.0
@@ -1293,7 +2009,7 @@ def solve_body_joints(
1293
2009
  # compute joint target, stiffness, damping
1294
2010
  ke_sum = float(0.0)
1295
2011
  axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1296
- axis_mode = wp.vec3ub(wp.uint8(0), wp.uint8(0), wp.uint8(0))
2012
+ axis_mode = wp.vec3i(0, 0, 0)
1297
2013
  axis_target_ke_kd = wp.mat33(0.0)
1298
2014
  # avoid a for loop here since local variables would need to be modified which is not yet differentiable
1299
2015
  if ang_axis_count > 0:
@@ -1303,10 +2019,10 @@ def solve_body_joints(
1303
2019
  up_temp = axis * joint_limit_upper[axis_idx]
1304
2020
  axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
1305
2021
  mode = joint_axis_mode[axis_idx]
1306
- if mode != JOINT_MODE_LIMIT: # position or velocity target
2022
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1307
2023
  ke = joint_target_ke[axis_idx]
1308
2024
  kd = joint_target_kd[axis_idx]
1309
- target = joint_target[axis_idx]
2025
+ target = joint_act[axis_idx]
1310
2026
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1311
2027
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1312
2028
  ke_sum += ke
@@ -1317,10 +2033,10 @@ def solve_body_joints(
1317
2033
  upper = joint_limit_upper[axis_idx]
1318
2034
  axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1319
2035
  mode = joint_axis_mode[axis_idx]
1320
- if mode != JOINT_MODE_LIMIT: # position or velocity target
2036
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1321
2037
  ke = joint_target_ke[axis_idx]
1322
2038
  kd = joint_target_kd[axis_idx]
1323
- target = joint_target[axis_idx]
2039
+ target = joint_act[axis_idx]
1324
2040
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1325
2041
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1326
2042
  ke_sum += ke
@@ -1331,10 +2047,10 @@ def solve_body_joints(
1331
2047
  upper = joint_limit_upper[axis_idx]
1332
2048
  axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1333
2049
  mode = joint_axis_mode[axis_idx]
1334
- if mode != JOINT_MODE_LIMIT: # position or velocity target
2050
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1335
2051
  ke = joint_target_ke[axis_idx]
1336
2052
  kd = joint_target_kd[axis_idx]
1337
- target = joint_target[axis_idx]
2053
+ target = joint_act[axis_idx]
1338
2054
  axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1339
2055
  axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1340
2056
  ke_sum += ke
@@ -1378,12 +2094,8 @@ def solve_body_joints(
1378
2094
  upper = axis_limits_upper[dim]
1379
2095
  if e < lower:
1380
2096
  err = e - lower
1381
- compliance = angular_compliance
1382
- damping = 0.0
1383
2097
  elif e > upper:
1384
2098
  err = e - upper
1385
- compliance = angular_compliance
1386
- damping = 0.0
1387
2099
  else:
1388
2100
  target = axis_target[dim]
1389
2101
  if mode == JOINT_MODE_TARGET_POSITION:
@@ -1397,6 +2109,7 @@ def solve_body_joints(
1397
2109
  err = (derr - target) * dt
1398
2110
  compliance = 1.0 / axis_stiffness[dim]
1399
2111
  damping = axis_damping[dim]
2112
+ derr = 0.0
1400
2113
 
1401
2114
  d_lambda = (
1402
2115
  compute_angular_correction(
@@ -1404,6 +2117,7 @@ def solve_body_joints(
1404
2117
  )
1405
2118
  * angular_relaxation
1406
2119
  )
2120
+
1407
2121
  # update deltas
1408
2122
  ang_delta_p += angular_p * d_lambda
1409
2123
  ang_delta_c += angular_c * d_lambda
@@ -1444,11 +2158,11 @@ def compute_contact_constraint_delta(
1444
2158
  denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
1445
2159
  denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
1446
2160
 
1447
- deltaLambda = -err
2161
+ delta_lambda = -err
1448
2162
  if denom > 0.0:
1449
- deltaLambda /= dt * dt * denom
2163
+ delta_lambda /= dt * denom
1450
2164
 
1451
- return deltaLambda * relaxation
2165
+ return delta_lambda * relaxation
1452
2166
 
1453
2167
 
1454
2168
  @wp.func
@@ -1487,11 +2201,11 @@ def compute_positional_correction(
1487
2201
  alpha = compliance
1488
2202
  gamma = compliance * damping
1489
2203
 
1490
- deltaLambda = -(err + alpha * lambda_in + gamma * derr)
2204
+ delta_lambda = -(err + alpha * lambda_in + gamma * derr)
1491
2205
  if denom + alpha > 0.0:
1492
- deltaLambda /= dt * (dt + gamma) * denom + alpha
2206
+ delta_lambda /= (dt + gamma) * denom + alpha / dt
1493
2207
 
1494
- return deltaLambda
2208
+ return delta_lambda
1495
2209
 
1496
2210
 
1497
2211
  @wp.func
@@ -1524,11 +2238,11 @@ def compute_angular_correction(
1524
2238
  alpha = compliance
1525
2239
  gamma = compliance * damping
1526
2240
 
1527
- deltaLambda = -(err + alpha * lambda_in + gamma * derr)
2241
+ delta_lambda = -(err + alpha * lambda_in + gamma * derr)
1528
2242
  if denom + alpha > 0.0:
1529
- deltaLambda /= dt * (dt + gamma) * denom + alpha
2243
+ delta_lambda /= (dt + gamma) * denom + alpha / dt
1530
2244
 
1531
- return deltaLambda
2245
+ return delta_lambda
1532
2246
 
1533
2247
 
1534
2248
  @wp.kernel
@@ -1538,9 +2252,8 @@ def solve_body_contact_positions(
1538
2252
  body_com: wp.array(dtype=wp.vec3),
1539
2253
  body_m_inv: wp.array(dtype=float),
1540
2254
  body_I_inv: wp.array(dtype=wp.mat33),
2255
+ shape_body: wp.array(dtype=int),
1541
2256
  contact_count: wp.array(dtype=int),
1542
- contact_body0: wp.array(dtype=int),
1543
- contact_body1: wp.array(dtype=int),
1544
2257
  contact_point0: wp.array(dtype=wp.vec3),
1545
2258
  contact_point1: wp.array(dtype=wp.vec3),
1546
2259
  contact_offset0: wp.array(dtype=wp.vec3),
@@ -1556,9 +2269,6 @@ def solve_body_contact_positions(
1556
2269
  contact_rolling_friction: float,
1557
2270
  # outputs
1558
2271
  deltas: wp.array(dtype=wp.spatial_vector),
1559
- active_contact_point0: wp.array(dtype=wp.vec3),
1560
- active_contact_point1: wp.array(dtype=wp.vec3),
1561
- active_contact_distance: wp.array(dtype=float),
1562
2272
  contact_inv_weight: wp.array(dtype=float),
1563
2273
  ):
1564
2274
  tid = wp.tid()
@@ -1567,12 +2277,17 @@ def solve_body_contact_positions(
1567
2277
  if tid >= count:
1568
2278
  return
1569
2279
 
1570
- body_a = contact_body0[tid]
1571
- body_b = contact_body1[tid]
1572
-
1573
- if body_a == body_b:
2280
+ shape_a = contact_shape0[tid]
2281
+ shape_b = contact_shape1[tid]
2282
+ if shape_a == shape_b:
1574
2283
  return
1575
- if contact_shape0[tid] == contact_shape1[tid]:
2284
+ body_a = -1
2285
+ if shape_a >= 0:
2286
+ body_a = shape_body[shape_a]
2287
+ body_b = -1
2288
+ if shape_b >= 0:
2289
+ body_b = shape_body[shape_b]
2290
+ if body_a == body_b:
1576
2291
  return
1577
2292
 
1578
2293
  # find body to world transform
@@ -1586,15 +2301,11 @@ def solve_body_contact_positions(
1586
2301
  # compute body position in world space
1587
2302
  bx_a = wp.transform_point(X_wb_a, contact_point0[tid])
1588
2303
  bx_b = wp.transform_point(X_wb_b, contact_point1[tid])
1589
- active_contact_point0[tid] = bx_a
1590
- active_contact_point1[tid] = bx_b
1591
2304
 
1592
2305
  thickness = contact_thickness[tid]
1593
2306
  n = -contact_normal[tid]
1594
2307
  d = wp.dot(n, bx_b - bx_a) - thickness
1595
2308
 
1596
- active_contact_distance[tid] = d
1597
-
1598
2309
  if d >= 0.0:
1599
2310
  return
1600
2311
 
@@ -1632,8 +2343,6 @@ def solve_body_contact_positions(
1632
2343
  # use average contact material properties
1633
2344
  mat_nonzero = 0
1634
2345
  mu = 0.0
1635
- shape_a = contact_shape0[tid]
1636
- shape_b = contact_shape1[tid]
1637
2346
  if shape_a >= 0:
1638
2347
  mat_nonzero += 1
1639
2348
  mu += shape_materials.mu[shape_a]
@@ -1783,16 +2492,17 @@ def apply_rigid_restitution(
1783
2492
  body_com: wp.array(dtype=wp.vec3),
1784
2493
  body_m_inv: wp.array(dtype=float),
1785
2494
  body_I_inv: wp.array(dtype=wp.mat33),
2495
+ shape_body: wp.array(dtype=int),
1786
2496
  contact_count: wp.array(dtype=int),
1787
- contact_body0: wp.array(dtype=int),
1788
- contact_body1: wp.array(dtype=int),
1789
2497
  contact_normal: wp.array(dtype=wp.vec3),
1790
2498
  contact_shape0: wp.array(dtype=int),
1791
2499
  contact_shape1: wp.array(dtype=int),
1792
2500
  shape_materials: ModelShapeMaterials,
1793
- active_contact_distance: wp.array(dtype=float),
1794
- active_contact_point0: wp.array(dtype=wp.vec3),
1795
- active_contact_point1: wp.array(dtype=wp.vec3),
2501
+ contact_point0: wp.array(dtype=wp.vec3),
2502
+ contact_point1: wp.array(dtype=wp.vec3),
2503
+ contact_offset0: wp.array(dtype=wp.vec3),
2504
+ contact_offset1: wp.array(dtype=wp.vec3),
2505
+ contact_thickness: wp.array(dtype=float),
1796
2506
  contact_inv_weight: wp.array(dtype=float),
1797
2507
  gravity: wp.vec3,
1798
2508
  dt: float,
@@ -1804,26 +2514,28 @@ def apply_rigid_restitution(
1804
2514
  count = contact_count[0]
1805
2515
  if tid >= count:
1806
2516
  return
1807
- d = active_contact_distance[tid]
1808
- if d >= 0.0:
2517
+ shape_a = contact_shape0[tid]
2518
+ shape_b = contact_shape1[tid]
2519
+ if shape_a == shape_b:
1809
2520
  return
2521
+ body_a = -1
2522
+ body_b = -1
1810
2523
 
1811
2524
  # use average contact material properties
1812
2525
  mat_nonzero = 0
1813
2526
  restitution = 0.0
1814
- shape_a = contact_shape0[tid]
1815
- shape_b = contact_shape1[tid]
1816
2527
  if shape_a >= 0:
1817
2528
  mat_nonzero += 1
1818
2529
  restitution += shape_materials.restitution[shape_a]
2530
+ body_a = shape_body[shape_a]
1819
2531
  if shape_b >= 0:
1820
2532
  mat_nonzero += 1
1821
2533
  restitution += shape_materials.restitution[shape_b]
2534
+ body_b = shape_body[shape_b]
1822
2535
  if mat_nonzero > 0:
1823
2536
  restitution /= float(mat_nonzero)
1824
-
1825
- body_a = contact_body0[tid]
1826
- body_b = contact_body1[tid]
2537
+ if body_a == body_b:
2538
+ return
1827
2539
 
1828
2540
  m_inv_a = 0.0
1829
2541
  m_inv_b = 0.0
@@ -1858,37 +2570,43 @@ def apply_rigid_restitution(
1858
2570
  I_inv_b = body_I_inv[body_b]
1859
2571
  com_b = body_com[body_b]
1860
2572
 
1861
- bx_a = active_contact_point0[tid]
1862
- bx_b = active_contact_point1[tid]
1863
-
1864
- r_a = bx_a - wp.transform_point(X_wb_a, com_a)
1865
- r_b = bx_b - wp.transform_point(X_wb_b, com_b)
2573
+ # compute body position in world space
2574
+ bx_a = wp.transform_point(X_wb_a_prev, contact_point0[tid] + contact_offset0[tid])
2575
+ bx_b = wp.transform_point(X_wb_b_prev, contact_point1[tid] + contact_offset1[tid])
1866
2576
 
2577
+ thickness = contact_thickness[tid]
1867
2578
  n = contact_normal[tid]
2579
+ d = -wp.dot(n, bx_b - bx_a) - thickness
2580
+ if d >= 0.0:
2581
+ return
2582
+
2583
+ r_a = bx_a - wp.transform_point(X_wb_a_prev, com_a)
2584
+ r_b = bx_b - wp.transform_point(X_wb_b_prev, com_b)
2585
+
2586
+ rxn_a = wp.vec3(0.0)
2587
+ rxn_b = wp.vec3(0.0)
1868
2588
  if body_a >= 0:
1869
2589
  v_a = velocity_at_point(body_qd_prev[body_a], r_a) + gravity * dt
1870
2590
  v_a_new = velocity_at_point(body_qd[body_a], r_a)
1871
2591
  q_a = wp.transform_get_rotation(X_wb_a_prev)
1872
- rxn = wp.quat_rotate_inv(q_a, wp.cross(r_a, n))
2592
+ rxn_a = wp.quat_rotate_inv(q_a, wp.cross(r_a, n))
1873
2593
  # Eq. 2
1874
- inv_mass_a = m_inv_a + wp.dot(rxn, I_inv_a * rxn)
1875
- # if (contact_inv_weight):
1876
- # if (contact_inv_weight[body_a] > 0.0):
1877
- # inv_mass_a *= contact_inv_weight[body_a]
2594
+ inv_mass_a = m_inv_a + wp.dot(rxn_a, I_inv_a * rxn_a)
2595
+ # if contact_inv_weight:
2596
+ # if contact_inv_weight[body_a] > 0.0:
2597
+ # inv_mass_a *= contact_inv_weight[body_a]
1878
2598
  inv_mass += inv_mass_a
1879
- # inv_mass += m_inv_a + wp.dot(rxn, I_inv_a * rxn)
1880
2599
  if body_b >= 0:
1881
2600
  v_b = velocity_at_point(body_qd_prev[body_b], r_b) + gravity * dt
1882
2601
  v_b_new = velocity_at_point(body_qd[body_b], r_b)
1883
2602
  q_b = wp.transform_get_rotation(X_wb_b_prev)
1884
- rxn = wp.quat_rotate_inv(q_b, wp.cross(r_b, n))
2603
+ rxn_b = wp.quat_rotate_inv(q_b, wp.cross(r_b, n))
1885
2604
  # Eq. 3
1886
- inv_mass_b = m_inv_b + wp.dot(rxn, I_inv_b * rxn)
1887
- # if (contact_inv_weight):
1888
- # if (contact_inv_weight[body_b] > 0.0):
1889
- # inv_mass_b *= contact_inv_weight[body_b]
2605
+ inv_mass_b = m_inv_b + wp.dot(rxn_b, I_inv_b * rxn_b)
2606
+ # if contact_inv_weight:
2607
+ # if contact_inv_weight[body_b] > 0.0:
2608
+ # inv_mass_b *= contact_inv_weight[body_b]
1890
2609
  inv_mass += inv_mass_b
1891
- # inv_mass += m_inv_b + wp.dot(rxn, I_inv_b * rxn)
1892
2610
 
1893
2611
  if inv_mass == 0.0:
1894
2612
  return
@@ -1897,36 +2615,40 @@ def apply_rigid_restitution(
1897
2615
  rel_vel_old = wp.dot(n, v_a - v_b)
1898
2616
  rel_vel_new = wp.dot(n, v_a_new - v_b_new)
1899
2617
 
1900
- # Eq. 34 (Eq. 33 from the ACM paper, note the max operation)
1901
- dv = n * (-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0))
2618
+ if rel_vel_old >= 0.0:
2619
+ return
2620
+
2621
+ # Eq. 34
2622
+ dv = (-rel_vel_new - restitution * rel_vel_old) / inv_mass
1902
2623
 
1903
2624
  # Eq. 33
1904
- p = dv / inv_mass
1905
2625
  if body_a >= 0:
1906
- p_a = p
1907
- if contact_inv_weight:
1908
- if contact_inv_weight[body_a] > 0.0:
1909
- p_a /= contact_inv_weight[body_a]
1910
- q_a = wp.transform_get_rotation(X_wb_a)
1911
- rxp = wp.quat_rotate_inv(q_a, wp.cross(r_a, p_a))
1912
- dq = wp.quat_rotate(q_a, I_inv_a * rxp)
1913
- wp.atomic_add(deltas, body_a, wp.spatial_vector(dq, p_a * m_inv_a))
2626
+ dv_a = dv
2627
+ # if contact_inv_weight:
2628
+ # if contact_inv_weight[body_a] > 0.0:
2629
+ # dv_a *= contact_inv_weight[body_a]
2630
+ q_a = wp.transform_get_rotation(X_wb_a_prev)
2631
+ dq = wp.quat_rotate(q_a, I_inv_a * rxn_a * dv_a)
2632
+ wp.atomic_add(deltas, body_a, wp.spatial_vector(dq, n * m_inv_a * dv_a))
1914
2633
 
1915
2634
  if body_b >= 0:
1916
- p_b = p
1917
- if contact_inv_weight:
1918
- if contact_inv_weight[body_b] > 0.0:
1919
- p_b /= contact_inv_weight[body_b]
1920
- q_b = wp.transform_get_rotation(X_wb_b)
1921
- rxp = wp.quat_rotate_inv(q_b, wp.cross(r_b, p_b))
1922
- dq = wp.quat_rotate(q_b, I_inv_b * rxp)
1923
- wp.atomic_sub(deltas, body_b, wp.spatial_vector(dq, p_b * m_inv_b))
2635
+ dv_b = -dv
2636
+ # if contact_inv_weight:
2637
+ # if contact_inv_weight[body_b] > 0.0:
2638
+ # dv_b *= contact_inv_weight[body_b]
2639
+ q_b = wp.transform_get_rotation(X_wb_b_prev)
2640
+ dq = wp.quat_rotate(q_b, I_inv_b * rxn_b * dv_b)
2641
+ wp.atomic_add(deltas, body_b, wp.spatial_vector(dq, n * m_inv_b * dv_b))
2642
+
1924
2643
 
2644
+ class XPBDIntegrator(Integrator):
2645
+ """An implicit integrator using eXtended Position-Based Dynamics (XPBD) for rigid and soft body simulation.
1925
2646
 
1926
- class XPBDIntegrator:
1927
- """A implicit integrator using XPBD
2647
+ References:
2648
+ - 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
2649
+ - 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
1928
2650
 
1929
- After constructing `Model` and `State` objects this time-integrator
2651
+ After constructing :class:`Model`, :class:`State`, and :class:`Control` (optional) objects, this time-integrator
1930
2652
  may be used to advance the simulation state forward in time.
1931
2653
 
1932
2654
  Example
@@ -1934,11 +2656,11 @@ class XPBDIntegrator:
1934
2656
 
1935
2657
  .. code-block:: python
1936
2658
 
1937
- integrator = wp.SemiImplicitIntegrator()
2659
+ integrator = wp.XPBDIntegrator()
1938
2660
 
1939
2661
  # simulation loop
1940
2662
  for i in range(100):
1941
- state = integrator.simulate(model, state_in, state_out, dt)
2663
+ state = integrator.simulate(model, state_in, state_out, dt, control)
1942
2664
 
1943
2665
  """
1944
2666
 
@@ -1969,36 +2691,164 @@ class XPBDIntegrator:
1969
2691
 
1970
2692
  self.enable_restitution = enable_restitution
1971
2693
 
1972
- def simulate(self, model, state_in, state_out, dt, requires_grad=False):
2694
+ self.compute_body_velocity_from_position_delta = False
2695
+
2696
+ # helper variables to track constraint resolution vars
2697
+ self._particle_delta_counter = 0
2698
+ self._body_delta_counter = 0
2699
+
2700
+ def apply_particle_deltas(
2701
+ self,
2702
+ model: Model,
2703
+ state_in: State,
2704
+ state_out: State,
2705
+ particle_deltas: wp.array,
2706
+ dt: float,
2707
+ ):
2708
+ if state_in.requires_grad:
2709
+ particle_q = state_out.particle_q
2710
+ # allocate new particle arrays so gradients can be tracked correctly without overwriting
2711
+ new_particle_q = wp.empty_like(state_out.particle_q)
2712
+ new_particle_qd = wp.empty_like(state_out.particle_qd)
2713
+ self._particle_delta_counter += 1
2714
+ else:
2715
+ if self._particle_delta_counter == 0:
2716
+ particle_q = state_out.particle_q
2717
+ new_particle_q = state_in.particle_q
2718
+ new_particle_qd = state_in.particle_qd
2719
+ else:
2720
+ particle_q = state_in.particle_q
2721
+ new_particle_q = state_out.particle_q
2722
+ new_particle_qd = state_out.particle_qd
2723
+ self._particle_delta_counter = 1 - self._particle_delta_counter
2724
+
2725
+ wp.launch(
2726
+ kernel=apply_particle_deltas,
2727
+ dim=model.particle_count,
2728
+ inputs=[
2729
+ self.particle_q_init,
2730
+ particle_q,
2731
+ model.particle_flags,
2732
+ particle_deltas,
2733
+ dt,
2734
+ model.particle_max_velocity,
2735
+ ],
2736
+ outputs=[new_particle_q, new_particle_qd],
2737
+ device=model.device,
2738
+ )
2739
+
2740
+ if state_in.requires_grad:
2741
+ state_out.particle_q = new_particle_q
2742
+ state_out.particle_qd = new_particle_qd
2743
+
2744
+ return new_particle_q, new_particle_qd
2745
+
2746
+ def apply_body_deltas(
2747
+ self,
2748
+ model: Model,
2749
+ state_in: State,
2750
+ state_out: State,
2751
+ body_deltas: wp.array,
2752
+ dt: float,
2753
+ rigid_contact_inv_weight: wp.array = None,
2754
+ ):
2755
+ with wp.ScopedTimer("apply_body_deltas", False):
2756
+ if state_in.requires_grad:
2757
+ body_q = state_out.body_q
2758
+ body_qd = state_out.body_qd
2759
+ new_body_q = wp.clone(body_q)
2760
+ new_body_qd = wp.clone(body_qd)
2761
+ self._body_delta_counter += 1
2762
+ else:
2763
+ if self._body_delta_counter == 0:
2764
+ body_q = state_out.body_q
2765
+ body_qd = state_out.body_qd
2766
+ new_body_q = state_in.body_q
2767
+ new_body_qd = state_in.body_qd
2768
+ else:
2769
+ body_q = state_in.body_q
2770
+ body_qd = state_in.body_qd
2771
+ new_body_q = state_out.body_q
2772
+ new_body_qd = state_out.body_qd
2773
+ self._body_delta_counter = 1 - self._body_delta_counter
2774
+
2775
+ wp.launch(
2776
+ kernel=apply_body_deltas,
2777
+ dim=model.body_count,
2778
+ inputs=[
2779
+ body_q,
2780
+ body_qd,
2781
+ model.body_com,
2782
+ model.body_inertia,
2783
+ model.body_inv_mass,
2784
+ model.body_inv_inertia,
2785
+ body_deltas,
2786
+ rigid_contact_inv_weight,
2787
+ dt,
2788
+ ],
2789
+ outputs=[
2790
+ new_body_q,
2791
+ new_body_qd,
2792
+ ],
2793
+ device=model.device,
2794
+ )
2795
+
2796
+ if state_in.requires_grad:
2797
+ state_out.body_q = new_body_q
2798
+ state_out.body_qd = new_body_qd
2799
+
2800
+ return new_body_q, new_body_qd
2801
+
2802
+ def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
2803
+ requires_grad = state_in.requires_grad
2804
+ self._particle_delta_counter = 0
2805
+ self._body_delta_counter = 0
2806
+
2807
+ particle_q = None
2808
+ particle_qd = None
2809
+ particle_deltas = None
2810
+
2811
+ body_q = None
2812
+ body_qd = None
2813
+ body_deltas = None
2814
+
2815
+ rigid_contact_inv_weight = None
2816
+
2817
+ if model.rigid_contact_max > 0:
2818
+ if self.rigid_contact_con_weighting:
2819
+ rigid_contact_inv_weight = wp.zeros_like(model.rigid_contact_thickness)
2820
+ rigid_contact_inv_weight_init = None
2821
+
2822
+ if control is None:
2823
+ control = model.control(clone_variables=False)
2824
+
1973
2825
  with wp.ScopedTimer("simulate", False):
1974
- particle_q = None
1975
- particle_qd = None
1976
2826
 
1977
2827
  if model.particle_count:
1978
2828
  if requires_grad:
1979
- particle_q = wp.zeros_like(state_in.particle_q)
1980
- particle_qd = wp.zeros_like(state_in.particle_qd)
2829
+ particle_q = state_out.particle_q
2830
+ particle_qd = state_out.particle_qd
1981
2831
  else:
1982
2832
  particle_q = state_out.particle_q
1983
2833
  particle_qd = state_out.particle_qd
1984
- wp.launch(
1985
- kernel=integrate_particles,
1986
- dim=model.particle_count,
1987
- inputs=[
1988
- state_in.particle_q,
1989
- state_in.particle_qd,
1990
- state_in.particle_f,
1991
- model.particle_inv_mass,
1992
- model.particle_flags,
1993
- model.gravity,
1994
- dt,
1995
- model.particle_max_velocity,
1996
- ],
1997
- outputs=[particle_q, particle_qd],
1998
- device=model.device,
1999
- )
2834
+
2835
+ self.particle_q_init = wp.clone(state_in.particle_q)
2836
+ if self.enable_restitution:
2837
+ self.particle_qd_init = wp.clone(state_in.particle_qd)
2838
+ particle_deltas = wp.empty_like(state_out.particle_qd)
2839
+
2840
+ self.integrate_particles(model, state_in, state_out, dt)
2000
2841
 
2001
2842
  if model.body_count:
2843
+ body_q = state_out.body_q
2844
+ body_qd = state_out.body_qd
2845
+
2846
+ if self.compute_body_velocity_from_position_delta or self.enable_restitution:
2847
+ body_q_init = wp.clone(state_in.body_q)
2848
+ body_qd_init = wp.clone(state_in.body_qd)
2849
+
2850
+ body_deltas = wp.empty_like(state_out.body_qd)
2851
+
2002
2852
  if model.joint_count:
2003
2853
  wp.launch(
2004
2854
  kernel=apply_joint_torques,
@@ -2016,416 +2866,332 @@ class XPBDIntegrator:
2016
2866
  model.joint_axis_start,
2017
2867
  model.joint_axis_dim,
2018
2868
  model.joint_axis,
2019
- model.joint_act,
2869
+ model.joint_axis_mode,
2870
+ control.joint_act,
2020
2871
  ],
2021
2872
  outputs=[state_in.body_f],
2022
2873
  device=model.device,
2023
2874
  )
2024
2875
 
2025
- wp.launch(
2026
- kernel=integrate_bodies,
2027
- dim=model.body_count,
2028
- inputs=[
2029
- state_in.body_q,
2030
- state_in.body_qd,
2031
- state_in.body_f,
2032
- model.body_com,
2033
- model.body_mass,
2034
- model.body_inertia,
2035
- model.body_inv_mass,
2036
- model.body_inv_inertia,
2037
- model.gravity,
2038
- self.angular_damping,
2039
- dt,
2040
- ],
2041
- outputs=[state_out.body_q, state_out.body_qd],
2042
- device=model.device,
2043
- )
2876
+ self.integrate_bodies(model, state_in, state_out, dt, self.angular_damping)
2044
2877
 
2878
+ spring_constraint_lambdas = None
2045
2879
  if model.spring_count:
2046
- model.spring_constraint_lambdas.zero_()
2047
-
2880
+ spring_constraint_lambdas = wp.empty_like(model.spring_rest_length)
2881
+ edge_constraint_lambdas = None
2048
2882
  if model.edge_count:
2049
- model.edge_constraint_lambdas.zero_()
2883
+ edge_constraint_lambdas = wp.empty_like(model.edge_rest_angle)
2050
2884
 
2051
2885
  for i in range(self.iterations):
2052
- # print(f"### iteration {i} / {self.iterations-1}")
2053
2886
 
2054
- if model.body_count:
2055
- if requires_grad:
2056
- out_body_q = wp.clone(state_out.body_q)
2057
- out_body_qd = wp.clone(state_out.body_qd)
2058
- state_out.body_deltas = wp.zeros_like(state_out.body_deltas)
2059
- else:
2060
- out_body_q = state_out.body_q
2061
- out_body_qd = state_out.body_qd
2062
- state_out.body_deltas.zero_()
2063
- else:
2064
- out_body_q = None
2065
- out_body_qd = None
2887
+ with wp.ScopedTimer(f"iteration_{i}", False):
2066
2888
 
2067
- # ----------------------------
2068
- # handle particles
2069
- if model.particle_count:
2070
- if requires_grad:
2071
- deltas = wp.zeros_like(state_out.particle_f)
2072
- else:
2073
- deltas = state_out.particle_f
2074
- deltas.zero_()
2889
+ if model.body_count:
2075
2890
 
2076
- # particle ground contact
2077
- if model.ground:
2078
- wp.launch(
2079
- kernel=solve_particle_ground_contacts,
2080
- dim=model.particle_count,
2081
- inputs=[
2082
- particle_q,
2083
- particle_qd,
2084
- model.particle_inv_mass,
2085
- model.particle_radius,
2086
- model.particle_flags,
2087
- model.soft_contact_ke,
2088
- model.soft_contact_kd,
2089
- model.soft_contact_kf,
2090
- model.soft_contact_mu,
2091
- model.ground_plane,
2092
- dt,
2093
- self.soft_contact_relaxation,
2094
- ],
2095
- outputs=[deltas],
2096
- device=model.device,
2891
+ if requires_grad and i > 0:
2892
+ body_deltas = wp.zeros_like(body_deltas)
2893
+ else:
2894
+ body_deltas.zero_()
2895
+
2896
+ if model.particle_count:
2897
+
2898
+ if requires_grad and i > 0:
2899
+ particle_deltas = wp.zeros_like(particle_deltas)
2900
+ else:
2901
+ particle_deltas.zero_()
2902
+
2903
+ # particle ground contact
2904
+ if model.ground:
2905
+ wp.launch(
2906
+ kernel=solve_particle_ground_contacts,
2907
+ dim=model.particle_count,
2908
+ inputs=[
2909
+ particle_q,
2910
+ particle_qd,
2911
+ model.particle_inv_mass,
2912
+ model.particle_radius,
2913
+ model.particle_flags,
2914
+ model.soft_contact_ke,
2915
+ model.soft_contact_kd,
2916
+ model.soft_contact_kf,
2917
+ model.soft_contact_mu,
2918
+ model.ground_plane,
2919
+ dt,
2920
+ self.soft_contact_relaxation,
2921
+ ],
2922
+ outputs=[particle_deltas],
2923
+ device=model.device,
2924
+ )
2925
+
2926
+ # particle-rigid body contacts (besides ground plane)
2927
+ if model.shape_count > 1:
2928
+ wp.launch(
2929
+ kernel=solve_particle_shape_contacts,
2930
+ dim=model.soft_contact_max,
2931
+ inputs=[
2932
+ particle_q,
2933
+ particle_qd,
2934
+ model.particle_inv_mass,
2935
+ model.particle_radius,
2936
+ model.particle_flags,
2937
+ body_q,
2938
+ body_qd,
2939
+ model.body_com,
2940
+ model.body_inv_mass,
2941
+ model.body_inv_inertia,
2942
+ model.shape_body,
2943
+ model.shape_materials,
2944
+ model.soft_contact_mu,
2945
+ model.particle_adhesion,
2946
+ model.soft_contact_count,
2947
+ model.soft_contact_particle,
2948
+ model.soft_contact_shape,
2949
+ model.soft_contact_body_pos,
2950
+ model.soft_contact_body_vel,
2951
+ model.soft_contact_normal,
2952
+ model.soft_contact_max,
2953
+ dt,
2954
+ self.soft_contact_relaxation,
2955
+ ],
2956
+ # outputs
2957
+ outputs=[particle_deltas, body_deltas],
2958
+ device=model.device,
2959
+ )
2960
+
2961
+ if model.particle_max_radius > 0.0 and model.particle_count > 1:
2962
+ # assert model.particle_grid.reserved, "model.particle_grid must be built, see HashGrid.build()"
2963
+ wp.launch(
2964
+ kernel=solve_particle_particle_contacts,
2965
+ dim=model.particle_count,
2966
+ inputs=[
2967
+ model.particle_grid.id,
2968
+ particle_q,
2969
+ particle_qd,
2970
+ model.particle_inv_mass,
2971
+ model.particle_radius,
2972
+ model.particle_flags,
2973
+ model.particle_mu,
2974
+ model.particle_cohesion,
2975
+ model.particle_max_radius,
2976
+ dt,
2977
+ self.soft_contact_relaxation,
2978
+ ],
2979
+ outputs=[particle_deltas],
2980
+ device=model.device,
2981
+ )
2982
+
2983
+ # distance constraints
2984
+ if model.spring_count:
2985
+ spring_constraint_lambdas.zero_()
2986
+ wp.launch(
2987
+ kernel=solve_springs,
2988
+ dim=model.spring_count,
2989
+ inputs=[
2990
+ particle_q,
2991
+ particle_qd,
2992
+ model.particle_inv_mass,
2993
+ model.spring_indices,
2994
+ model.spring_rest_length,
2995
+ model.spring_stiffness,
2996
+ model.spring_damping,
2997
+ dt,
2998
+ spring_constraint_lambdas,
2999
+ ],
3000
+ outputs=[particle_deltas],
3001
+ device=model.device,
3002
+ )
3003
+
3004
+ # bending constraints
3005
+ if model.edge_count:
3006
+ edge_constraint_lambdas.zero_()
3007
+ wp.launch(
3008
+ kernel=bending_constraint,
3009
+ dim=model.edge_count,
3010
+ inputs=[
3011
+ particle_q,
3012
+ particle_qd,
3013
+ model.particle_inv_mass,
3014
+ model.edge_indices,
3015
+ model.edge_rest_angle,
3016
+ model.edge_bending_properties,
3017
+ dt,
3018
+ edge_constraint_lambdas,
3019
+ ],
3020
+ outputs=[particle_deltas],
3021
+ device=model.device,
3022
+ )
3023
+
3024
+ # tetrahedral FEM
3025
+ if model.tet_count:
3026
+ wp.launch(
3027
+ kernel=solve_tetrahedra,
3028
+ dim=model.tet_count,
3029
+ inputs=[
3030
+ particle_q,
3031
+ particle_qd,
3032
+ model.particle_inv_mass,
3033
+ model.tet_indices,
3034
+ model.tet_poses,
3035
+ model.tet_activations,
3036
+ model.tet_materials,
3037
+ dt,
3038
+ self.soft_body_relaxation,
3039
+ ],
3040
+ outputs=[particle_deltas],
3041
+ device=model.device,
3042
+ )
3043
+
3044
+ particle_q, particle_qd = self.apply_particle_deltas(
3045
+ model, state_in, state_out, particle_deltas, dt
2097
3046
  )
2098
3047
 
2099
- # particle - rigid body contacts (besides ground plane)
2100
- if model.shape_count > 1:
3048
+ # handle rigid bodies
3049
+ # ----------------------------
3050
+
3051
+ if model.joint_count:
3052
+
3053
+ # wp.launch(
3054
+ # kernel=solve_simple_body_joints,
3055
+ # dim=model.joint_count,
3056
+ # inputs=[
3057
+ # body_q,
3058
+ # body_qd,
3059
+ # model.body_com,
3060
+ # model.body_inv_mass,
3061
+ # model.body_inv_inertia,
3062
+ # model.joint_type,
3063
+ # model.joint_enabled,
3064
+ # model.joint_parent,
3065
+ # model.joint_child,
3066
+ # model.joint_X_p,
3067
+ # model.joint_X_c,
3068
+ # model.joint_limit_lower,
3069
+ # model.joint_limit_upper,
3070
+ # model.joint_axis_start,
3071
+ # model.joint_axis_dim,
3072
+ # model.joint_axis_mode,
3073
+ # model.joint_axis,
3074
+ # control.joint_target,
3075
+ # model.joint_target_ke,
3076
+ # model.joint_target_kd,
3077
+ # model.joint_linear_compliance,
3078
+ # model.joint_angular_compliance,
3079
+ # self.joint_angular_relaxation,
3080
+ # self.joint_linear_relaxation,
3081
+ # dt,
3082
+ # ],
3083
+ # outputs=[body_deltas],
3084
+ # device=model.device,
3085
+ # )
3086
+
2101
3087
  wp.launch(
2102
- kernel=solve_particle_shape_contacts,
2103
- dim=model.soft_contact_max,
3088
+ kernel=solve_body_joints,
3089
+ dim=model.joint_count,
2104
3090
  inputs=[
2105
- particle_q,
2106
- particle_qd,
2107
- model.particle_inv_mass,
2108
- model.particle_radius,
2109
- model.particle_flags,
2110
- out_body_q,
2111
- out_body_qd,
3091
+ body_q,
3092
+ body_qd,
2112
3093
  model.body_com,
2113
3094
  model.body_inv_mass,
2114
3095
  model.body_inv_inertia,
2115
- model.shape_body,
2116
- model.shape_materials,
2117
- model.soft_contact_mu,
2118
- model.particle_adhesion,
2119
- model.soft_contact_count,
2120
- model.soft_contact_particle,
2121
- model.soft_contact_shape,
2122
- model.soft_contact_body_pos,
2123
- model.soft_contact_body_vel,
2124
- model.soft_contact_normal,
2125
- model.soft_contact_max,
3096
+ model.joint_type,
3097
+ model.joint_enabled,
3098
+ model.joint_parent,
3099
+ model.joint_child,
3100
+ model.joint_X_p,
3101
+ model.joint_X_c,
3102
+ model.joint_limit_lower,
3103
+ model.joint_limit_upper,
3104
+ model.joint_axis_start,
3105
+ model.joint_axis_dim,
3106
+ model.joint_axis_mode,
3107
+ model.joint_axis,
3108
+ control.joint_act,
3109
+ model.joint_target_ke,
3110
+ model.joint_target_kd,
3111
+ model.joint_linear_compliance,
3112
+ model.joint_angular_compliance,
3113
+ self.joint_angular_relaxation,
3114
+ self.joint_linear_relaxation,
2126
3115
  dt,
2127
- self.soft_contact_relaxation,
2128
3116
  ],
2129
- # outputs
2130
- outputs=[deltas, state_out.body_deltas],
3117
+ outputs=[body_deltas],
2131
3118
  device=model.device,
2132
3119
  )
2133
3120
 
2134
- if model.particle_max_radius > 0.0:
2135
- wp.launch(
2136
- kernel=solve_particle_particle_contacts,
2137
- dim=model.particle_count,
2138
- inputs=[
2139
- model.particle_grid.id,
2140
- particle_q,
2141
- particle_qd,
2142
- model.particle_inv_mass,
2143
- model.particle_radius,
2144
- model.particle_flags,
2145
- model.particle_mu,
2146
- model.particle_cohesion,
2147
- model.particle_max_radius,
2148
- dt,
2149
- self.soft_contact_relaxation,
2150
- ],
2151
- outputs=[deltas],
2152
- device=model.device,
2153
- )
3121
+ body_q, body_qd = self.apply_body_deltas(model, state_in, state_out, body_deltas, dt)
2154
3122
 
2155
- # distance constraints
2156
- if model.spring_count:
2157
- wp.launch(
2158
- kernel=solve_springs,
2159
- dim=model.spring_count,
2160
- inputs=[
2161
- particle_q,
2162
- particle_qd,
2163
- model.particle_inv_mass,
2164
- model.spring_indices,
2165
- model.spring_rest_length,
2166
- model.spring_stiffness,
2167
- model.spring_damping,
2168
- dt,
2169
- model.spring_constraint_lambdas,
2170
- ],
2171
- outputs=[deltas],
2172
- device=model.device,
2173
- )
3123
+ # Solve rigid contact constraints
3124
+ if model.rigid_contact_max and (
3125
+ model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
3126
+ ):
3127
+ if self.rigid_contact_con_weighting:
3128
+ rigid_contact_inv_weight.zero_()
3129
+ body_deltas.zero_()
2174
3130
 
2175
- # bending constraints
2176
- if model.edge_count:
2177
3131
  wp.launch(
2178
- kernel=bending_constraint,
2179
- dim=model.edge_count,
3132
+ kernel=solve_body_contact_positions,
3133
+ dim=model.rigid_contact_max,
2180
3134
  inputs=[
2181
- particle_q,
2182
- particle_qd,
2183
- model.particle_inv_mass,
2184
- model.edge_indices,
2185
- model.edge_rest_angle,
2186
- model.edge_bending_properties,
3135
+ body_q,
3136
+ body_qd,
3137
+ model.body_com,
3138
+ model.body_inv_mass,
3139
+ model.body_inv_inertia,
3140
+ model.shape_body,
3141
+ model.rigid_contact_count,
3142
+ model.rigid_contact_point0,
3143
+ model.rigid_contact_point1,
3144
+ model.rigid_contact_offset0,
3145
+ model.rigid_contact_offset1,
3146
+ model.rigid_contact_normal,
3147
+ model.rigid_contact_thickness,
3148
+ model.rigid_contact_shape0,
3149
+ model.rigid_contact_shape1,
3150
+ model.shape_materials,
3151
+ self.rigid_contact_relaxation,
2187
3152
  dt,
2188
- model.edge_constraint_lambdas,
3153
+ model.rigid_contact_torsional_friction,
3154
+ model.rigid_contact_rolling_friction,
2189
3155
  ],
2190
- outputs=[deltas],
2191
- device=model.device,
2192
- )
2193
-
2194
- # tetrahedral FEM
2195
- if model.tet_count:
2196
- wp.launch(
2197
- kernel=solve_tetrahedra,
2198
- dim=model.tet_count,
2199
- inputs=[
2200
- particle_q,
2201
- particle_qd,
2202
- model.particle_inv_mass,
2203
- model.tet_indices,
2204
- model.tet_poses,
2205
- model.tet_activations,
2206
- model.tet_materials,
2207
- dt,
2208
- self.soft_body_relaxation,
3156
+ outputs=[
3157
+ body_deltas,
3158
+ rigid_contact_inv_weight,
2209
3159
  ],
2210
- outputs=[deltas],
2211
3160
  device=model.device,
2212
3161
  )
2213
3162
 
2214
- # apply particle deltas
2215
- if requires_grad:
2216
- new_particle_q = wp.clone(particle_q)
2217
- new_particle_qd = wp.clone(particle_qd)
2218
- else:
2219
- new_particle_q = particle_q
2220
- new_particle_qd = particle_qd
2221
-
2222
- wp.launch(
2223
- kernel=apply_particle_deltas,
2224
- dim=model.particle_count,
2225
- inputs=[
2226
- state_in.particle_q,
2227
- particle_q,
2228
- model.particle_flags,
2229
- deltas,
2230
- dt,
2231
- model.particle_max_velocity,
2232
- ],
2233
- outputs=[new_particle_q, new_particle_qd],
2234
- device=model.device,
2235
- )
2236
-
2237
- if requires_grad:
2238
- particle_q.assign(new_particle_q)
2239
- particle_qd.assign(new_particle_qd)
2240
- else:
2241
- particle_q = new_particle_q
2242
- particle_qd = new_particle_qd
2243
-
2244
- # handle rigid bodies
2245
- # ----------------------------
3163
+ # if model.rigid_contact_count.numpy()[0] > 0:
3164
+ # print("rigid_contact_count:", model.rigid_contact_count.numpy().flatten())
3165
+ # # print("rigid_active_contact_distance:", rigid_active_contact_distance.numpy().flatten())
3166
+ # # print("rigid_active_contact_point0:", rigid_active_contact_point0.numpy().flatten())
3167
+ # # print("rigid_active_contact_point1:", rigid_active_contact_point1.numpy().flatten())
3168
+ # print("body_deltas:", body_deltas.numpy().flatten())
2246
3169
 
2247
- if model.joint_count:
2248
- wp.launch(
2249
- kernel=solve_body_joints,
2250
- dim=model.joint_count,
2251
- inputs=[
2252
- state_out.body_q,
2253
- state_out.body_qd,
2254
- model.body_com,
2255
- model.body_inv_mass,
2256
- model.body_inv_inertia,
2257
- model.joint_type,
2258
- model.joint_enabled,
2259
- model.joint_parent,
2260
- model.joint_child,
2261
- model.joint_X_p,
2262
- model.joint_X_c,
2263
- model.joint_limit_lower,
2264
- model.joint_limit_upper,
2265
- model.joint_axis_start,
2266
- model.joint_axis_dim,
2267
- model.joint_axis_mode,
2268
- model.joint_axis,
2269
- model.joint_target,
2270
- model.joint_target_ke,
2271
- model.joint_target_kd,
2272
- model.joint_linear_compliance,
2273
- model.joint_angular_compliance,
2274
- self.joint_angular_relaxation,
2275
- self.joint_linear_relaxation,
2276
- dt,
2277
- ],
2278
- outputs=[state_out.body_deltas],
2279
- device=model.device,
2280
- )
3170
+ # print(rigid_active_contact_distance.numpy().flatten())
2281
3171
 
2282
- # apply updates
2283
- wp.launch(
2284
- kernel=apply_body_deltas,
2285
- dim=model.body_count,
2286
- inputs=[
2287
- state_out.body_q,
2288
- state_out.body_qd,
2289
- model.body_com,
2290
- model.body_inertia,
2291
- model.body_inv_mass,
2292
- model.body_inv_inertia,
2293
- state_out.body_deltas,
2294
- None,
2295
- dt,
2296
- ],
2297
- outputs=[
2298
- out_body_q,
2299
- out_body_qd,
2300
- ],
2301
- device=model.device,
2302
- )
3172
+ if self.enable_restitution and i == 0:
3173
+ # remember contact constraint weighting from the first iteration
3174
+ if self.rigid_contact_con_weighting:
3175
+ rigid_contact_inv_weight_init = wp.clone(rigid_contact_inv_weight)
3176
+ else:
3177
+ rigid_contact_inv_weight_init = None
2303
3178
 
2304
- if model.body_count and requires_grad:
2305
- # update state
2306
- state_out.body_q.assign(out_body_q)
2307
- state_out.body_qd.assign(out_body_qd)
2308
-
2309
- # Solve rigid contact constraints
2310
- if model.rigid_contact_max and (
2311
- model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
2312
- ):
2313
- rigid_contact_inv_weight = None
2314
- if requires_grad:
2315
- body_deltas = wp.zeros_like(state_out.body_deltas)
2316
- rigid_active_contact_distance = wp.zeros_like(model.rigid_active_contact_distance)
2317
- rigid_active_contact_point0 = wp.empty_like(
2318
- model.rigid_active_contact_point0, requires_grad=True
2319
- )
2320
- rigid_active_contact_point1 = wp.empty_like(
2321
- model.rigid_active_contact_point1, requires_grad=True
3179
+ body_q, body_qd = self.apply_body_deltas(
3180
+ model, state_in, state_out, body_deltas, dt, rigid_contact_inv_weight
2322
3181
  )
2323
- if self.rigid_contact_con_weighting:
2324
- rigid_contact_inv_weight = wp.zeros_like(model.rigid_contact_inv_weight)
2325
- else:
2326
- body_deltas = state_out.body_deltas
2327
- body_deltas.zero_()
2328
- rigid_active_contact_distance = model.rigid_active_contact_distance
2329
- rigid_active_contact_point0 = model.rigid_active_contact_point0
2330
- rigid_active_contact_point1 = model.rigid_active_contact_point1
2331
- rigid_active_contact_distance.zero_()
2332
- if self.rigid_contact_con_weighting:
2333
- rigid_contact_inv_weight = model.rigid_contact_inv_weight
2334
- rigid_contact_inv_weight.zero_()
2335
3182
 
2336
- wp.launch(
2337
- kernel=solve_body_contact_positions,
2338
- dim=model.rigid_contact_max,
2339
- inputs=[
2340
- state_out.body_q,
2341
- state_out.body_qd,
2342
- model.body_com,
2343
- model.body_inv_mass,
2344
- model.body_inv_inertia,
2345
- model.rigid_contact_count,
2346
- model.rigid_contact_body0,
2347
- model.rigid_contact_body1,
2348
- model.rigid_contact_point0,
2349
- model.rigid_contact_point1,
2350
- model.rigid_contact_offset0,
2351
- model.rigid_contact_offset1,
2352
- model.rigid_contact_normal,
2353
- model.rigid_contact_thickness,
2354
- model.rigid_contact_shape0,
2355
- model.rigid_contact_shape1,
2356
- model.shape_materials,
2357
- self.rigid_contact_relaxation,
2358
- dt,
2359
- model.rigid_contact_torsional_friction,
2360
- model.rigid_contact_rolling_friction,
2361
- ],
2362
- outputs=[
2363
- body_deltas,
2364
- rigid_active_contact_point0,
2365
- rigid_active_contact_point1,
2366
- rigid_active_contact_distance,
2367
- rigid_contact_inv_weight,
2368
- ],
2369
- device=model.device,
2370
- )
2371
-
2372
- if self.enable_restitution and i == 0:
2373
- # remember the contacts from the first iteration
2374
- if requires_grad:
2375
- model.rigid_active_contact_distance_prev = wp.clone(rigid_active_contact_distance)
2376
- model.rigid_active_contact_point0_prev = wp.clone(rigid_active_contact_point0)
2377
- model.rigid_active_contact_point1_prev = wp.clone(rigid_active_contact_point1)
2378
- if self.rigid_contact_con_weighting:
2379
- model.rigid_contact_inv_weight_prev = wp.clone(rigid_contact_inv_weight)
2380
- else:
2381
- model.rigid_contact_inv_weight_prev = None
2382
- else:
2383
- model.rigid_active_contact_distance_prev.assign(rigid_active_contact_distance)
2384
- model.rigid_active_contact_point0_prev.assign(rigid_active_contact_point0)
2385
- model.rigid_active_contact_point1_prev.assign(rigid_active_contact_point1)
2386
- if self.rigid_contact_con_weighting:
2387
- model.rigid_contact_inv_weight_prev.assign(rigid_contact_inv_weight)
2388
- else:
2389
- model.rigid_contact_inv_weight_prev = None
2390
-
2391
- if requires_grad:
2392
- model.rigid_active_contact_distance = rigid_active_contact_distance
2393
- model.rigid_active_contact_point0 = rigid_active_contact_point0
2394
- model.rigid_active_contact_point1 = rigid_active_contact_point1
2395
- body_q = wp.clone(state_out.body_q)
2396
- body_qd = wp.clone(state_out.body_qd)
2397
- else:
2398
- body_q = state_out.body_q
2399
- body_qd = state_out.body_qd
2400
-
2401
- # apply updates
2402
- wp.launch(
2403
- kernel=apply_body_deltas,
2404
- dim=model.body_count,
2405
- inputs=[
2406
- state_out.body_q,
2407
- state_out.body_qd,
2408
- model.body_com,
2409
- model.body_inertia,
2410
- model.body_inv_mass,
2411
- model.body_inv_inertia,
2412
- body_deltas,
2413
- rigid_contact_inv_weight,
2414
- dt,
2415
- ],
2416
- outputs=[
2417
- body_q,
2418
- body_qd,
2419
- ],
2420
- device=model.device,
2421
- )
3183
+ if model.particle_count:
3184
+ if particle_q.ptr != state_out.particle_q.ptr:
3185
+ state_out.particle_q.assign(particle_q)
3186
+ state_out.particle_qd.assign(particle_qd)
2422
3187
 
2423
- if requires_grad:
2424
- state_out.body_q = body_q
2425
- state_out.body_qd = body_qd
3188
+ if model.body_count:
3189
+ if body_q.ptr != state_out.body_q.ptr:
3190
+ state_out.body_q.assign(body_q)
3191
+ state_out.body_qd.assign(body_qd)
2426
3192
 
2427
3193
  # update body velocities from position changes
2428
- if model.body_count and not requires_grad:
3194
+ if self.compute_body_velocity_from_position_delta and model.body_count and not requires_grad:
2429
3195
  # causes gradient issues (probably due to numerical problems
2430
3196
  # when computing velocities from position changes)
2431
3197
  if requires_grad:
@@ -2437,78 +3203,98 @@ class XPBDIntegrator:
2437
3203
  wp.launch(
2438
3204
  kernel=update_body_velocities,
2439
3205
  dim=model.body_count,
2440
- inputs=[state_out.body_q, state_in.body_q, model.body_com, dt],
3206
+ inputs=[state_out.body_q, body_q_init, model.body_com, dt],
2441
3207
  outputs=[out_body_qd],
2442
3208
  device=model.device,
2443
3209
  )
2444
3210
 
2445
- if requires_grad:
2446
- state_out.body_qd.assign(out_body_qd)
2447
-
2448
3211
  if self.enable_restitution:
2449
3212
  if model.particle_count:
2450
- if requires_grad:
2451
- new_particle_qd = wp.clone(particle_qd)
2452
- else:
2453
- new_particle_qd = particle_qd
2454
-
2455
3213
  wp.launch(
2456
- kernel=apply_soft_restitution_ground,
3214
+ kernel=apply_particle_shape_restitution,
2457
3215
  dim=model.particle_count,
2458
3216
  inputs=[
2459
3217
  particle_q,
2460
3218
  particle_qd,
2461
- state_in.particle_q,
2462
- state_in.particle_qd,
3219
+ self.particle_q_init,
3220
+ self.particle_qd_init,
2463
3221
  model.particle_inv_mass,
2464
3222
  model.particle_radius,
2465
3223
  model.particle_flags,
3224
+ body_q,
3225
+ body_qd,
3226
+ model.body_com,
3227
+ model.body_inv_mass,
3228
+ model.body_inv_inertia,
3229
+ model.shape_body,
3230
+ model.shape_materials,
3231
+ model.particle_adhesion,
2466
3232
  model.soft_contact_restitution,
2467
- model.ground_plane,
3233
+ model.soft_contact_count,
3234
+ model.soft_contact_particle,
3235
+ model.soft_contact_shape,
3236
+ model.soft_contact_body_pos,
3237
+ model.soft_contact_body_vel,
3238
+ model.soft_contact_normal,
3239
+ model.soft_contact_max,
2468
3240
  dt,
2469
3241
  self.soft_contact_relaxation,
2470
3242
  ],
2471
- outputs=[new_particle_qd],
3243
+ outputs=[state_out.particle_qd],
2472
3244
  device=model.device,
2473
3245
  )
2474
-
2475
- if requires_grad:
2476
- particle_qd.assign(new_particle_qd)
2477
- else:
2478
- particle_qd = new_particle_qd
3246
+ if model.ground:
3247
+ wp.launch(
3248
+ kernel=apply_particle_ground_restitution,
3249
+ dim=model.particle_count,
3250
+ inputs=[
3251
+ particle_q,
3252
+ particle_qd,
3253
+ self.particle_q_init,
3254
+ self.particle_qd_init,
3255
+ model.particle_inv_mass,
3256
+ model.particle_radius,
3257
+ model.particle_flags,
3258
+ model.particle_adhesion,
3259
+ model.soft_contact_restitution,
3260
+ model.ground_plane,
3261
+ dt,
3262
+ self.soft_contact_relaxation,
3263
+ ],
3264
+ outputs=[state_out.particle_qd],
3265
+ device=model.device,
3266
+ )
2479
3267
 
2480
3268
  if model.body_count:
2481
- if requires_grad:
2482
- state_out.body_deltas = wp.zeros_like(state_out.body_deltas)
2483
- else:
2484
- state_out.body_deltas.zero_()
3269
+ body_deltas.zero_()
2485
3270
  wp.launch(
2486
3271
  kernel=apply_rigid_restitution,
2487
3272
  dim=model.rigid_contact_max,
2488
3273
  inputs=[
2489
3274
  state_out.body_q,
2490
3275
  state_out.body_qd,
2491
- state_in.body_q,
2492
- state_in.body_qd,
3276
+ body_q_init,
3277
+ body_qd_init,
2493
3278
  model.body_com,
2494
3279
  model.body_inv_mass,
2495
3280
  model.body_inv_inertia,
3281
+ model.shape_body,
2496
3282
  model.rigid_contact_count,
2497
- model.rigid_contact_body0,
2498
- model.rigid_contact_body1,
2499
3283
  model.rigid_contact_normal,
2500
3284
  model.rigid_contact_shape0,
2501
3285
  model.rigid_contact_shape1,
2502
3286
  model.shape_materials,
2503
- model.rigid_active_contact_distance_prev,
2504
- model.rigid_active_contact_point0_prev,
2505
- model.rigid_active_contact_point1_prev,
2506
- model.rigid_contact_inv_weight_prev,
3287
+ model.rigid_contact_point0,
3288
+ model.rigid_contact_point1,
3289
+ model.rigid_contact_offset0,
3290
+ model.rigid_contact_offset1,
3291
+ model.rigid_contact_thickness,
3292
+ rigid_contact_inv_weight_init,
2507
3293
  model.gravity,
2508
3294
  dt,
2509
3295
  ],
2510
3296
  outputs=[
2511
- state_out.body_deltas,
3297
+ body_deltas,
2512
3298
  ],
2513
3299
  device=model.device,
2514
3300
  )
@@ -2517,16 +3303,10 @@ class XPBDIntegrator:
2517
3303
  kernel=apply_body_delta_velocities,
2518
3304
  dim=model.body_count,
2519
3305
  inputs=[
2520
- state_out.body_qd,
2521
- state_out.body_deltas,
3306
+ body_deltas,
2522
3307
  ],
2523
3308
  outputs=[state_out.body_qd],
2524
3309
  device=model.device,
2525
3310
  )
2526
3311
 
2527
- if model.particle_count:
2528
- # update particle state
2529
- state_out.particle_q.assign(particle_q)
2530
- state_out.particle_qd.assign(particle_qd)
2531
-
2532
3312
  return state_out