From de48f4178f971d945fc135afb42699b8ca6bec8e Mon Sep 17 00:00:00 2001
From: Yuval Tassa
Date: Tue, 11 Mar 2025 03:00:25 -0700
Subject: [PATCH] Add missing term in `mj_jacDot`
PiperOrigin-RevId: 735686836
Change-Id: I813ca46f71e368ddc97a6d2e540102137b31dbcf
---
doc/changelog.rst | 8 +++
src/engine/engine_core_smooth.c | 2 +-
src/engine/engine_support.c | 26 ++++++---
test/engine/engine_support_test.cc | 94 +++++++++++++++++++-----------
4 files changed, 88 insertions(+), 42 deletions(-)
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 65a668ef..c2361990 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -2,6 +2,14 @@
Changelog
=========
+Upcoming version (not yet released)
+-----------------------------------
+
+Bug fixes
+^^^^^^^^^
+- :ref:`mj_jacDot` was missing a term that accounts for the motion of the point with respect to
+ which the Jacobian is computed, now fixed.
+
Version 3.3.0 (Feb 26, 2025)
----------------------------
diff --git a/src/engine/engine_core_smooth.c b/src/engine/engine_core_smooth.c
index 2e8cf8bb..9b453c3e 100644
--- a/src/engine/engine_core_smooth.c
+++ b/src/engine/engine_core_smooth.c
@@ -1893,7 +1893,7 @@ void mj_comVel(const mjModel* m, mjData* d) {
// assign cvel, cdofdot
mju_copy(d->cvel+6*i, cvel, 6);
- mju_copy(d->cdof_dot+6*bda, cdofdot, 6*m->body_dofnum[i]);
+ mju_copy(d->cdof_dot+6*bda, cdofdot, 6*dofnum);
}
}
diff --git a/src/engine/engine_support.c b/src/engine/engine_support.c
index 60e05973..4f5d06af 100644
--- a/src/engine/engine_support.c
+++ b/src/engine/engine_support.c
@@ -811,11 +811,14 @@ void mj_jacDot(const mjModel* m, const mjData* d,
mjtNum* jacp, mjtNum* jacr, const mjtNum point[3], int body) {
int nv = m->nv;
mjtNum offset[3];
+ mjtNum pvel[6]; // point velocity (rot:lin order)
- // clear jacobians, compute offset if required
+ // clear jacobians, compute offset and pvel if required
if (jacp) {
mju_zero(jacp, 3*nv);
- mju_sub3(offset, point, d->subtree_com+3*m->body_rootid[body]);
+ const mjtNum* com = d->subtree_com+3*m->body_rootid[body];
+ mju_sub3(offset, point, com);
+ mju_transformSpatial(pvel, d->cvel+6*body, 0, point, com, 0);
}
if (jacr) {
mju_zero(jacr, 3*nv);
@@ -838,6 +841,7 @@ void mj_jacDot(const mjModel* m, const mjData* d,
while (i >= 0) {
mjtNum cdof_dot[6];
mju_copy(cdof_dot, d->cdof_dot+6*i, 6);
+ mjtNum* cdof = d->cdof+6*i;
// check for quaternion
mjtJoint type = m->jnt_type[m->dof_jntid[i]];
@@ -846,7 +850,7 @@ void mj_jacDot(const mjModel* m, const mjData* d,
// compute cdof_dot for quaternion (use current body cvel)
if (is_quat) {
- mju_crossMotion(cdof_dot, d->cvel+6*m->dof_bodyid[i], d->cdof+6*i);
+ mju_crossMotion(cdof_dot, d->cvel+6*m->dof_bodyid[i], cdof);
}
// construct rotation jacobian
@@ -858,11 +862,17 @@ void mj_jacDot(const mjModel* m, const mjData* d,
// construct translation jacobian (correct for rotation)
if (jacp) {
- mjtNum tmp[3] = {0};
- mju_cross(tmp, cdof_dot, offset);
- jacp[i+0*nv] += cdof_dot[3] + tmp[0];
- jacp[i+1*nv] += cdof_dot[4] + tmp[1];
- jacp[i+2*nv] += cdof_dot[5] + tmp[2];
+ // first correction term, account for varying cdof
+ mjtNum tmp1[3];
+ mju_cross(tmp1, cdof_dot, offset);
+
+ // second correction term, account for point translational velocity
+ mjtNum tmp2[3];
+ mju_cross(tmp2, cdof, pvel + 3);
+
+ jacp[i+0*nv] += cdof_dot[3] + tmp1[0] + tmp2[0];
+ jacp[i+1*nv] += cdof_dot[4] + tmp1[1] + tmp2[1];
+ jacp[i+2*nv] += cdof_dot[5] + tmp1[2] + tmp2[2];
}
// advance to parent dof
diff --git a/test/engine/engine_support_test.cc b/test/engine/engine_support_test.cc
index 20dbfa29..c7d4f018 100644
--- a/test/engine/engine_support_test.cc
+++ b/test/engine/engine_support_test.cc
@@ -288,6 +288,7 @@ static constexpr char kQuat[] = R"(
+
@@ -315,6 +316,7 @@ static constexpr char kFreeBall[] = R"(
+
+
+