@@ -861,6 +861,128 @@ void mj_tendon(const mjModel* m, mjData* d) {
861
861
862
862
863
863
864
+ // compute time derivative of dense tendon Jacobian for one tendon
865
+ void mj_tendonDot (const mjModel * m , mjData * d , int id , mjtNum * Jdot ) {
866
+ int nv = m -> nv ;
867
+
868
+ // allocate stack arrays
869
+ mjtNum * jac1 , * jac2 , * jacdif , * tmp ;
870
+ mj_markStack (d );
871
+ jac1 = mjSTACKALLOC (d , 3 * nv , mjtNum );
872
+ jac2 = mjSTACKALLOC (d , 3 * nv , mjtNum );
873
+ jacdif = mjSTACKALLOC (d , 3 * nv , mjtNum );
874
+ tmp = mjSTACKALLOC (d , nv , mjtNum );
875
+
876
+ // return if tendon id is invalid
877
+ if (id < 0 || id >= m -> ntendon ) {
878
+ return ;
879
+ }
880
+
881
+ // clear output
882
+ mju_zero (Jdot , nv );
883
+
884
+ // fixed tendon has zero Jdot: return
885
+ int adr = m -> tendon_adr [id ];
886
+ if (m -> wrap_type [adr ] == mjWRAP_JOINT ) {
887
+ return ;
888
+ }
889
+
890
+ // process spatial tendon
891
+ mjtNum divisor = 1 ;
892
+ int wraptype , j = 0 ;
893
+ int num = m -> tendon_num [id ];
894
+ while (j < num - 1 ) {
895
+ // get 1st and 2nd object
896
+ int type0 = m -> wrap_type [adr + j + 0 ];
897
+ int type1 = m -> wrap_type [adr + j + 1 ];
898
+ int id0 = m -> wrap_objid [adr + j + 0 ];
899
+ int id1 = m -> wrap_objid [adr + j + 1 ];
900
+
901
+ // pulley
902
+ if (type0 == mjWRAP_PULLEY || type1 == mjWRAP_PULLEY ) {
903
+ // get divisor, insert obj=-2
904
+ if (type0 == mjWRAP_PULLEY ) {
905
+ divisor = m -> wrap_prm [adr + j ];
906
+ }
907
+
908
+ // move to next
909
+ j ++ ;
910
+ continue ;
911
+ }
912
+
913
+ // init sequence; assume it starts with site
914
+ mjtNum wpnt [6 ];
915
+ mju_copy3 (wpnt , d -> site_xpos + 3 * id0 );
916
+ mjtNum vel [6 ];
917
+ mj_objectVelocity (m , d , mjOBJ_SITE , id0 , vel , /*flg_local=*/ 0 );
918
+ mjtNum wvel [6 ] = {vel [3 ], vel [4 ], vel [5 ], 0 , 0 , 0 };
919
+ int wbody [2 ];
920
+ wbody [0 ] = m -> site_bodyid [id0 ];
921
+
922
+ // second object is geom: process site-geom-site
923
+ if (type1 == mjWRAP_SPHERE || type1 == mjWRAP_CYLINDER ) {
924
+ // TODO(tassa) support geom wrapping (requires derivatives of mju_wrap)
925
+ mjERROR ("geom wrapping not supported" );
926
+ } else {
927
+ wraptype = mjWRAP_NONE ;
928
+ }
929
+
930
+ // complete sequence
931
+ wbody [1 ] = m -> site_bodyid [id1 ];
932
+ mju_copy3 (wpnt + 3 , d -> site_xpos + 3 * id1 );
933
+ mj_objectVelocity (m , d , mjOBJ_SITE , id1 , vel , /*flg_local=*/ 0 );
934
+ mju_copy3 (wvel + 3 , vel + 3 );
935
+
936
+ // accumulate moments if consecutive points are in different bodies
937
+ if (wbody [0 ] != wbody [1 ]) {
938
+ // dpnt = 3D position difference, normalize
939
+ mjtNum dpnt [3 ];
940
+ mju_sub3 (dpnt , wpnt + 3 , wpnt );
941
+ mjtNum norm = mju_norm3 (dpnt );
942
+ mju_scl3 (dpnt , dpnt , 1 /norm );
943
+
944
+ // dvel = d / dt (dpnt)
945
+ mjtNum dvel [3 ];
946
+ mju_sub3 (dvel , wvel + 3 , wvel );
947
+ mjtNum dot = mju_dot3 (dpnt , dvel );
948
+ mju_addToScl3 (dvel , dpnt , - dot );
949
+ mju_scl3 (dvel , dvel , 1 /norm );
950
+
951
+ // TODO(tassa ) write sparse branch, requires mj_jacDotSparse
952
+ // if (mj_isSparse(m)) { ... }
953
+
954
+ // get endpoint JacobianDots, subtract
955
+ mj_jacDot (m , d , jac1 , 0 , wpnt , wbody [0 ]);
956
+ mj_jacDot (m , d , jac2 , 0 , wpnt + 3 , wbody [1 ]);
957
+ mju_sub (jacdif , jac2 , jac1 , 3 * nv );
958
+
959
+ // chain rule, first term: Jdot += d/dt(jac2 - jac1) * dpnt
960
+ mju_mulMatTVec (tmp , jacdif , dpnt , 3 , nv );
961
+
962
+ // add to existing
963
+ mju_addToScl (Jdot , tmp , 1 /divisor , nv );
964
+
965
+ // get endpoint Jacobians, subtract
966
+ mj_jac (m , d , jac1 , 0 , wpnt , wbody [0 ]);
967
+ mj_jac (m , d , jac2 , 0 , wpnt + 3 , wbody [1 ]);
968
+ mju_sub (jacdif , jac2 , jac1 , 3 * nv );
969
+
970
+ // chain rule, second term: Jdot += (jac2 - jac1) * d/dt(dpnt)
971
+ mju_mulMatTVec (tmp , jacdif , dvel , 3 , nv );
972
+
973
+ // add to existing
974
+ mju_addToScl (Jdot , tmp , 1 /divisor , nv );
975
+ }
976
+
977
+ // advance
978
+ j += (wraptype != mjWRAP_NONE ? 2 : 1 );
979
+ }
980
+
981
+ mj_freeStack (d );
982
+ }
983
+
984
+
985
+
864
986
// compute actuator/transmission lengths and moments
865
987
void mj_transmission (const mjModel * m , mjData * d ) {
866
988
int nv = m -> nv , nu = m -> nu ;
0 commit comments