Migrate mjd_inverseFD mass Jacobian from qM to M
PiperOrigin-RevId: 942268237 Change-Id: I0ecfe161867ce9930cd6366d778077df2cd3197f
This commit is contained in:
committed by
Copybara-Service
parent
4b345457a8
commit
7e9ac58ff9
@@ -3198,7 +3198,7 @@ using finite-differencing. These matrices and their dimensions are:
|
|||||||
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
|
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
|
||||||
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
|
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
|
||||||
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
|
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
|
||||||
``DmDq``, :math:`\partial M / \partial q`, ``nv x nM``
|
``DmDq``, :math:`\partial M / \partial q`, ``nv x nC``
|
||||||
|
|
||||||
- All outputs are optional (can be NULL).
|
- All outputs are optional (can be NULL).
|
||||||
- All outputs are transposed relative to Control Theory convention (i.e., column major).
|
- All outputs are transposed relative to Control Theory convention (i.e., column major).
|
||||||
|
|||||||
@@ -874,7 +874,7 @@ using finite-differencing. These matrices and their dimensions are:
|
|||||||
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
|
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
|
||||||
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
|
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
|
||||||
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
|
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
|
||||||
``DmDq``, :math:`\partial M / \partial q`, ``nv x nM``
|
``DmDq``, :math:`\partial M / \partial q`, ``nv x nC``
|
||||||
|
|
||||||
- All outputs are optional (can be NULL).
|
- All outputs are optional (can be NULL).
|
||||||
- All outputs are transposed relative to Control Theory convention (i.e., column major).
|
- All outputs are transposed relative to Control Theory convention (i.e., column major).
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ General
|
|||||||
:class: attention
|
:class: attention
|
||||||
|
|
||||||
- Return type of :ref:`mj_encode` and the :ref:`mjfEncode` callback changed from ``int`` to ``mjtSize`` (64-bit).
|
- Return type of :ref:`mj_encode` and the :ref:`mjfEncode` callback changed from ``int`` to ``mjtSize`` (64-bit).
|
||||||
|
- Switched :ref:`mjd_inverseFD` to use the CSR-format ``mjData.M`` representation instead of the legacy ``mjData.qM``
|
||||||
|
for the mass matrix derivative. This changes the shape of the ``DmDq`` parameter from ``(nv x nM)`` to
|
||||||
|
``(nv x nC)``.
|
||||||
|
|
||||||
Version 3.10.0 (June 22, 2026)
|
Version 3.10.0 (June 22, 2026)
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|||||||
@@ -1483,10 +1483,10 @@ MJAPI void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg
|
|||||||
// DsDq: (nv x nsensordata)
|
// DsDq: (nv x nsensordata)
|
||||||
// DsDv: (nv x nsensordata)
|
// DsDv: (nv x nsensordata)
|
||||||
// DsDa: (nv x nsensordata)
|
// DsDa: (nv x nsensordata)
|
||||||
// DmDq: (nv x nM)
|
// DmDq: (nv x nC)
|
||||||
// single-letter shortcuts:
|
// single-letter shortcuts:
|
||||||
// inputs: q=qpos, v=qvel, a=qacc
|
// inputs: q=qpos, v=qvel, a=qacc
|
||||||
// outputs: f=qfrc_inverse, s=sensordata, m=qM
|
// outputs: f=qfrc_inverse, s=sensordata, m=M
|
||||||
// notes:
|
// notes:
|
||||||
// optionally computes mass matrix Jacobian DmDq
|
// optionally computes mass matrix Jacobian DmDq
|
||||||
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse
|
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse
|
||||||
|
|||||||
@@ -1508,7 +1508,7 @@ Euler integrator, semi-implicit in velocity.
|
|||||||
ds_dq = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
ds_dq = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
||||||
ds_dv = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
ds_dv = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
||||||
ds_da = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
ds_da = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
|
||||||
dm_dq = np.zeros((self.model.nv, self.model.nM), dtype=DTYPE)
|
dm_dq = np.zeros((self.model.nv, self.model.nC), dtype=DTYPE)
|
||||||
mujoco.mjd_inverseFD(
|
mujoco.mjd_inverseFD(
|
||||||
self.model,
|
self.model,
|
||||||
self.data,
|
self.data,
|
||||||
|
|||||||
@@ -1640,8 +1640,8 @@ PYBIND11_MODULE(_functions, pymodule, pybind11::mod_gil_not_used()) {
|
|||||||
throw py::type_error("DsDa should be of shape (nv, nsensordata)");
|
throw py::type_error("DsDa should be of shape (nv, nsensordata)");
|
||||||
}
|
}
|
||||||
if (DmDq.has_value() &&
|
if (DmDq.has_value() &&
|
||||||
(DmDq->rows() != m->nv || DmDq->cols() != m->nM)) {
|
(DmDq->rows() != m->nv || DmDq->cols() != m->nC)) {
|
||||||
throw py::type_error("DmDq should be of shape (nv, nM)");
|
throw py::type_error("DmDq should be of shape (nv, nC)");
|
||||||
}
|
}
|
||||||
return InterceptMjErrors(::mjd_inverseFD)(
|
return InterceptMjErrors(::mjd_inverseFD)(
|
||||||
m, d, eps, flg_actuation,
|
m, d, eps, flg_actuation,
|
||||||
|
|||||||
@@ -9533,7 +9533,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
|||||||
nullable=True,
|
nullable=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
doc='Finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) All outputs are optional. Output dimensions (transposed w.r.t Control Theory convention): DfDq: (nv x nv) DfDv: (nv x nv) DfDa: (nv x nv) DsDq: (nv x nsensordata) DsDv: (nv x nsensordata) DsDa: (nv x nsensordata) DmDq: (nv x nM) single-letter shortcuts: inputs: q=qpos, v=qvel, a=qacc outputs: f=qfrc_inverse, s=sensordata, m=qM notes: optionally computes mass matrix Jacobian DmDq flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse', # pylint: disable=line-too-long
|
doc='Finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) All outputs are optional. Output dimensions (transposed w.r.t Control Theory convention): DfDq: (nv x nv) DfDv: (nv x nv) DfDa: (nv x nv) DsDq: (nv x nsensordata) DsDv: (nv x nsensordata) DsDa: (nv x nsensordata) DmDq: (nv x nC) single-letter shortcuts: inputs: q=qpos, v=qvel, a=qacc outputs: f=qfrc_inverse, s=sensordata, m=M notes: optionally computes mass matrix Jacobian DmDq flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse', # pylint: disable=line-too-long
|
||||||
)),
|
)),
|
||||||
('mjd_subQuat',
|
('mjd_subQuat',
|
||||||
FunctionDecl(
|
FunctionDecl(
|
||||||
|
|||||||
@@ -598,10 +598,10 @@ void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_cente
|
|||||||
// DsDq: (nv x nsensordata)
|
// DsDq: (nv x nsensordata)
|
||||||
// DsDv: (nv x nsensordata)
|
// DsDv: (nv x nsensordata)
|
||||||
// DsDa: (nv x nsensordata)
|
// DsDa: (nv x nsensordata)
|
||||||
// DmDq: (nv x nM)
|
// DmDq: (nv x nC)
|
||||||
// single-letter shortcuts:
|
// single-letter shortcuts:
|
||||||
// inputs: q=qpos, v=qvel, a=qacc
|
// inputs: q=qpos, v=qvel, a=qacc
|
||||||
// outputs: f=qfrc_inverse, s=sensordata, m=qM
|
// outputs: f=qfrc_inverse, s=sensordata, m=M
|
||||||
// notes:
|
// notes:
|
||||||
// optionally compute mass matrix Jacobian DmDq
|
// optionally compute mass matrix Jacobian DmDq
|
||||||
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse
|
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse
|
||||||
@@ -609,7 +609,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
|
|||||||
mjtNum *DfDq, mjtNum *DfDv, mjtNum *DfDa,
|
mjtNum *DfDq, mjtNum *DfDv, mjtNum *DfDa,
|
||||||
mjtNum *DsDq, mjtNum *DsDv, mjtNum *DsDa,
|
mjtNum *DsDq, mjtNum *DsDv, mjtNum *DsDa,
|
||||||
mjtNum *DmDq) {
|
mjtNum *DmDq) {
|
||||||
int nq = m->nq, nv = m->nv, nM = m->nM, ns = m->nsensordata;
|
int nq = m->nq, nv = m->nv, nC = m->nC, ns = m->nsensordata;
|
||||||
|
|
||||||
if (m->opt.integrator == mjINT_RK4) {
|
if (m->opt.integrator == mjINT_RK4) {
|
||||||
mjERROR("RK4 integrator is not supported");
|
mjERROR("RK4 integrator is not supported");
|
||||||
@@ -628,7 +628,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
|
|||||||
mjtNum *force = mjSTACKALLOC(d, nv, mjtNum); // force
|
mjtNum *force = mjSTACKALLOC(d, nv, mjtNum); // force
|
||||||
mjtNum *force_plus = mjSTACKALLOC(d, nv, mjtNum); // nudged force
|
mjtNum *force_plus = mjSTACKALLOC(d, nv, mjtNum); // nudged force
|
||||||
mjtNum *sensor = skipsensor ? NULL : mjSTACKALLOC(d, ns, mjtNum); // sensor values
|
mjtNum *sensor = skipsensor ? NULL : mjSTACKALLOC(d, ns, mjtNum); // sensor values
|
||||||
mjtNum *mass = DmDq ? mjSTACKALLOC(d, nM, mjtNum) : NULL; // mass matrix
|
mjtNum *mass = DmDq ? mjSTACKALLOC(d, nC, mjtNum) : NULL; // mass matrix
|
||||||
|
|
||||||
// save current positions
|
// save current positions
|
||||||
mju_copy(pos, d->qpos, nq);
|
mju_copy(pos, d->qpos, nq);
|
||||||
@@ -636,7 +636,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
|
|||||||
// center point outputs
|
// center point outputs
|
||||||
inverseSkip(m, d, mjSTAGE_NONE, skipsensor, flg_actuation, force);
|
inverseSkip(m, d, mjSTAGE_NONE, skipsensor, flg_actuation, force);
|
||||||
if (sensor) mju_copy(sensor, d->sensordata, ns);
|
if (sensor) mju_copy(sensor, d->sensordata, ns);
|
||||||
if (mass) mju_copy(mass, d->qM, nM);
|
if (mass) mju_copy(mass, d->M, nC);
|
||||||
|
|
||||||
// acceleration: skip = mjSTAGE_VEL
|
// acceleration: skip = mjSTAGE_VEL
|
||||||
if (DfDa || DsDa) {
|
if (DfDa || DsDa) {
|
||||||
@@ -702,7 +702,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
|
|||||||
if (DsDq) diff(DsDq + i*ns, sensor, d->sensordata, eps, ns);
|
if (DsDq) diff(DsDq + i*ns, sensor, d->sensordata, eps, ns);
|
||||||
|
|
||||||
// row of inertia Jacobian
|
// row of inertia Jacobian
|
||||||
if (DmDq) diff(DmDq + i*nM, mass, d->qM, eps, nM);
|
if (DmDq) diff(DmDq + i*nC, mass, d->M, eps, nC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -785,7 +785,7 @@ TEST_F(DerivativeTest, LinearSystemInverse) {
|
|||||||
|
|
||||||
int nv = model->nv;
|
int nv = model->nv;
|
||||||
int ns = model->nsensordata;
|
int ns = model->nsensordata;
|
||||||
int nM = model->nM;
|
int nC = model->nC;
|
||||||
|
|
||||||
vector<mjtNum> DfDq(nv * nv);
|
vector<mjtNum> DfDq(nv * nv);
|
||||||
vector<mjtNum> DfDv(nv * nv);
|
vector<mjtNum> DfDv(nv * nv);
|
||||||
@@ -793,7 +793,7 @@ TEST_F(DerivativeTest, LinearSystemInverse) {
|
|||||||
vector<mjtNum> DsDq(nv * ns);
|
vector<mjtNum> DsDq(nv * ns);
|
||||||
vector<mjtNum> DsDv(nv * ns);
|
vector<mjtNum> DsDv(nv * ns);
|
||||||
vector<mjtNum> DsDa(nv * ns);
|
vector<mjtNum> DsDa(nv * ns);
|
||||||
vector<mjtNum> DmDq(nv * nM);
|
vector<mjtNum> DmDq(nv * nC);
|
||||||
|
|
||||||
// call mj_forward to get accelerations at initial state
|
// call mj_forward to get accelerations at initial state
|
||||||
mj_forward(model, data);
|
mj_forward(model, data);
|
||||||
@@ -847,7 +847,7 @@ TEST_F(DerivativeTest, LinearSystemInverse) {
|
|||||||
EXPECT_THAT(DsDa, Pointwise(DoubleNear(eps), DsDa_expect));
|
EXPECT_THAT(DsDa, Pointwise(DoubleNear(eps), DsDa_expect));
|
||||||
|
|
||||||
// expect that mass matrix derivatives are zero
|
// expect that mass matrix derivatives are zero
|
||||||
vector<mjtNum> DmDq_expect(nv * nM, 0);
|
vector<mjtNum> DmDq_expect(nv * nC, 0);
|
||||||
EXPECT_THAT(DmDq, Pointwise(DoubleNear(eps), DmDq_expect));
|
EXPECT_THAT(DmDq, Pointwise(DoubleNear(eps), DmDq_expect));
|
||||||
|
|
||||||
mj_deleteData(data);
|
mj_deleteData(data);
|
||||||
|
|||||||
@@ -2057,7 +2057,7 @@ void mjd_inverseFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtBool flg_
|
|||||||
CHECK_SIZE(DsDq, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDq, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DsDv, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDv, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DsDa, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDa, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DmDq, m.nv() * m.nM());
|
CHECK_SIZE(DmDq, m.nv() * m.nC());
|
||||||
mjd_inverseFD(m.get(), d.get(), eps, flg_actuation, DfDq_.data(), DfDv_.data(), DfDa_.data(), DsDq_.data(), DsDv_.data(), DsDa_.data(), DmDq_.data());
|
mjd_inverseFD(m.get(), d.get(), eps, flg_actuation, DfDq_.data(), DfDv_.data(), DfDa_.data(), DsDq_.data(), DsDv_.data(), DsDa_.data(), DmDq_.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ FUNCTION_BOUNDS_CHECKS: Dict[str, str] = {
|
|||||||
CHECK_SIZE(DsDq, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDq, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DsDv, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDv, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DsDa, m.nv() * m.nsensordata());
|
CHECK_SIZE(DsDa, m.nv() * m.nsensordata());
|
||||||
CHECK_SIZE(DmDq, m.nv() * m.nM());
|
CHECK_SIZE(DmDq, m.nv() * m.nC());
|
||||||
""".strip(),
|
""".strip(),
|
||||||
"mjd_subQuat": """
|
"mjd_subQuat": """
|
||||||
CHECK_SIZE(qa, 4);
|
CHECK_SIZE(qa, 4);
|
||||||
|
|||||||
@@ -1305,7 +1305,7 @@ describe('MuJoCo WASM Bindings', () => {
|
|||||||
|
|
||||||
const nv = model!.nv;
|
const nv = model!.nv;
|
||||||
const nsensordata = model!.nsensordata;
|
const nsensordata = model!.nsensordata;
|
||||||
const nM = model!.nM;
|
const nC = model!.nC;
|
||||||
|
|
||||||
const dfDq = new mujoco.DoubleBuffer(nv * nv);
|
const dfDq = new mujoco.DoubleBuffer(nv * nv);
|
||||||
const dfDv = new mujoco.DoubleBuffer(nv * nv);
|
const dfDv = new mujoco.DoubleBuffer(nv * nv);
|
||||||
@@ -1313,7 +1313,7 @@ describe('MuJoCo WASM Bindings', () => {
|
|||||||
const dsDq = new mujoco.DoubleBuffer(nv * nsensordata);
|
const dsDq = new mujoco.DoubleBuffer(nv * nsensordata);
|
||||||
const dsDv = new mujoco.DoubleBuffer(nv * nsensordata);
|
const dsDv = new mujoco.DoubleBuffer(nv * nsensordata);
|
||||||
const dsDa = new mujoco.DoubleBuffer(nv * nsensordata);
|
const dsDa = new mujoco.DoubleBuffer(nv * nsensordata);
|
||||||
const dmDq = new mujoco.DoubleBuffer(nv * nM);
|
const dmDq = new mujoco.DoubleBuffer(nv * nC);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mujoco.mjd_inverseFD(
|
mujoco.mjd_inverseFD(
|
||||||
|
|||||||
Reference in New Issue
Block a user