Remove midpoint integration, superseded by free-body gyroscopic derivatives.
The gyroscopic (bias) derivatives applied to standalone free bodies by the implicitfast integrator provide comparable stability for spinning bodies, with none of midpoint's restrictions: they apply under contacts, fluid forces and constraints, and preserve the linear force-velocity relation required by discrete-time inverse dynamics. The invdiscrete flag reverts to its original single meaning and no longer affects forward dynamics. Restore implicitfast coverage in the DiscreteInverseMatch test, removed when midpoint made discrete inverse dynamics untestable. Add implicit gyroscopic (bias) derivatives for free bodies in implicitfast. The implicitfast integrator drops the RNE (bias) derivative to stay on the symmetric Cholesky path, so fast-spinning free bodies integrate gyroscopic forces explicitly and can gain energy. Symmetrizing the gyroscopic Jacobian is not an option: its stabilizing content is the antisymmetric part, and adding only the symmetric part is destabilizing. Instead, exploit the fact that for a standalone free body the 6x6 block of M - h*D is decoupled from the rest of the system (qDeriv sparsity is tree-local): after the global solve, rebuild the block with the exact bias derivative in closed form (mjd_freeBias_vel) and re-solve it with dense unsymmetric LU, overwriting the block's rows of qacc. For lone spinning bodies this makes implicitfast match implicit to rounding, at ~150ns per eligible body: cheaper than the midpoint machinery it will replace. Eligibility is structural only; contacts, fluid and constraints need no gating. The same block is mirrored in discrete inverse dynamics (mj_discreteAcc), making invdiscrete exact for spinning free bodies. PiperOrigin-RevId: 948472495 Change-Id: I813ef3d98c7b399881bc8603b9f9208cfb02eb58
This commit is contained in:
committed by
Copybara-Service
parent
b2106db52f
commit
f0fa3d8260
@@ -147,6 +147,84 @@ TEST_F(DerivativeTest, SmoothDvel) {
|
||||
}
|
||||
}
|
||||
|
||||
// mjd_freeBias_vel: 6x6 bias-derivative block for a standalone free body
|
||||
// validated against mjd_rne_vel and against finite-differenced mj_rne
|
||||
TEST_F(DerivativeTest, FreeBiasVel) {
|
||||
// free body with offset CoM, rotated inertia, non-identity orientation
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<body pos="0.1 -0.2 0.3" euler="20 -30 40">
|
||||
<freejoint/>
|
||||
<geom type="box" size=".1 .2 .3" mass="2" pos=".04 -.02 .03" euler="10 20 30"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
|
||||
char error[1024];
|
||||
MjModelPtr model = LoadModelFromString(xml, error, sizeof(error));
|
||||
ASSERT_THAT(model.get(), NotNull()) << error;
|
||||
MjDataPtr data = MakeData(model);
|
||||
mjModel* m = model.get();
|
||||
mjData* d = data.get();
|
||||
|
||||
// set fast, fully populated velocity
|
||||
mjtNum qvel[6] = {0.4, -0.3, 0.2, 5, -3, 2};
|
||||
mju_copy(d->qvel, qvel, 6);
|
||||
mj_forward(m, d);
|
||||
|
||||
// analytic block
|
||||
mjtNum B[36];
|
||||
mjd_freeBias_vel(m, d, /*jnt=*/0, B);
|
||||
|
||||
// linear columns are zero by construction
|
||||
for (int r = 0; r < 6; r++) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
EXPECT_EQ(B[6 * r + c], 0);
|
||||
}
|
||||
}
|
||||
|
||||
// compare with mjd_rne_vel: B == -(qDeriv(flg_bias=1) - qDeriv(flg_bias=0))
|
||||
mju_zero(d->qDeriv, m->nD);
|
||||
mjd_smooth_vel(m, d, /*flg_bias=*/1);
|
||||
vector<mjtNum> qDeriv_bias = AsVector(d->qDeriv, m->nD);
|
||||
mju_zero(d->qDeriv, m->nD);
|
||||
mjd_smooth_vel(m, d, /*flg_bias=*/0);
|
||||
for (int r = 0; r < 6; r++) {
|
||||
int rowadr = m->D_rowadr[r];
|
||||
ASSERT_EQ(m->D_rownnz[r], 6);
|
||||
for (int k = 0; k < 6; k++) {
|
||||
int c = m->D_colind[rowadr + k];
|
||||
mjtNum rne_val = -(qDeriv_bias[rowadr + k] - d->qDeriv[rowadr + k]);
|
||||
EXPECT_NEAR(B[6 * r + c], rne_val, MjTol(1e-14, 1e-6))
|
||||
<< "mismatch at (" << r << ", " << c << ")";
|
||||
}
|
||||
}
|
||||
|
||||
// compare with central finite differences of mj_rne
|
||||
mjtNum eps = MjTol(1e-6, 1e-3);
|
||||
for (int c = 0; c < 6; c++) {
|
||||
mjtNum bias_plus[6], bias_minus[6];
|
||||
|
||||
d->qvel[c] = qvel[c] + eps;
|
||||
mj_comVel(m, d);
|
||||
mj_rne(m, d, /*flg_acc=*/0, bias_plus);
|
||||
|
||||
d->qvel[c] = qvel[c] - eps;
|
||||
mj_comVel(m, d);
|
||||
mj_rne(m, d, /*flg_acc=*/0, bias_minus);
|
||||
|
||||
d->qvel[c] = qvel[c];
|
||||
|
||||
for (int r = 0; r < 6; r++) {
|
||||
mjtNum fd = (bias_plus[r] - bias_minus[r]) / (2 * eps);
|
||||
EXPECT_NEAR(B[6 * r + c], fd, MjTol(1e-7, 1e-2))
|
||||
<< "FD mismatch at (" << r << ", " << c << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disabled actuators do not contribute to d_qfrc_actuator/d_qvel
|
||||
TEST_F(DerivativeTest, DisabledActuators) {
|
||||
// model with only a position actuator
|
||||
|
||||
Reference in New Issue
Block a user