Add actuator and sensor delays. Fixes #1004

PiperOrigin-RevId: 866478839
Change-Id: Id21a6da0f98454c8fa39ea5af8a5e213d6eae497
This commit is contained in:
Yuval Tassa
2026-02-06 08:46:45 -08:00
committed by Copybara-Service
parent 84fa527723
commit 6419534bad
48 changed files with 6282 additions and 219 deletions
+90
View File
@@ -3267,5 +3267,95 @@ TEST_F(XMLReaderTest, CameraOutputDefault) {
mj_deleteModel(model);
}
// ------------- test delay attribute parsing ----------------------------------
TEST_F(ActuatorParseTest, ActuatorDelayParsed) {
static constexpr char xml[] = R"(
<mujoco>
<option timestep="1"/>
<worldbody>
<body>
<geom size="1"/>
<joint name="jnt1"/>
<joint name="jnt2"/>
<joint name="jnt3"/>
</body>
</worldbody>
<actuator>
<motor joint="jnt1"/>
<motor joint="jnt2" delay="3" nsample="3" interp="zoh"/>
<motor joint="jnt3" delay="10" nsample="10" interp="cubic"/>
</actuator>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << error.data();
ASSERT_EQ(model->nu, 3);
// actuator_history[2*i] = nsample, actuator_history[2*i+1] = interp
EXPECT_EQ(model->actuator_history[0], 0); // jnt1 nsample
EXPECT_EQ(model->actuator_history[1], 0); // jnt1 interp (no buffer, default 0)
EXPECT_EQ(model->actuator_history[2], 3); // jnt2 nsample
EXPECT_EQ(model->actuator_history[3], 0); // jnt2 interp (ZOH)
EXPECT_EQ(model->actuator_history[4], 10); // jnt3 nsample
EXPECT_EQ(model->actuator_history[5], 2); // jnt3 interp (cubic)
EXPECT_EQ(model->actuator_historyadr[0], -1);
EXPECT_EQ(model->actuator_historyadr[1], 0);
EXPECT_EQ(model->actuator_historyadr[2], 8); // 2+2*3 = 8
mj_deleteModel(model);
}
TEST_F(ActuatorParseTest, ActuatorDelayDefault) {
static constexpr char xml[] = R"(
<mujoco>
<option timestep="1"/>
<default>
<motor delay="5" nsample="5"/>
</default>
<worldbody>
<body>
<geom size="1"/>
<joint name="jnt1"/>
<joint name="jnt2"/>
</body>
</worldbody>
<actuator>
<motor joint="jnt1"/>
<motor joint="jnt2" delay="0" nsample="0"/>
</actuator>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << error.data();
ASSERT_EQ(model->nu, 2);
EXPECT_EQ(model->actuator_history[0], 5);
EXPECT_EQ(model->actuator_history[2], 0);
EXPECT_EQ(model->actuator_historyadr[0], 0);
EXPECT_EQ(model->actuator_historyadr[1], -1);
mj_deleteModel(model);
}
TEST_F(ActuatorParseTest, ActuatorDelayRequiresHistory) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<body>
<geom size="1"/>
<joint name="jnt"/>
</body>
</worldbody>
<actuator>
<motor joint="jnt" delay="1"/>
</actuator>
</mujoco>
)";
std::array<char, 1024> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, IsNull());
EXPECT_THAT(error.data(),
HasSubstr("setting delay > 0 without a history buffer"));
}
} // namespace
} // namespace mujoco