2020 unit_quat_conj_apply_jacobian ,
2121 unit_quat_conj_to_rotation_matrix ,
2222)
23+ from ...core .types import mat34f
2324from ...kinematics .joints import (
2425 correct_quat_vector_coord ,
2526 correct_rotational_coord ,
@@ -112,6 +113,127 @@ def _resolve_fk_actuation_type(act_type: wp.int32, fk_act_flag: wp.int32) -> wp.
112113 return act_type
113114
114115
116+ @wp .func
117+ def _load_joint_poses (
118+ base_id : wp .int32 , follower_id : wp .int32 , bodies_q : wp .array [wp .transformf ]
119+ ) -> tuple [wp .vec3f , wp .quatf , wp .vec3f , wp .quatf ]:
120+ """Load the base and follower poses, using the identity pose for the world."""
121+ c_base = wp .vec3f (0.0 , 0.0 , 0.0 )
122+ q_base = wp .quatf (0.0 , 0.0 , 0.0 , 1.0 )
123+ if base_id >= 0 :
124+ c_base = wp .transform_get_translation (bodies_q [base_id ])
125+ q_base = wp .transform_get_rotation (bodies_q [base_id ])
126+ c_follower = wp .transform_get_translation (bodies_q [follower_id ])
127+ q_follower = wp .transform_get_rotation (bodies_q [follower_id ])
128+ return c_base , q_base , c_follower , q_follower
129+
130+
131+ @wp .func
132+ def _get_reduced_constraint_ids (
133+ joint_id : wp .int32 , ct_full_to_red_map : wp .array [wp .int32 ]
134+ ) -> tuple [wp .vec3i , wp .vec3i ]:
135+ """Load the translational and rotational reduced constraint ids for a joint."""
136+ first_ct_id_full = 6 * joint_id
137+ trans_ct_ids_red = wp .vec3i (
138+ ct_full_to_red_map [first_ct_id_full ],
139+ ct_full_to_red_map [first_ct_id_full + 1 ],
140+ ct_full_to_red_map [first_ct_id_full + 2 ],
141+ )
142+ rot_ct_ids_red = wp .vec3i (
143+ ct_full_to_red_map [first_ct_id_full + 3 ],
144+ ct_full_to_red_map [first_ct_id_full + 4 ],
145+ ct_full_to_red_map [first_ct_id_full + 5 ],
146+ )
147+ return trans_ct_ids_red , rot_ct_ids_red
148+
149+
150+ @wp .func
151+ def _eval_translation_jacobian_blocks (
152+ X_T : wp .mat33f ,
153+ q_base : wp .quatf ,
154+ q_follower : wp .quatf ,
155+ x_follower : wp .vec3f ,
156+ c_base : wp .vec3f ,
157+ c_follower : wp .vec3f ,
158+ has_base : wp .bool ,
159+ ) -> tuple [wp .mat33f , mat34f , wp .mat33f , mat34f ]:
160+ """Evaluate the base and follower blocks of a translational constraint Jacobian."""
161+ X_T_R_base_T = X_T * unit_quat_conj_to_rotation_matrix (q_base )
162+ jac_trans_c_base = wp .mat33f (0.0 )
163+ jac_trans_q_base = mat34f (0.0 )
164+ if has_base :
165+ jac_trans_c_base = - X_T_R_base_T
166+ delta_pos = unit_quat_apply (q_follower , x_follower ) + c_follower - c_base
167+ jac_trans_q_base = X_T * unit_quat_conj_apply_jacobian (q_base , delta_pos )
168+ jac_trans_c_follower = X_T_R_base_T
169+ jac_trans_q_follower = X_T_R_base_T * unit_quat_apply_jacobian (q_follower , x_follower )
170+ return jac_trans_c_base , jac_trans_q_base , jac_trans_c_follower , jac_trans_q_follower
171+
172+
173+ @wp .func
174+ def _eval_rotation_jacobian_blocks (
175+ X_T : wp .mat33f ,
176+ q_base : wp .quatf ,
177+ q_follower : wp .quatf ,
178+ q_rel_body : wp .quatf ,
179+ has_base : wp .bool ,
180+ ) -> tuple [mat34f , mat34f ]:
181+ """Evaluate the base and follower blocks of a rotational constraint Jacobian."""
182+ q_base_sq_norm = wp .dot (q_base , q_base )
183+ q_follower_sq_norm = wp .dot (q_follower , q_follower )
184+ R_base_T = unit_quat_conj_to_rotation_matrix (q_base / wp .sqrt (q_base_sq_norm ))
185+ q_rel = q_follower * wp .quat_inverse (q_rel_body ) * wp .quat_inverse (q_base )
186+ temp = X_T * R_base_T * quat_left_jacobian_inverse (q_rel )
187+ jac_rot_q_base = mat34f (0.0 )
188+ if has_base :
189+ jac_rot_q_base = (- 2.0 / q_base_sq_norm ) * temp * G_of (q_base )
190+ jac_rot_q_follower = (2.0 / q_follower_sq_norm ) * temp * G_of (q_follower )
191+ return jac_rot_q_base , jac_rot_q_follower
192+
193+
194+ @wp .func
195+ def _eval_passive_universal_jacobian_blocks (
196+ X_T : wp .mat33f , q_base : wp .quatf , q_follower : wp .quatf , has_base : wp .bool
197+ ) -> tuple [wp .vec4f , wp .vec4f ]:
198+ """Evaluate the base and follower blocks of a passive universal constraint Jacobian."""
199+ a_x = X_T [0 ]
200+ a_y = X_T [1 ]
201+ jac_q_base = wp .vec4f (0.0 )
202+ if has_base :
203+ a_y_follower = unit_quat_apply (q_follower , a_y )
204+ jac_q_base = - a_y_follower * unit_quat_apply_jacobian (q_base , a_x )
205+ a_x_base = unit_quat_apply (q_base , a_x )
206+ jac_q_follower = - a_x_base * unit_quat_apply_jacobian (q_follower , a_y )
207+ return jac_q_base , jac_q_follower
208+
209+
210+ @wp .func
211+ def _correct_rotational_actuator_coord (
212+ actuators_q : wp .array [wp .float32 ], actuators_q_ref : wp .array [wp .float32 ], coord_id : wp .int32
213+ ):
214+ """Correct an angular actuator coordinate against its reference."""
215+ actuators_q [coord_id ] = correct_rotational_coord (actuators_q [coord_id ], actuators_q_ref [coord_id ])
216+
217+
218+ @wp .func
219+ def _correct_quat_actuator_coords (
220+ actuators_q : wp .array [wp .float32 ], actuators_q_ref : wp .array [wp .float32 ], coord_id : wp .int32
221+ ):
222+ """Correct four quaternion actuator coordinates against their reference."""
223+ quat = wp .vec4f (
224+ actuators_q [coord_id ], actuators_q [coord_id + 1 ], actuators_q [coord_id + 2 ], actuators_q [coord_id + 3 ]
225+ )
226+ quat_ref = wp .vec4f (
227+ actuators_q_ref [coord_id ],
228+ actuators_q_ref [coord_id + 1 ],
229+ actuators_q_ref [coord_id + 2 ],
230+ actuators_q_ref [coord_id + 3 ],
231+ )
232+ quat_corrected = correct_quat_vector_coord (quat , quat_ref )
233+ for i in range (4 ):
234+ actuators_q [coord_id + i ] = quat_corrected [i ]
235+
236+
115237###
116238# Kernels
117239###
@@ -481,15 +603,8 @@ def _eval_actuator_coords(
481603
482604 # Get base and follower transformations
483605 base_id = joints_bid_B [jt_id ]
484- if base_id < 0 :
485- c_base = wp .vec3f (0.0 , 0.0 , 0.0 )
486- q_base = wp .quatf (0.0 , 0.0 , 0.0 , 1.0 )
487- else :
488- c_base = wp .transform_get_translation (bodies_q [base_id ])
489- q_base = wp .transform_get_rotation (bodies_q [base_id ])
490606 follower_id = joints_bid_F [jt_id ]
491- c_follower = wp .transform_get_translation (bodies_q [follower_id ])
492- q_follower = wp .transform_get_rotation (bodies_q [follower_id ])
607+ c_base , q_base , c_follower , q_follower = _load_joint_poses (base_id , follower_id , bodies_q )
493608
494609 # Compute relative pose of follower body in joint frame of base body
495610 pos_base = c_base + wp .quat_rotate (q_base , x_base )
@@ -540,46 +655,16 @@ def _correct_actuator_coords(
540655 ): # No correction needed
541656 return
542657 elif dof_type == FKJointDoFType .CYLINDRICAL : # Correct angle up to +/- 2 pi
543- angle = actuators_q [coord_id + 1 ]
544- angle_ref = actuators_q_ref [coord_id + 1 ]
545- actuators_q [coord_id + 1 ] = correct_rotational_coord (angle , angle_ref )
658+ _correct_rotational_actuator_coord (actuators_q , actuators_q_ref , coord_id + 1 )
546659 elif dof_type == FKJointDoFType .FREE : # Correct quaternion up to sign
547- quat = wp .vec4f (
548- actuators_q [coord_id + 3 ], actuators_q [coord_id + 4 ], actuators_q [coord_id + 5 ], actuators_q [coord_id + 6 ]
549- )
550- quat_ref = wp .vec4f (
551- actuators_q_ref [coord_id + 3 ],
552- actuators_q_ref [coord_id + 4 ],
553- actuators_q_ref [coord_id + 5 ],
554- actuators_q_ref [coord_id + 6 ],
555- )
556- quat_corrected = correct_quat_vector_coord (quat , quat_ref )
557- for i in range (4 ):
558- actuators_q [coord_id + 3 + i ] = quat_corrected [i ]
660+ _correct_quat_actuator_coords (actuators_q , actuators_q_ref , coord_id + 3 )
559661 elif dof_type == FKJointDoFType .REVOLUTE : # Correct angle up to +/- 2 pi
560- angle = actuators_q [coord_id ]
561- angle_ref = actuators_q_ref [coord_id ]
562- actuators_q [coord_id ] = correct_rotational_coord (angle , angle_ref )
662+ _correct_rotational_actuator_coord (actuators_q , actuators_q_ref , coord_id )
563663 elif dof_type == FKJointDoFType .SPHERICAL : # Correct quaternion up to sign
564- quat = wp .vec4f (
565- actuators_q [coord_id ], actuators_q [coord_id + 1 ], actuators_q [coord_id + 2 ], actuators_q [coord_id + 3 ]
566- )
567- quat_ref = wp .vec4f (
568- actuators_q_ref [coord_id ],
569- actuators_q_ref [coord_id + 1 ],
570- actuators_q_ref [coord_id + 2 ],
571- actuators_q_ref [coord_id + 3 ],
572- )
573- quat_corrected = correct_quat_vector_coord (quat , quat_ref )
574- for i in range (4 ):
575- actuators_q [coord_id + i ] = quat_corrected [i ]
664+ _correct_quat_actuator_coords (actuators_q , actuators_q_ref , coord_id )
576665 elif dof_type == FKJointDoFType .UNIVERSAL : # Correct angles up to +/- 2 pi
577- angle = actuators_q [coord_id ]
578- angle_ref = actuators_q_ref [coord_id ]
579- actuators_q [coord_id ] = correct_rotational_coord (angle , angle_ref )
580- angle = actuators_q [coord_id + 1 ]
581- angle_ref = actuators_q_ref [coord_id + 1 ]
582- actuators_q [coord_id + 1 ] = correct_rotational_coord (angle , angle_ref )
666+ _correct_rotational_actuator_coord (actuators_q , actuators_q_ref , coord_id )
667+ _correct_rotational_actuator_coord (actuators_q , actuators_q_ref , coord_id + 1 )
583668 else :
584669 assert False , "Unexpected actuator dof type" # noqa: B011
585670
@@ -929,17 +1014,7 @@ def _eval_joint_constraints(
9291014 jt_id_tot = first_joint_id [wd_id ] + jt_id_loc
9301015
9311016 # Get reduced constraint ids (-1 meaning constraint is not used)
932- first_ct_id_full = 6 * jt_id_tot
933- trans_ct_ids_red = wp .vec3i (
934- ct_full_to_red_map [first_ct_id_full ],
935- ct_full_to_red_map [first_ct_id_full + 1 ],
936- ct_full_to_red_map [first_ct_id_full + 2 ],
937- )
938- rot_ct_ids_red = wp .vec3i (
939- ct_full_to_red_map [first_ct_id_full + 3 ],
940- ct_full_to_red_map [first_ct_id_full + 4 ],
941- ct_full_to_red_map [first_ct_id_full + 5 ],
942- )
1017+ trans_ct_ids_red , rot_ct_ids_red = _get_reduced_constraint_ids (jt_id_tot , ct_full_to_red_map )
9431018
9441019 # Get joint local positions and orientation
9451020 x_base = joints_B_r_B [jt_id_tot ]
@@ -948,15 +1023,8 @@ def _eval_joint_constraints(
9481023
9491024 # Get base and follower transformations
9501025 base_id = joints_bid_B [jt_id_tot ]
951- if base_id < 0 :
952- c_base = wp .vec3f (0.0 , 0.0 , 0.0 )
953- q_base = wp .quatf (0.0 , 0.0 , 0.0 , 1.0 )
954- else :
955- c_base = wp .transform_get_translation (bodies_q [base_id ])
956- q_base = wp .transform_get_rotation (bodies_q [base_id ])
9571026 follower_id = joints_bid_F [jt_id_tot ]
958- c_follower = wp .transform_get_translation (bodies_q [follower_id ])
959- q_follower = wp .transform_get_rotation (bodies_q [follower_id ])
1027+ c_base , q_base , c_follower , q_follower = _load_joint_poses (base_id , follower_id , bodies_q )
9601028
9611029 # Get target relative transformation, in joint/body frame for translation/rotation part
9621030 t_rel_joint = wp .transform_get_translation (target_rel_transforms [jt_id_tot ])
@@ -1141,57 +1209,32 @@ def _eval_joint_constraints_jacobian(
11411209 jt_id_tot = first_joint_id [wd_id ] + jt_id_loc
11421210
11431211 # Get reduced constraint ids (-1 meaning constraint is not used)
1144- first_ct_id_full = 6 * jt_id_tot
1145- trans_ct_ids_red = wp .vec3i (
1146- ct_full_to_red_map [first_ct_id_full ],
1147- ct_full_to_red_map [first_ct_id_full + 1 ],
1148- ct_full_to_red_map [first_ct_id_full + 2 ],
1149- )
1150- rot_ct_ids_red = wp .vec3i (
1151- ct_full_to_red_map [first_ct_id_full + 3 ],
1152- ct_full_to_red_map [first_ct_id_full + 4 ],
1153- ct_full_to_red_map [first_ct_id_full + 5 ],
1154- )
1212+ trans_ct_ids_red , rot_ct_ids_red = _get_reduced_constraint_ids (jt_id_tot , ct_full_to_red_map )
11551213
11561214 # Get joint local positions and orientation
11571215 x_follower = joints_F_r_F [jt_id_tot ]
11581216 X_T = wp .transpose (joints_X_Bj [jt_id_tot ])
11591217
11601218 # Get base and follower transformations
11611219 base_id_tot = joints_bid_B [jt_id_tot ]
1162- if base_id_tot < 0 :
1163- c_base = wp .vec3f (0.0 , 0.0 , 0.0 )
1164- q_base = wp .quatf (0.0 , 0.0 , 0.0 , 1.0 )
1165- else :
1166- c_base = wp .transform_get_translation (bodies_q [base_id_tot ])
1167- q_base = wp .transform_get_rotation (bodies_q [base_id_tot ])
11681220 follower_id_tot = joints_bid_F [jt_id_tot ]
1169- c_follower = wp .transform_get_translation (bodies_q [follower_id_tot ])
1170- q_follower = wp .transform_get_rotation (bodies_q [follower_id_tot ])
1221+ c_base , q_base , c_follower , q_follower = _load_joint_poses (base_id_tot , follower_id_tot , bodies_q )
11711222 base_id_loc = base_id_tot - first_body_id [wd_id ]
11721223 follower_id_loc = follower_id_tot - first_body_id [wd_id ]
11731224
11741225 # Get target relative transformation (rotation part only, as translation part doesn't affect the Jacobian)
11751226 q_rel_body = wp .transform_get_rotation (target_rel_transforms [jt_id_tot ])
11761227
11771228 # Translation constraints
1178- X_T_R_base_T = X_T * unit_quat_conj_to_rotation_matrix (q_base )
1179- if base_id_tot >= 0 :
1180- jac_trans_c_base = - X_T_R_base_T
1181- delta_pos = unit_quat_apply (q_follower , x_follower ) + c_follower - c_base
1182- jac_trans_q_base = X_T * unit_quat_conj_apply_jacobian (q_base , delta_pos )
1183- jac_trans_c_follower = X_T_R_base_T
1184- jac_trans_q_follower = X_T_R_base_T * unit_quat_apply_jacobian (q_follower , x_follower )
1185-
1229+ jac_trans_c_base , jac_trans_q_base , jac_trans_c_follower , jac_trans_q_follower = (
1230+ _eval_translation_jacobian_blocks (
1231+ X_T , q_base , q_follower , x_follower , c_base , c_follower , base_id_tot >= 0
1232+ )
1233+ )
11861234 # Rotation constraints
1187- q_base_sq_norm = wp .dot (q_base , q_base )
1188- q_follower_sq_norm = wp .dot (q_follower , q_follower )
1189- R_base_T = unit_quat_conj_to_rotation_matrix (q_base / wp .sqrt (q_base_sq_norm ))
1190- q_rel = q_follower * wp .quat_inverse (q_rel_body ) * wp .quat_inverse (q_base )
1191- temp = X_T * R_base_T * quat_left_jacobian_inverse (q_rel )
1192- if base_id_tot >= 0 :
1193- jac_rot_q_base = (- 2.0 / q_base_sq_norm ) * temp * G_of (q_base )
1194- jac_rot_q_follower = (2.0 / q_follower_sq_norm ) * temp * G_of (q_follower )
1235+ jac_rot_q_base , jac_rot_q_follower = _eval_rotation_jacobian_blocks (
1236+ X_T , q_base , q_follower , q_rel_body , base_id_tot >= 0
1237+ )
11951238 # Note: we need X^T * R_base^T both for translation and rotation constraints, but to get the correct
11961239 # derivatives for non-unit quaternions (which may be encountered before convergence) we end up needing
11971240 # to use a separate formula to evaluate R_base in either case
@@ -1228,13 +1271,9 @@ def _eval_joint_constraints_jacobian(
12281271 return
12291272
12301273 # Compute constraint Jacobian (cross product between x axis on base and y axis on follower)
1231- a_x = X_T [0 ]
1232- a_y = X_T [1 ]
1233- if base_id_tot >= 0 :
1234- a_y_follower = unit_quat_apply (q_follower , a_y )
1235- jac_q_base = - a_y_follower * unit_quat_apply_jacobian (q_base , a_x )
1236- a_x_base = unit_quat_apply (q_base , a_x )
1237- jac_q_follower = - a_x_base * unit_quat_apply_jacobian (q_follower , a_y )
1274+ jac_q_base , jac_q_follower = _eval_passive_universal_jacobian_blocks (
1275+ X_T , q_base , q_follower , base_id_tot >= 0
1276+ )
12381277
12391278 # Write out Jacobian
12401279 for i in range (4 ):
@@ -1319,37 +1358,20 @@ def _eval_joint_constraints_sparse_jacobian(
13191358
13201359 # Get base and follower transformations
13211360 base_id = joints_bid_B [jt_id_tot ]
1322- if base_id < 0 :
1323- c_base = wp .vec3f (0.0 , 0.0 , 0.0 )
1324- q_base = wp .quatf (0.0 , 0.0 , 0.0 , 1.0 )
1325- else :
1326- c_base = wp .transform_get_translation (bodies_q [base_id ])
1327- q_base = wp .transform_get_rotation (bodies_q [base_id ])
13281361 follower_id = joints_bid_F [jt_id_tot ]
1329- c_follower = wp .transform_get_translation (bodies_q [follower_id ])
1330- q_follower = wp .transform_get_rotation (bodies_q [follower_id ])
1362+ c_base , q_base , c_follower , q_follower = _load_joint_poses (base_id , follower_id , bodies_q )
13311363
13321364 # Get target relative transformation (rotation part only, as translation part doesn't affect the Jacobian)
13331365 q_rel_body = wp .transform_get_rotation (target_rel_transforms [jt_id_tot ])
13341366
13351367 # Translation constraints
1336- X_T_R_base_T = X_T * unit_quat_conj_to_rotation_matrix (q_base )
1337- if base_id >= 0 :
1338- jac_trans_c_base = - X_T_R_base_T
1339- delta_pos = unit_quat_apply (q_follower , x_follower ) + c_follower - c_base
1340- jac_trans_q_base = X_T * unit_quat_conj_apply_jacobian (q_base , delta_pos )
1341- jac_trans_c_follower = X_T_R_base_T
1342- jac_trans_q_follower = X_T_R_base_T * unit_quat_apply_jacobian (q_follower , x_follower )
1343-
1368+ jac_trans_c_base , jac_trans_q_base , jac_trans_c_follower , jac_trans_q_follower = (
1369+ _eval_translation_jacobian_blocks (X_T , q_base , q_follower , x_follower , c_base , c_follower , base_id >= 0 )
1370+ )
13441371 # Rotation constraints
1345- q_base_sq_norm = wp .dot (q_base , q_base )
1346- q_follower_sq_norm = wp .dot (q_follower , q_follower )
1347- R_base_T = unit_quat_conj_to_rotation_matrix (q_base / wp .sqrt (q_base_sq_norm ))
1348- q_rel = q_follower * wp .quat_inverse (q_rel_body ) * wp .quat_inverse (q_base )
1349- temp = X_T * R_base_T * quat_left_jacobian_inverse (q_rel )
1350- if base_id >= 0 :
1351- jac_rot_q_base = (- 2.0 / q_base_sq_norm ) * temp * G_of (q_base )
1352- jac_rot_q_follower = (2.0 / q_follower_sq_norm ) * temp * G_of (q_follower )
1372+ jac_rot_q_base , jac_rot_q_follower = _eval_rotation_jacobian_blocks (
1373+ X_T , q_base , q_follower , q_rel_body , base_id >= 0
1374+ )
13531375 # Note: we need X^T * R_base^T both for translation and rotation constraints, but to get the correct
13541376 # derivatives for non-unit quaternions (which may be encountered before convergence) we end up needing
13551377 # to use a separate formula to evaluate R_base in either case
@@ -1390,13 +1412,9 @@ def _eval_joint_constraints_sparse_jacobian(
13901412 return
13911413
13921414 # Compute constraint Jacobian (cross product between x axis on base and y axis on follower)
1393- a_x = X_T [0 ]
1394- a_y = X_T [1 ]
1395- if base_id >= 0 :
1396- a_y_follower = unit_quat_apply (q_follower , a_y )
1397- jac_q_base = - a_y_follower * unit_quat_apply_jacobian (q_base , a_x )
1398- a_x_base = unit_quat_apply (q_base , a_x )
1399- jac_q_follower = - a_x_base * unit_quat_apply_jacobian (q_follower , a_y )
1415+ jac_q_base , jac_q_follower = _eval_passive_universal_jacobian_blocks (
1416+ X_T , q_base , q_follower , base_id >= 0
1417+ )
14001418
14011419 # Write out Jacobian
14021420 if base_id >= 0 :
0 commit comments