6#include <gtest/gtest.h>
19namespace fs = std::filesystem;
21static constexpr int kFillerBodies = 50;
23static mj_kdl::SceneObject fixed_box(
const std::string &name,
double half,
double x,
double y)
32 for (
int k = 0; k < 3; ++k) box.
size[k] = half;
33 for (
int k = 0; k < 3; ++k) box.
rgba[k] = 0.5f;
54 if (!fs::exists(mjcf) || !fs::exists(cube)) {
55 GTEST_SKIP() <<
"kinova_gen3/gen3.xml or cube.xml missing from assets/";
59 spec_.timestep = 0.002;
60 spec_.add_floor =
true;
61 spec_.add_skybox =
true;
64 spec_.robots.push_back(rs);
66 falling.
name =
"cube";
70 spec_.objects.push_back(falling);
71 spec_.objects.push_back(fixed_box(
"block", 0.02, -0.6, 0.0));
72 for (
int i = 0; i < kFillerBodies; ++i)
73 spec_.objects.push_back(fixed_box(
"filler_" + std::to_string(i), 0.01, -1.0, 0.05 * i));
83 const int jid = mj_name2id(
model_, mjOBJ_JOINT,
robot_.joint_names[joint].c_str());
84 for (
int a = 0; a <
model_->nu; ++a) {
85 if (
model_->actuator_trnid[2 * a] == jid &&
model_->actuator_group[a] == group)
95 ASSERT_NE(cube,
nullptr);
100 const double *q = data_->qpos + cube->
qpos_adr;
101 EXPECT_EQ(cube->
pose.
p.
x(), q[0]);
102 EXPECT_EQ(cube->
pose.
p.
y(), q[1]);
103 EXPECT_EQ(cube->
pose.
p.
z(), q[2]);
104 EXPECT_EQ(cube->
seq, 1u);
109 EXPECT_NEAR(derived.
p.
x(), cube->
pose.
p.
x(), 1e-12);
110 EXPECT_NEAR(derived.
p.
y(), cube->
pose.
p.
y(), 1e-12);
111 EXPECT_NEAR(derived.
p.
z(), cube->
pose.
p.
z(), 1e-12);
117 ASSERT_NE(slot,
nullptr);
122 data_->qpos[adr + 2] = 2.5;
124 EXPECT_NEAR(cube.
p.
z(), 2.5, 1e-12);
129 mjData *reference = mj_makeData(model_);
130 mj_copyData(reference, model_, data_);
131 for (
int i = 0; i < 200; ++i) {
133 mj_step(model_, reference);
135 EXPECT_EQ(std::memcmp(data_->qpos, reference->qpos,
sizeof(mjtNum) * model_->nq), 0);
136 EXPECT_EQ(std::memcmp(data_->qvel, reference->qvel,
sizeof(mjtNum) * model_->nv), 0);
137 mj_deleteData(reference);
143 ASSERT_NE(slot,
nullptr);
146 mjData *reference = mj_makeData(model_);
147 mj_copyData(reference, model_, data_);
149 data_->qpos[adr + 2] = reference->qpos[adr + 2] = 2.0;
151 mj_step(model_, reference);
152 EXPECT_EQ(data_->qpos[adr + 2], reference->qpos[adr + 2]);
156 EXPECT_NEAR(cube.
p.
z(), data_->qpos[adr + 2], 1e-12);
157 mj_deleteData(reference);
166 ASSERT_NE(push,
nullptr);
168 const int fz = 6 * push->
body_id + 2;
170 std::atomic<bool> stop{
false };
171 std::thread reader([&] {
177 for (
int i = 0; i < 1000; ++i) {
179 std::this_thread::sleep_for(std::chrono::milliseconds(1));
180 if (data_->xfrc_applied[fz] != 5.0) ++lost;
185 EXPECT_EQ(lost, 0) <<
"the render thread cleared a user wrench";
204 ASSERT_NE(slot,
nullptr);
207 for (
int i = 0; i < robot_.n_joints; ++i) q(i) = 0.0;
211 EXPECT_NEAR(slot->
position, -1.25, 1e-12);
223 ASSERT_NE(first,
nullptr);
225 for (
int i = 0; i < kFillerBodies; ++i) {
231 EXPECT_EQ(first, &env_.scene.wrenches.front());
232 EXPECT_EQ(first->
name,
"cube");
233 EXPECT_EQ(env_.scene.wrenches.size(),
static_cast<std::size_t
>(kFillerBodies) + 1);
239 ASSERT_NE(push,
nullptr);
243 for (
int i = 0; i < robot_.n_joints; ++i) robot_.jnt_trq_cmd[i] = 0.1 * (i + 1);
247 for (
int i = 0; i < robot_.n_joints; ++i) {
248 const int jid = mj_name2id(model_, mjOBJ_JOINT, robot_.joint_names[i].c_str());
249 EXPECT_EQ(robot_.jnt_pos_msr[i], data_->qpos[model_->jnt_qposadr[jid]]);
250 const int motor = actuator(i, 2);
252 EXPECT_DOUBLE_EQ(data_->ctrl[motor], 0.1 * (i + 1));
253 EXPECT_EQ(data_->qfrc_applied[model_->jnt_dofadr[jid]], 0.0);
255 EXPECT_EQ(data_->xfrc_applied[6 * push->
body_id + 2], 2.0);
261 ASSERT_NE(slot,
nullptr);
265 const double *applied = data_->xfrc_applied + 6 * slot->
body_id;
266 EXPECT_EQ(applied[0], 1.0);
267 EXPECT_EQ(applied[5], 0.6);
271 for (
int k = 0; k < 6; ++k) EXPECT_EQ(applied[k], 0.0) <<
"component " << k;
280 ASSERT_NE(joint,
nullptr);
281 ASSERT_NE(cube,
nullptr);
282 ASSERT_NE(push,
nullptr);
283 ASSERT_NE(drive,
nullptr);
287 for (
int i = 0; i < 20; ++i) {
292 ASSERT_GT(joint->
seq, 1u);
299 EXPECT_EQ(joint->
seq, 1u) <<
"re-read once from the reset state";
301 EXPECT_EQ(cube->
seq, 1u);
305 for (
int k = 0; k < 6; ++k) EXPECT_EQ(data_->xfrc_applied[6 * push->
body_id + k], 0.0);
308int main(
int argc,
char *argv[])
310 testing::InitGoogleTest(&argc, argv);
311 return RUN_ALL_TESTS();
int actuator(int joint, int group) const
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
void set_log_level(LogLevel level)
Status set_control_mode(Robot *r, CtrlMode mode)
Status init_robot_from_mjcf(Robot *r, Env *env, const char *base_body, const char *tip_body, const char *prefix="", const ToolFrameSpec *tool=nullptr)
void set_joint_pos(Robot *r, const KDL::JntArray &q)
bool get_body_frame(Env *env, const char *body_name, KDL::Frame *out)
SceneWrenchSlot * bind_scene_wrench(SceneState *s, const char *body_name)
SceneActuatorSlot * bind_scene_actuator(SceneState *s, const char *name)
SceneFreeBodySlot * bind_scene_free_body(SceneState *s, const char *body_name)
SceneJointSlot * bind_scene_joint(SceneState *s, const char *joint_name)
Status open_viewer(Env *env, const char *title="MuJoCo")
std::string find_asset(const fs::path &relative)
int main(int argc, char *argv[])
TEST_F(SceneStateTest, FreeBodyPoseAndDerivedFrameAgreeAfterAStep)