Implement midpoint integrator for free bodies.
PiperOrigin-RevId: 899043541 Change-Id: I0bb38f6ad94e189b45ab16777a04ad6fefc6adf7
This commit is contained in:
committed by
Copybara-Service
parent
d9b5d8babb
commit
0c337799bd
@@ -320,17 +320,19 @@ TEST_F(DerivativeTest, PassiveDvel) {
|
||||
mj_forward(model, data);
|
||||
|
||||
// get analytic derivatives
|
||||
mju_zero(data->qDeriv, model->nD);
|
||||
mjd_passive_vel(model, data);
|
||||
mju_copy(qDerivAnalytic, data->qDeriv, nD);
|
||||
|
||||
// clear qDeriv, get finite-difference derivatives
|
||||
mju_zero(data->qDeriv, nD);
|
||||
mju_zero(qDerivFD, nD);
|
||||
mjtNum eps = MjTol(1e-6, 1e-3);
|
||||
mjtNum eps = MjTol(1e-6, 1e-4);
|
||||
mjd_passive_velFD(model, data, eps);
|
||||
|
||||
// expect FD and analytic derivatives to be similar to tol precision
|
||||
EXPECT_THAT(AsVector(data->qDeriv, nD),
|
||||
Pointwise(MjNear(1e-4, 1e-3), AsVector(qDerivAnalytic, nD)));
|
||||
Pointwise(MjNear(1e-6, 1e-4), AsVector(qDerivAnalytic, nD)));
|
||||
}
|
||||
|
||||
mju_free(qDerivFD);
|
||||
@@ -1733,5 +1735,79 @@ TEST_F(DerivativeTest, FlexInterpDerivativesDeformed) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(DerivativeTest, MidpointFluidAccuracy) {
|
||||
const std::string xml_path =
|
||||
GetTestDataFilePath(kTumblingThinObjectEllipsoidPath);
|
||||
char error[1024];
|
||||
mjModel* m = mj_loadXML(xml_path.c_str(), nullptr, error, sizeof(error));
|
||||
ASSERT_THAT(m, NotNull()) << error;
|
||||
|
||||
mjtNum dt_small = 1e-4;
|
||||
mjtNum dt_large = m->opt.timestep; // 2e-3, the default
|
||||
mjtNum duration = 0.5;
|
||||
|
||||
mjData* d_ref = mj_makeData(m);
|
||||
mjData* d_midpoint = mj_makeData(m);
|
||||
mjData* d_nomidpoint = mj_makeData(m);
|
||||
|
||||
// give initial angular velocity for tumbling
|
||||
mj_resetData(m, d_ref);
|
||||
mj_resetData(m, d_midpoint);
|
||||
mj_resetData(m, d_nomidpoint);
|
||||
d_ref->qvel[3] = 5;
|
||||
d_ref->qvel[4] = 3;
|
||||
d_ref->qvel[5] = 1;
|
||||
d_midpoint->qvel[3] = 5;
|
||||
d_midpoint->qvel[4] = 3;
|
||||
d_midpoint->qvel[5] = 1;
|
||||
d_nomidpoint->qvel[3] = 5;
|
||||
d_nomidpoint->qvel[4] = 3;
|
||||
d_nomidpoint->qvel[5] = 1;
|
||||
|
||||
int nsteps_large = static_cast<int>(duration / dt_large);
|
||||
int substeps = static_cast<int>(dt_large / dt_small);
|
||||
|
||||
mjtNum error_midpoint = 0;
|
||||
mjtNum error_nomidpoint = 0;
|
||||
|
||||
for (int i = 0; i < nsteps_large; i++) {
|
||||
// reference: RK4 at small timestep
|
||||
m->opt.integrator = mjINT_RK4;
|
||||
m->opt.timestep = dt_small;
|
||||
m->opt.enableflags &= ~mjENBL_INVDISCRETE;
|
||||
for (int j = 0; j < substeps; j++) {
|
||||
mj_step(m, d_ref);
|
||||
}
|
||||
|
||||
// implicit with midpoint (default)
|
||||
m->opt.integrator = mjINT_IMPLICIT;
|
||||
m->opt.timestep = dt_large;
|
||||
m->opt.enableflags &= ~mjENBL_INVDISCRETE;
|
||||
mj_step(m, d_midpoint);
|
||||
|
||||
// implicit without midpoint
|
||||
m->opt.enableflags |= mjENBL_INVDISCRETE;
|
||||
mj_step(m, d_nomidpoint);
|
||||
|
||||
// accumulate position errors
|
||||
for (int k = 0; k < 7; k++) {
|
||||
mjtNum diff_mid = d_ref->qpos[k] - d_midpoint->qpos[k];
|
||||
mjtNum diff_nomid = d_ref->qpos[k] - d_nomidpoint->qpos[k];
|
||||
error_midpoint += diff_mid * diff_mid;
|
||||
error_nomidpoint += diff_nomid * diff_nomid;
|
||||
}
|
||||
}
|
||||
|
||||
// expect midpoint to be more accurate
|
||||
EXPECT_LT(error_midpoint, error_nomidpoint)
|
||||
<< "implicit midpoint should be more accurate than implicit without "
|
||||
<< "midpoint for a free body with fluid forces";
|
||||
|
||||
mj_deleteData(d_nomidpoint);
|
||||
mj_deleteData(d_midpoint);
|
||||
mj_deleteData(d_ref);
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
Reference in New Issue
Block a user