|
59 | 59 | ### |
60 | 60 |
|
61 | 61 |
|
| 62 | +@wp.func |
| 63 | +def build_full_joint_jacobian( |
| 64 | + joint_id: wp.int32, |
| 65 | + dof_type: wp.int32, |
| 66 | + bid_B: wp.int32, |
| 67 | + bid_F: wp.int32, |
| 68 | + model_joints_X_Bj: wp.array[wp.mat33f], |
| 69 | + model_joints_X_Fj: wp.array[wp.mat33f], |
| 70 | + state_joints_p: wp.array[wp.transformf], |
| 71 | + state_bodies_q: wp.array[wp.transformf], |
| 72 | +): |
| 73 | + """ |
| 74 | + Computes the full (6x6) joint Jacobian (constraint and DoFs) for a specific joint. |
| 75 | + """ |
| 76 | + # Retrieve the pose transform of the joint |
| 77 | + T_j = state_joints_p[joint_id] |
| 78 | + r_j = wp.transform_get_translation(T_j) |
| 79 | + R_X_j = wp.quat_to_matrix(wp.transform_get_rotation(T_j)) |
| 80 | + |
| 81 | + # Retrieve the pose transforms of each body |
| 82 | + T_B_j = wp.transform_identity() |
| 83 | + if bid_B > -1: |
| 84 | + T_B_j = state_bodies_q[bid_B] |
| 85 | + T_F_j = state_bodies_q[bid_F] |
| 86 | + r_B_j = wp.transform_get_translation(T_B_j) |
| 87 | + r_F_j = wp.transform_get_translation(T_F_j) |
| 88 | + |
| 89 | + if dof_type == JointDoFType.FREE: |
| 90 | + # By Newton's convention, a free joint's twist and wrench (specified in `joint_qd` and |
| 91 | + # `joint_f`) are both defined at the child's center of mass, with world-aligned axes. Since |
| 92 | + # Kamino avoids a conversion, the change of reference frame needs to be taken into account |
| 93 | + # in the Jacobian. |
| 94 | + JT_F_j = wp.identity(n=6, dtype=wp.float32) |
| 95 | + JT_B_j = -screw_transform_matrix_from_points(r_F_j, r_B_j) |
| 96 | + |
| 97 | + else: |
| 98 | + # Compute the wrench matrices |
| 99 | + # TODO: Since the lever-arm is a relative position, can we just use B_r_Bj and F_r_Fj instead? |
| 100 | + W_j_B = screw_transform_matrix_from_points(r_j, r_B_j) |
| 101 | + W_j_F = screw_transform_matrix_from_points(r_j, r_F_j) |
| 102 | + |
| 103 | + # General case: Compute the effective projector to joint frame and expand to 6D |
| 104 | + if dof_type != JointDoFType.UNIVERSAL: |
| 105 | + R_X_bar_j = expand6d(R_X_j) |
| 106 | + # Universal joint: replace R_X_j with the frame of the intermediate body for rotation constraints |
| 107 | + else: |
| 108 | + j_q_j = compute_joint_relative_quaternion( |
| 109 | + T_B_j, T_F_j, model_joints_X_Bj[joint_id], model_joints_X_Fj[joint_id] |
| 110 | + ) |
| 111 | + R_intermediate = compute_intermediate_body_frame_universal_joint(j_q_j) |
| 112 | + R_X_bar_j = concat6d(R_X_j, R_X_j @ R_intermediate) |
| 113 | + |
| 114 | + # Compute the extended jacobians, i.e. without the selection-matrix multiplication |
| 115 | + JT_B_j = -W_j_B @ R_X_bar_j # Reaction is on the Base body body ; (6 x 6) |
| 116 | + JT_F_j = W_j_F @ R_X_bar_j # Action is on the Follower body ; (6 x 6) |
| 117 | + |
| 118 | + return JT_B_j, JT_F_j |
| 119 | + |
| 120 | + |
62 | 121 | def make_store_joint_jacobian_dense_func(axes: Any): |
63 | 122 | """ |
64 | 123 | Generates a warp function to store body-pair Jacobian blocks into a target flat |
@@ -472,36 +531,17 @@ def _build_joint_jacobians_dense( |
472 | 531 | J_jdc_row_start = J_cjmio + nbd * (jdcgo + dyn_cts_offset_world) |
473 | 532 | J_jkc_row_start = J_cjmio + nbd * (jkcgo + kin_cts_offset_world) |
474 | 533 |
|
475 | | - # Retrieve the pose transform of the joint |
476 | | - T_j = state_joints_p[jid] |
477 | | - r_j = wp.transform_get_translation(T_j) |
478 | | - R_X_j = wp.quat_to_matrix(wp.transform_get_rotation(T_j)) |
479 | | - |
480 | | - # Retrieve the pose transforms of each body |
481 | | - T_B_j = wp.transform_identity() |
482 | | - if bid_B > -1: |
483 | | - T_B_j = state_bodies_q[bid_B] |
484 | | - T_F_j = state_bodies_q[bid_F] |
485 | | - r_B_j = wp.transform_get_translation(T_B_j) |
486 | | - r_F_j = wp.transform_get_translation(T_F_j) |
487 | | - |
488 | | - # Compute the wrench matrices |
489 | | - # TODO: Since the lever-arm is a relative position, can we just use B_r_Bj and F_r_Fj instead? |
490 | | - W_j_B = screw_transform_matrix_from_points(r_j, r_B_j) |
491 | | - W_j_F = screw_transform_matrix_from_points(r_j, r_F_j) |
492 | | - |
493 | | - # General case: Compute the effective projector to joint frame and expand to 6D |
494 | | - if dof_type != JointDoFType.UNIVERSAL: |
495 | | - R_X_bar_j = expand6d(R_X_j) |
496 | | - # Universal joint: replace R_X_j with the frame of the intermediate body for rotation constraints |
497 | | - else: |
498 | | - j_q_j = compute_joint_relative_quaternion(T_B_j, T_F_j, model_joints_X_Bj[jid], model_joints_X_Fj[jid]) |
499 | | - R_intermediate = compute_intermediate_body_frame_universal_joint(j_q_j) |
500 | | - R_X_bar_j = concat6d(R_X_j, R_X_j @ R_intermediate) |
501 | | - |
502 | | - # Compute the extended jacobians, i.e. without the selection-matrix multiplication |
503 | | - JT_B_j = -W_j_B @ R_X_bar_j # Reaction is on the Base body body ; (6 x 6) |
504 | | - JT_F_j = W_j_F @ R_X_bar_j # Action is on the Follower body ; (6 x 6) |
| 534 | + # Compute the full jacobians, i.e. without the selection-matrix multiplication |
| 535 | + JT_B_j, JT_F_j = build_full_joint_jacobian( |
| 536 | + jid, |
| 537 | + dof_type, |
| 538 | + bid_B, |
| 539 | + bid_F, |
| 540 | + model_joints_X_Bj, |
| 541 | + model_joints_X_Fj, |
| 542 | + state_joints_p, |
| 543 | + state_bodies_q, |
| 544 | + ) |
505 | 545 |
|
506 | 546 | # Store joint dynamic constraint jacobians if applicable |
507 | 547 | # NOTE: We use the extraction method for DoFs since dynamic constraints are in DoF-space |
@@ -561,36 +601,17 @@ def _build_joint_jacobians_sparse( |
561 | 601 | bid_B = model_joints_bid_B[jid] |
562 | 602 | bid_F = model_joints_bid_F[jid] |
563 | 603 |
|
564 | | - # Retrieve the pose transform of the joint |
565 | | - T_j = state_joints_p[jid] |
566 | | - r_j = wp.transform_get_translation(T_j) |
567 | | - R_X_j = wp.quat_to_matrix(wp.transform_get_rotation(T_j)) |
568 | | - |
569 | | - # Retrieve the pose transforms of each body |
570 | | - T_B_j = wp.transform_identity() |
571 | | - if bid_B > -1: |
572 | | - T_B_j = state_bodies_q[bid_B] |
573 | | - T_F_j = state_bodies_q[bid_F] |
574 | | - r_B_j = wp.transform_get_translation(T_B_j) |
575 | | - r_F_j = wp.transform_get_translation(T_F_j) |
576 | | - |
577 | | - # Compute the wrench matrices |
578 | | - # TODO: Since the lever-arm is a relative position, can we just use B_r_Bj and F_r_Fj instead? |
579 | | - W_j_B = screw_transform_matrix_from_points(r_j, r_B_j) |
580 | | - W_j_F = screw_transform_matrix_from_points(r_j, r_F_j) |
581 | | - |
582 | | - # General case: Compute the effective projector to joint frame and expand to 6D |
583 | | - if dof_type != JointDoFType.UNIVERSAL: |
584 | | - R_X_bar_j = expand6d(R_X_j) |
585 | | - # Universal joint: replace R_X_j with the frame of the intermediate body for rotation constraints |
586 | | - else: |
587 | | - j_q_j = compute_joint_relative_quaternion(T_B_j, T_F_j, model_joints_X_Bj[jid], model_joints_X_Fj[jid]) |
588 | | - R_intermediate = compute_intermediate_body_frame_universal_joint(j_q_j) |
589 | | - R_X_bar_j = concat6d(R_X_j, R_X_j @ R_intermediate) |
590 | | - |
591 | | - # Compute the extended jacobians, i.e. without the selection-matrix multiplication |
592 | | - JT_B_j = -W_j_B @ R_X_bar_j # Reaction is on the Base body body ; (6 x 6) |
593 | | - JT_F_j = W_j_F @ R_X_bar_j # Action is on the Follower body ; (6 x 6) |
| 604 | + # Compute the full jacobians, i.e. without the selection-matrix multiplication |
| 605 | + JT_B_j, JT_F_j = build_full_joint_jacobian( |
| 606 | + jid, |
| 607 | + dof_type, |
| 608 | + bid_B, |
| 609 | + bid_F, |
| 610 | + model_joints_X_Bj, |
| 611 | + model_joints_X_Fj, |
| 612 | + state_joints_p, |
| 613 | + state_bodies_q, |
| 614 | + ) |
594 | 615 |
|
595 | 616 | # Store joint dynamic constraint jacobians if applicable |
596 | 617 | # NOTE: We use the extraction method for DoFs since dynamic constraints are in DoF-space |
|
0 commit comments