5fb47df197
PiperOrigin-RevId: 861763477 Change-Id: Ib613c56949264573a16306800658f868d2aa7665
303 lines
8.0 KiB
C
303 lines
8.0 KiB
C
// Copyright 2021 DeepMind Technologies Limited
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "engine/engine_inverse.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <mujoco/mjdata.h>
|
|
#include <mujoco/mjmacro.h>
|
|
#include <mujoco/mjmodel.h>
|
|
#include <mujoco/mjsan.h> // IWYU pragma: keep
|
|
#include "engine/engine_collision_driver.h"
|
|
#include "engine/engine_core_constraint.h"
|
|
#include "engine/engine_core_smooth.h"
|
|
#include "engine/engine_derivative.h"
|
|
#include "engine/engine_memory.h"
|
|
#include "engine/engine_macro.h"
|
|
#include "engine/engine_forward.h"
|
|
#include "engine/engine_sensor.h"
|
|
#include "engine/engine_support.h"
|
|
#include "engine/engine_util_blas.h"
|
|
#include "engine/engine_util_errmem.h"
|
|
#include "engine/engine_util_misc.h"
|
|
#include "engine/engine_util_sparse.h"
|
|
|
|
// position-dependent computations
|
|
void mj_invPosition(const mjModel* m, mjData* d) {
|
|
TM_START1;
|
|
TM_START;
|
|
|
|
// clear flag for lazy evaluation
|
|
d->flg_energypos = 0;
|
|
|
|
mj_kinematics(m, d);
|
|
mj_comPos(m, d);
|
|
mj_camlight(m, d);
|
|
mj_flex(m, d);
|
|
mj_tendon(m, d);
|
|
TM_END(mjTIMER_POS_KINEMATICS);
|
|
|
|
mj_makeM(m, d); // timed internally (POS_INERTIA)
|
|
mj_factorM(m, d); // timed internally (POS_INERTIA)
|
|
|
|
mj_collision(m, d); // timed internally (POS_COLLISION)
|
|
|
|
TM_RESTART;
|
|
mj_makeConstraint(m, d);
|
|
TM_END(mjTIMER_POS_MAKE);
|
|
|
|
TM_RESTART;
|
|
mj_transmission(m, d);
|
|
TM_ADD(mjTIMER_POS_KINEMATICS);
|
|
|
|
TM_END1(mjTIMER_POSITION);
|
|
}
|
|
|
|
|
|
// velocity-dependent computations
|
|
void mj_invVelocity(const mjModel* m, mjData* d) {
|
|
mj_fwdVelocity(m, d);
|
|
}
|
|
|
|
|
|
// convert discrete-time qacc to continuous-time qacc
|
|
static void mj_discreteAcc(const mjModel* m, mjData* d) {
|
|
int nv = m->nv, nC = m->nC, nD = m->nD, dof_damping;
|
|
mjtNum *qacc = d->qacc;
|
|
|
|
mj_markStack(d);
|
|
mjtNum* qfrc = mjSTACKALLOC(d, nv, mjtNum);
|
|
|
|
// use selected integrator
|
|
switch ((mjtIntegrator) m->opt.integrator) {
|
|
case mjINT_RK4:
|
|
// not supported by RK4
|
|
mjERROR("discrete inverse dynamics is not supported by RK4 integrator");
|
|
return;
|
|
|
|
case mjINT_EULER:
|
|
// check for dof damping if disable flag is not set
|
|
dof_damping = 0;
|
|
if (!mjDISABLED(mjDSBL_EULERDAMP)) {
|
|
for (int i=0; i < nv; i++) {
|
|
if (m->dof_damping[i] > 0) {
|
|
dof_damping = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if disabled or no dof damping, nothing to do
|
|
if (!dof_damping) {
|
|
mj_freeStack(d);
|
|
return;
|
|
}
|
|
|
|
// set qfrc = (M + h*diag(B)) * qacc
|
|
mj_mulM(m, d, qfrc, qacc);
|
|
for (int i=0; i < nv; i++) {
|
|
qfrc[i] += m->opt.timestep * m->dof_damping[i] * d->qacc[i];
|
|
}
|
|
break;
|
|
|
|
case mjINT_IMPLICIT:
|
|
// compute qDeriv
|
|
mjd_smooth_vel(m, d, /* flg_bias = */ 1);
|
|
|
|
// gather qLU <- qM (lower to full)
|
|
mju_gatherMasked(d->qLU, d->M, m->mapM2D, nD);
|
|
|
|
// set qLU = qM - dt*qDeriv
|
|
mju_addToScl(d->qLU, d->qDeriv, -m->opt.timestep, m->nD);
|
|
|
|
// set qfrc = qLU * qacc
|
|
mju_mulMatVecSparse(qfrc, d->qLU, qacc, nv,
|
|
m->D_rownnz, m->D_rowadr, m->D_colind, /*rowsuper=*/NULL);
|
|
break;
|
|
|
|
case mjINT_IMPLICITFAST:
|
|
// compute analytical derivative qDeriv; skip rne derivative
|
|
mjd_smooth_vel(m, d, /* flg_bias = */ 0);
|
|
|
|
// save mass matrix
|
|
mjtNum* Msave = mjSTACKALLOC(d, m->nC, mjtNum);
|
|
mju_copy(Msave, d->M, m->nC);
|
|
|
|
// modified mass matrix: gather qH <- qDeriv (full to lower)
|
|
mju_gather(d->qH, d->qDeriv, m->mapD2M, nC);
|
|
|
|
// set qH = M - dt*qDeriv
|
|
mju_addScl(d->qH, d->M, d->qH, -m->opt.timestep, nC);
|
|
|
|
// set qfrc = (M - dt*qDeriv) * qacc
|
|
mju_mulSymVecSparse(qfrc, d->qH, qacc, m->nv, m->M_rownnz, m->M_rowadr, m->M_colind);
|
|
break;
|
|
}
|
|
|
|
// solve for qacc: qfrc = M * qacc
|
|
mj_solveM(m, d, qacc, qfrc, 1);
|
|
|
|
mj_freeStack(d);
|
|
}
|
|
|
|
|
|
// inverse constraint solver
|
|
void mj_invConstraint(const mjModel* m, mjData* d) {
|
|
TM_START;
|
|
int nefc = d->nefc;
|
|
|
|
// no constraints: clear, return
|
|
if (!nefc) {
|
|
mju_zero(d->qfrc_constraint, m->nv);
|
|
TM_END(mjTIMER_CONSTRAINT);
|
|
return;
|
|
}
|
|
|
|
mj_markStack(d);
|
|
mjtNum* jar = mjSTACKALLOC(d, nefc, mjtNum);
|
|
|
|
// compute jar = Jac*qacc - aref
|
|
mj_mulJacVec(m, d, jar, d->qacc);
|
|
mju_subFrom(jar, d->efc_aref, nefc);
|
|
|
|
// call update function
|
|
mj_constraintUpdate(m, d, jar, NULL, 0);
|
|
|
|
mj_freeStack(d);
|
|
TM_END(mjTIMER_CONSTRAINT);
|
|
}
|
|
|
|
|
|
// inverse dynamics with skip; skipstage is mjtStage
|
|
void mj_inverseSkip(const mjModel* m, mjData* d,
|
|
int skipstage, int skipsensor) {
|
|
TM_START;
|
|
mj_markStack(d);
|
|
mjtNum* qacc;
|
|
int nv = m->nv;
|
|
|
|
// position-dependent
|
|
if (skipstage < mjSTAGE_POS) {
|
|
mj_invPosition(m, d);
|
|
if (!skipsensor) {
|
|
mj_sensorPos(m, d);
|
|
}
|
|
if (mjENABLED(mjENBL_ENERGY) && !d->flg_energypos) {
|
|
mj_energyPos(m, d);
|
|
}
|
|
}
|
|
|
|
// velocity-dependent
|
|
if (skipstage < mjSTAGE_VEL) {
|
|
mj_invVelocity(m, d);
|
|
if (!skipsensor) {
|
|
mj_sensorVel(m, d);
|
|
}
|
|
if (mjENABLED(mjENBL_ENERGY) && !d->flg_energyvel) {
|
|
mj_energyVel(m, d);
|
|
}
|
|
}
|
|
|
|
if (mjENABLED(mjENBL_INVDISCRETE)) {
|
|
// save current qacc
|
|
qacc = mjSTACKALLOC(d, nv, mjtNum);
|
|
mju_copy(qacc, d->qacc, nv);
|
|
|
|
// modify qacc in-place
|
|
mj_discreteAcc(m, d);
|
|
}
|
|
|
|
// acceleration-dependent
|
|
mj_invConstraint(m, d);
|
|
|
|
// sum of bias forces in qfrc_inverse = centripetal + Coriolis + tendon bias
|
|
mj_rne(m, d, 0, d->qfrc_inverse);
|
|
mj_tendonBias(m, d, d->qfrc_inverse);
|
|
|
|
if (!skipsensor) {
|
|
d->flg_rnepost = 0; // clear flag for lazy evaluation
|
|
mj_sensorAcc(m, d);
|
|
}
|
|
|
|
// compute Ma = M*qacc
|
|
mjtNum* Ma = mjSTACKALLOC(d, nv, mjtNum);
|
|
mj_mulM(m, d, Ma, d->qacc);
|
|
|
|
// qfrc_inverse += Ma - qfrc_passive - qfrc_constraint
|
|
for (int i=0; i < nv; i++) {
|
|
d->qfrc_inverse[i] += Ma[i] - d->qfrc_passive[i] - d->qfrc_constraint[i];
|
|
}
|
|
|
|
if (mjENABLED(mjENBL_INVDISCRETE)) {
|
|
// restore qacc
|
|
mju_copy(d->qacc, qacc, nv);
|
|
}
|
|
|
|
mj_freeStack(d);
|
|
TM_END(mjTIMER_INVERSE);
|
|
}
|
|
|
|
|
|
// inverse dynamics
|
|
void mj_inverse(const mjModel* m, mjData* d) {
|
|
mj_inverseSkip(m, d, mjSTAGE_NONE, 0);
|
|
}
|
|
|
|
|
|
// compare forward and inverse dynamics, without changing results of forward
|
|
// fwdinv[0] = norm(qfrc_constraint(forward) - qfrc_constraint(inverse))
|
|
// fwdinv[1] = norm(qfrc_applied(forward) - qfrc_inverse)
|
|
void mj_compareFwdInv(const mjModel* m, mjData* d) {
|
|
int nv = m->nv, nefc = d->nefc;
|
|
mjtNum *qforce, *dif, *save_qfrc_constraint, *save_efc_force;
|
|
|
|
// clear result, return if no constraints
|
|
d->solver_fwdinv[0] = d->solver_fwdinv[1] = 0;
|
|
if (!nefc) {
|
|
return;
|
|
}
|
|
|
|
// allocate
|
|
mj_markStack(d);
|
|
qforce = mjSTACKALLOC(d, nv, mjtNum);
|
|
dif = mjSTACKALLOC(d, nv, mjtNum);
|
|
save_qfrc_constraint = mjSTACKALLOC(d, nv, mjtNum);
|
|
save_efc_force = mjSTACKALLOC(d, nefc, mjtNum);
|
|
|
|
// qforce = qfrc_applied + J'*xfrc_applied + qfrc_actuator
|
|
// should equal result of inverse dynamics
|
|
mju_add(qforce, d->qfrc_applied, d->qfrc_actuator, nv);
|
|
mj_xfrcAccumulate(m, d, qforce);
|
|
|
|
// save forward dynamics results that are about to be modified
|
|
mju_copy(save_qfrc_constraint, d->qfrc_constraint, nv);
|
|
mju_copy(save_efc_force, d->efc_force, nefc);
|
|
|
|
// run inverse dynamics, do not update position and velocity,
|
|
mj_inverseSkip(m, d, mjSTAGE_VEL, 1); // 1: do not recompute sensors and energy
|
|
|
|
// compute statistics
|
|
mju_sub(dif, save_qfrc_constraint, d->qfrc_constraint, nv);
|
|
d->solver_fwdinv[0] = mju_norm(dif, nv);
|
|
mju_sub(dif, qforce, d->qfrc_inverse, nv);
|
|
d->solver_fwdinv[1] = mju_norm(dif, nv);
|
|
|
|
// restore forward dynamics results
|
|
mju_copy(d->qfrc_constraint, save_qfrc_constraint, nv);
|
|
mju_copy(d->efc_force, save_efc_force, nefc);
|
|
|
|
mj_freeStack(d);
|
|
}
|