@@ -798,14 +798,17 @@ def _add_equality(self, name, type, objs_name, data, sol_params):
798798 # ------------------------------------------------------------------------------------
799799
800800 @gs .assert_built
801- def get_jacobian (self , link ):
801+ def get_jacobian (self , link , local_point = None ):
802802 """
803- Get the Jacobian matrix for a target link.
803+ Get the spatial Jacobian for a point on a target link.
804804
805805 Parameters
806806 ----------
807807 link : RigidLink
808808 The target link.
809+ local_point : torch.Tensor or None, shape (3,)
810+ Coordinates of the point in the link’s *local* frame.
811+ If None, the link origin is used (back-compat).
809812
810813 Returns
811814 -------
@@ -820,31 +823,48 @@ def get_jacobian(self, link):
820823 if self .n_dofs == 0 :
821824 gs .raise_exception ("Entity has zero dofs." )
822825
823- self ._kernel_get_jacobian (link .idx )
826+ if local_point is None :
827+ self ._kernel_get_jacobian_zero (link .idx )
828+ else :
829+ p_local = torch .as_tensor (local_point , dtype = gs .tc_float , device = gs .device )
830+ if p_local .shape != (3 ,):
831+ gs .raise_exception ("Must be a vector of length 3" )
832+ self ._kernel_get_jacobian (link .idx , p_local )
824833
825834 jacobian = self ._jacobian .to_torch (gs .device ).permute (2 , 0 , 1 )
826835 if self ._solver .n_envs == 0 :
827836 jacobian = jacobian .squeeze (0 )
828837
829838 return jacobian
830839
840+ @ti .func
841+ def _impl_get_jacobian (self , tgt_link_idx , i_b , p_vec ):
842+ self ._func_get_jacobian (
843+ tgt_link_idx ,
844+ i_b ,
845+ p_vec ,
846+ ti .Vector .one (gs .ti_int , 3 ),
847+ ti .Vector .one (gs .ti_int , 3 ),
848+ )
849+
831850 @ti .kernel
832- def _kernel_get_jacobian (self , tgt_link_idx : ti .i32 ):
833- ti .loop_config ( serialize = self . _solver . _para_level < gs .PARA_LEVEL . ALL )
851+ def _kernel_get_jacobian (self , tgt_link_idx : ti .i32 , p_local : ti . types . ndarray () ):
852+ p_vec = ti .Vector ([ p_local [ 0 ], p_local [ 1 ], p_local [ 2 ]], dt = gs .ti_float )
834853 for i_b in range (self ._solver ._B ):
835- self ._func_get_jacobian (
836- tgt_link_idx ,
837- i_b ,
838- ti .Vector . one ( gs . ti_int , 3 ),
839- ti . Vector . one ( gs . ti_int , 3 ),
840- )
854+ self ._impl_get_jacobian ( tgt_link_idx , i_b , p_vec )
855+
856+ @ ti . kernel
857+ def _kernel_get_jacobian_zero ( self , tgt_link_idx : ti .i32 ):
858+ for i_b in range ( self . _solver . _B ):
859+ self . _impl_get_jacobian ( tgt_link_idx , i_b , ti . Vector . zero ( gs . ti_float , 3 ) )
841860
842861 @ti .func
843- def _func_get_jacobian (self , tgt_link_idx , i_b , pos_mask , rot_mask ):
862+ def _func_get_jacobian (self , tgt_link_idx , i_b , p_local , pos_mask , rot_mask ):
844863 for i_row , i_d in ti .ndrange (6 , self .n_dofs ):
845864 self ._jacobian [i_row , i_d , i_b ] = 0.0
846865
847- tgt_link_pos = self ._solver .links_state [tgt_link_idx , i_b ].pos
866+ tgt_link_state = self ._solver .links_state [tgt_link_idx , i_b ]
867+ tgt_link_pos = tgt_link_state .pos + gu .ti_transform_by_quat (p_local , tgt_link_state .quat )
848868 i_l = tgt_link_idx
849869 while i_l > - 1 :
850870 I_l = [i_l , i_b ] if ti .static (self .solver ._options .batch_links_info ) else i_l
@@ -1320,7 +1340,7 @@ def _kernel_inverse_kinematics(
13201340 # update jacobian for ee link
13211341 i_l_ee = links_idx [i_ee ]
13221342 self ._func_get_jacobian (
1323- i_l_ee , i_b , pos_mask , rot_mask
1343+ i_l_ee , i_b , ti . Vector . zero ( gs . ti_float , 3 ), pos_mask , rot_mask
13241344 ) # NOTE: we still compute jacobian for all dofs as we haven't found a clean way to implement this
13251345
13261346 # copy to multi-link jacobian (only for the effective n_dofs instead of self.n_dofs)
0 commit comments