Mujoco KDL Wrapper  0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
test_init.cpp
Go to the documentation of this file.
1/* test_init.cpp
2 * Env and Robot lifecycle on the Kinova GEN3: init, reset (hook, ports, options), cleanup, an
3 * adopted model pair, a chain adopted from outside, two Envs side by side, what a failure says,
4 * required spec fields, the floor height, offscreen rendering and the recorder's output path.
5 * Tests that need the bundled Gen3 self-skip without it. */
6
8#include "common.hpp"
9#include "example_paths.hpp"
10
11#include <gtest/gtest.h>
12
13#include <cstdint>
14#include <filesystem>
15#include <string>
16#include <vector>
17
18namespace ex = mj_kdl_examples;
19namespace fs = std::filesystem;
20
21static std::string gen3_path() { return ex::find_asset("kinova_gen3/gen3.xml"); }
22
23static mj_kdl::SceneSpec arm_scene(const std::string &mjcf, const std::string &prefix = "")
24{
26 sc.timestep = 0.002;
27 sc.add_floor = true;
28 sc.add_skybox = false;
30 r.path = mjcf;
31 r.prefix = prefix;
32 sc.robots.push_back(r);
33 return sc;
34}
35
36static mj_kdl::SceneObject small_cube()
37{
39 cube.name = "cube";
41 cube.mass = 0.1;
42 for (int k = 0; k < 3; ++k) cube.size[k] = 0.02;
43 for (int k = 0; k < 4; ++k) cube.rgba[k] = 1.0f;
44 for (int k = 0; k < 3; ++k) cube.friction[k] = 0.5;
45 cube.pos[0] = 0.5;
46 cube.pos[2] = 0.02;
47 return cube;
48}
49
50class InitTest : public testing::Test
51{
52 protected:
56
57 void SetUp() override
58 {
59 const std::string mjcf = gen3_path();
60 if (!fs::exists(mjcf)) GTEST_SKIP() << mjcf << " not found";
61 sc_ = arm_scene(mjcf);
62 ASSERT_TRUE(mj_kdl::init_env(&env_, &sc_));
63 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&s, &env_, "base_link", "bracelet_link"));
64 }
65
66 int joint(int i) const { return mj_name2id(env_.model, mjOBJ_JOINT, s.joint_names[i].c_str()); }
67 double qpos(int i) const { return env_.data->qpos[env_.model->jnt_qposadr[joint(i)]]; }
68 int dof(int i) const { return env_.model->jnt_dofadr[joint(i)]; }
69 int servo(int i) const
70 {
71 for (int a = 0; a < env_.model->nu; ++a) {
72 if (env_.model->actuator_trnid[2 * a] == joint(i) && env_.model->actuator_group[a] == 1)
73 return a;
74 }
75 return -1;
76 }
77};
78
79TEST_F(InitTest, BasicDOF)
80{
81 EXPECT_EQ(s.n_joints, 7) << "expected 7 KDL joints, got " << s.n_joints;
82 EXPECT_EQ(env_.robots.size(), 1u) << "init_robot_from_mjcf() registers the robot";
83}
84
85TEST_F(InitTest, SimulationAdvance)
86{
88 const double t0 = env_.data->time;
89 for (int k = 0; k < 100; ++k) mj_kdl::step(&env_);
90 EXPECT_NEAR(env_.data->time - t0, 100 * sc_.timestep, 1e-9);
91}
92
93TEST_F(InitTest, ResetRestoresTheKeyframePose)
94{
95 ASSERT_GT(env_.model->nkey, 0) << "the bundled Gen3 has a home keyframe";
96 KDL::JntArray q_displaced = ex::home_q(7);
97 for (unsigned i = 0; i < 7; ++i) q_displaced(i) += 0.3;
98 mj_kdl::set_joint_pos(&s, q_displaced);
99 for (int k = 0; k < 50; ++k) mj_kdl::step(&env_);
100
101 mj_kdl::reset(&env_);
102 for (int i = 0; i < s.n_joints; ++i) {
103 const int adr = env_.model->jnt_qposadr[joint(i)];
104 EXPECT_DOUBLE_EQ(qpos(i), env_.model->key_qpos[adr]) << "joint " << i;
105 }
106 EXPECT_EQ(env_.data->time, 0.0);
107}
108
109TEST_F(InitTest, ResetOptionsPickTheKeyframeOrTheModelDefault)
110{
111 ASSERT_GE(env_.model->nkey, 2) << "the bundled Gen3 has home and retract keyframes";
112 mj_kdl::ResetOptions options;
113 options.keyframe = 1;
114 mj_kdl::ResetInfo info = mj_kdl::reset(&env_, &options);
115 EXPECT_TRUE(info.used_keyframe);
116 EXPECT_EQ(info.keyframe, 1);
117 for (int i = 0; i < s.n_joints; ++i) {
118 const int adr = env_.model->jnt_qposadr[joint(i)];
119 EXPECT_DOUBLE_EQ(qpos(i), env_.model->key_qpos[env_.model->nq + adr]) << "joint " << i;
120 EXPECT_DOUBLE_EQ(s.jnt_pos_msr[i], qpos(i));
121 }
122
123 options.use_keyframe = false;
124 info = mj_kdl::reset(&env_, &options);
125 EXPECT_FALSE(info.used_keyframe);
126 EXPECT_EQ(info.keyframe, -1);
127 for (int i = 0; i < s.n_joints; ++i) {
128 const int adr = env_.model->jnt_qposadr[joint(i)];
129 EXPECT_DOUBLE_EQ(qpos(i), env_.model->qpos0[adr]) << "joint " << i;
130 }
131}
132
133TEST_F(InitTest, ResetSyncsCmdPorts)
134{
135 for (int i = 0; i < s.n_joints; ++i) {
136 s.jnt_pos_cmd[i] = 99.0;
137 s.jnt_trq_cmd[i] = 42.0;
138 }
139
140 mj_kdl::reset(&env_);
141
142 for (int i = 0; i < s.n_joints; ++i) {
143 EXPECT_DOUBLE_EQ(s.jnt_pos_cmd[i], qpos(i));
144 EXPECT_DOUBLE_EQ(s.jnt_trq_cmd[i], 0.0);
145 }
146}
147
148TEST_F(InitTest, ResetRestoresEveryPort)
149{
150 for (int i = 0; i < s.n_joints; ++i) {
151 s.jnt_pos_msr[i] = 7.0;
152 s.jnt_vel_msr[i] = 7.0;
153 s.jnt_vel_cmd[i] = 3.0;
154 s.jnt_trq_cmd[i] = 3.0;
155 s.jnt_saturated[i] = 1;
156 }
157
158 mj_kdl::reset(&env_);
159
160 for (int i = 0; i < s.n_joints; ++i) {
161 EXPECT_DOUBLE_EQ(s.jnt_pos_msr[i], qpos(i));
162 EXPECT_DOUBLE_EQ(s.jnt_vel_msr[i], env_.data->qvel[dof(i)]);
163 EXPECT_DOUBLE_EQ(s.jnt_pos_cmd[i], qpos(i));
164 EXPECT_EQ(s.jnt_vel_cmd[i], 0.0);
165 EXPECT_EQ(s.jnt_trq_cmd[i], 0.0);
166 EXPECT_EQ(s.jnt_saturated[i], 0);
167 }
168}
169
170TEST_F(InitTest, ResetInvokesOnResetCallback)
171{
172 int call_count = 0;
173 env_.on_reset = [&](mj_kdl::ResetContext *) { ++call_count; };
174 mj_kdl::reset(&env_);
175
176 EXPECT_EQ(call_count, 1) << "on_reset was not called by reset()";
177}
178
179TEST_F(InitTest, ResetWithoutAHookStillReseedsTheRobot)
180{
181 ASSERT_FALSE(env_.on_reset);
182 s.jnt_pos_cmd[0] = 99.0;
183 const mj_kdl::ResetInfo info = mj_kdl::reset(&env_);
184 EXPECT_TRUE(info.used_keyframe);
185 EXPECT_DOUBLE_EQ(s.jnt_pos_cmd[0], qpos(0));
186}
187
188TEST_F(InitTest, EnvResetInvokesHookAndSyncsRobot)
189{
190 int call_count = 0;
191 env_.on_reset = [&](mj_kdl::ResetContext *ctx) {
192 ++call_count;
193 EXPECT_EQ(ctx->env, &env_);
194 EXPECT_EQ(ctx->model, env_.model);
195 EXPECT_EQ(ctx->data, env_.data);
196 };
197
198 for (int i = 0; i < s.n_joints; ++i) {
199 s.jnt_pos_cmd[i] = 99.0;
200 s.jnt_trq_cmd[i] = 42.0;
201 env_.data->qfrc_applied[dof(i)] = 12.0;
202 }
203
204 mj_kdl::ResetInfo info = mj_kdl::reset(&env_);
205
206 EXPECT_EQ(call_count, 1);
207 EXPECT_TRUE(info.used_keyframe);
208 EXPECT_EQ(info.keyframe, 0);
209 for (int i = 0; i < s.n_joints; ++i) {
210 EXPECT_DOUBLE_EQ(s.jnt_pos_msr[i], qpos(i));
211 EXPECT_DOUBLE_EQ(s.jnt_pos_cmd[i], qpos(i));
212 EXPECT_DOUBLE_EQ(s.jnt_trq_cmd[i], 0.0);
213 EXPECT_DOUBLE_EQ(env_.data->qfrc_applied[dof(i)], 0.0);
214 }
215}
216
217TEST_F(InitTest, ResetKeepsARequestedControlMode)
218{
219 s.ctrl_mode = mj_kdl::CtrlMode::TORQUE;
220 mj_kdl::reset(&env_);
221 EXPECT_EQ(s.ctrl_mode, mj_kdl::CtrlMode::TORQUE);
222
223 mj_kdl::update(&env_);
224 EXPECT_TRUE(env_.model->opt.disableactuator & (1 << 1)) << "POSITION group off";
225 EXPECT_FALSE(env_.model->opt.disableactuator & (1 << 2)) << "TORQUE group on";
226}
227
228TEST_F(InitTest, OnResetPrimesCommandsAndMovesAreReadBack)
229{
230 env_.on_reset = [&](mj_kdl::ResetContext *ctx) {
231 s.jnt_trq_cmd[0] = 1.5;
232 ctx->data->qpos[ctx->model->jnt_qposadr[joint(1)]] += 0.2;
233 };
234 mj_kdl::reset(&env_);
235 EXPECT_DOUBLE_EQ(s.jnt_trq_cmd[0], 1.5) << "primed in the hook, not overwritten";
236 EXPECT_DOUBLE_EQ(s.jnt_pos_msr[1], qpos(1)) << "a move in the hook is read back";
237}
238
239TEST_F(InitTest, CleanupRobotUnregistersIt)
240{
241 const int a = servo(0);
242 ASSERT_GE(a, 0);
243 mj_kdl::cleanup(&s);
244 EXPECT_TRUE(env_.robots.empty());
245
246 env_.data->ctrl[a] = 0.123;
247 s.jnt_pos_cmd.assign(7, 1.0);
248 mj_kdl::update(&env_);
249 EXPECT_EQ(env_.data->ctrl[a], 0.123) << "update() no longer commands the robot";
250}
251
252TEST_F(InitTest, ChainFromOutsideDrivesTheSameJoints)
253{
254 mj_kdl::Robot given;
255 ASSERT_TRUE(mj_kdl::init_robot_from_chain(&given, &env_, s.chain, s.joint_names));
256 EXPECT_EQ(env_.robots.size(), 2u);
257 EXPECT_EQ(given.n_joints, s.n_joints);
258 EXPECT_EQ(given.chain.getNrOfSegments(), s.chain.getNrOfSegments());
259
261 q(1) += 0.1;
262 mj_kdl::set_joint_pos(&given, q);
263 mj_kdl::update(&env_);
264 for (int i = 0; i < 7; ++i) {
265 EXPECT_DOUBLE_EQ(given.jnt_pos_msr[i], q(i));
266 EXPECT_DOUBLE_EQ(s.jnt_pos_msr[i], q(i)) << "both robots read the same joints";
267 }
268
269 std::vector<std::string> short_list(s.joint_names.begin(), s.joint_names.end() - 1);
270 mj_kdl::Robot wrong;
271 EXPECT_FALSE(mj_kdl::init_robot_from_chain(&wrong, &env_, s.chain, short_list));
272 mj_kdl::cleanup(&given);
273}
274
275TEST(TwoEnvs, StepIndependently)
276{
277 const std::string mjcf = gen3_path();
278 if (!fs::exists(mjcf)) GTEST_SKIP() << mjcf << " not found";
279
280 mj_kdl::SceneSpec sc = arm_scene(mjcf);
281 sc.add_floor = false;
282
283 mj_kdl::Env a, b;
284 mj_kdl::Robot ra, rb;
285 ASSERT_TRUE(mj_kdl::init_env(&a, &sc));
286 ASSERT_TRUE(mj_kdl::init_env(&b, &sc));
287 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&ra, &a, "base_link", "bracelet_link"));
288 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&rb, &b, "base_link", "bracelet_link"));
289
291 mj_kdl::set_joint_pos(&ra, q);
292 q(1) += 0.5;
293 mj_kdl::set_joint_pos(&rb, q);
294
295 KDL::Frame fa, fb;
296 ASSERT_TRUE(mj_kdl::get_body_frame(&a, "bracelet_link", &fa));
297 ASSERT_TRUE(mj_kdl::get_body_frame(&b, "bracelet_link", &fb));
298 EXPECT_GT((fa.p - fb.p).Norm(), 0.05) << "each Env computes its own frames";
299
300 for (int k = 0; k < 20; ++k) mj_kdl::step(&a);
301 EXPECT_GT(a.data->time, 0.0);
302 EXPECT_EQ(b.data->time, 0.0) << "stepping one Env leaves the other alone";
303
304 KDL::Frame fb_again;
305 ASSERT_TRUE(mj_kdl::get_body_frame(&b, "bracelet_link", &fb_again));
306 EXPECT_TRUE(KDL::Equal(fb, fb_again, 1e-12));
307}
308
309TEST(EnvSpec, OwnsItsStringsAcrossARebuild)
310{
311 const std::string mjcf = gen3_path();
312 if (!fs::exists(mjcf)) GTEST_SKIP() << mjcf << " not found";
313
314 mj_kdl::Env env;
315 mj_kdl::Robot robot;
316 {
317 const mj_kdl::SceneSpec sc = arm_scene(std::string(mjcf), std::string("arm_"));
318 ASSERT_TRUE(mj_kdl::init_env(&env, &sc));
319 }
320 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&robot, &env, "arm_base_link", "arm_bracelet_link"));
321
322 const mj_kdl::Status added = mj_kdl::scene_add_object(&env, small_cube());
323 ASSERT_TRUE(added) << added.error;
324 EXPECT_EQ(env.spec.robots[0].prefix, "arm_");
325
326 for (int k = 0; k < 10; ++k) ASSERT_TRUE(mj_kdl::step(&env));
327 mj_kdl::update(&env);
328 EXPECT_GT(env.data->time, 0.0);
329 EXPECT_EQ(robot.n_joints, 7);
330 mj_kdl::cleanup(&env);
331}
332
333TEST(EnvAdopt, RunsOnTheCallersPairAndNeverFreesIt)
334{
335 const std::string mjcf = gen3_path();
336 if (!fs::exists(mjcf)) GTEST_SKIP() << mjcf << " not found";
337
338 std::vector<std::pair<mjModel *, mjData *>> owned;
339 mj_kdl::Env env;
340 mj_kdl::Robot robot;
341 env.adopt = [&](mjModel *m, mjData *d) {
342 mjModel *om = mj_copyModel(nullptr, m);
343 mjData *od = mj_makeData(om);
344 mj_copyData(od, m, d);
346 owned.emplace_back(om, od);
347 return owned.back();
348 };
349
350 const mj_kdl::SceneSpec sc = arm_scene(mjcf);
351 ASSERT_TRUE(mj_kdl::init_env(&env, &sc));
352 ASSERT_EQ(owned.size(), 1u);
353 EXPECT_EQ(env.model, owned[0].first);
354 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&robot, &env, "base_link", "bracelet_link"));
355 for (int k = 0; k < 10; ++k) ASSERT_TRUE(mj_kdl::step(&env));
356
357 ASSERT_TRUE(mj_kdl::scene_add_object(&env, small_cube()));
358 ASSERT_EQ(owned.size(), 2u);
359 EXPECT_EQ(env.model, owned[1].first);
360 EXPECT_EQ(robot.model, owned[1].first);
361 EXPECT_GT(owned[1].first->nbody, owned[0].first->nbody);
362 for (int k = 0; k < 10; ++k) ASSERT_TRUE(mj_kdl::step(&env));
363
364 const fs::path xml = fs::temp_directory_path() / "mj_kdl_adopted.xml";
365 EXPECT_TRUE(mj_kdl::save_model_xml(env.model, xml.c_str())) << "the spec follows the pair";
366 fs::remove(xml);
367
368 mj_kdl::cleanup(&env);
369 // Both pairs are still the caller's: usable here, freed once here (ASan sees a double free).
370 for (auto &[m, d] : owned) {
371 mj_step(m, d);
372 mj_deleteData(d);
373 mj_deleteModel(m);
374 }
375}
376
377TEST(SceneFloor, PlacedAtFloorZ)
378{
380 sc.timestep = 0.002;
381 sc.add_floor = true;
382 sc.floor_z = -0.72;
383 sc.add_skybox = false;
384
385 mjModel *model = nullptr;
386 mjData *data = nullptr;
387 ASSERT_TRUE(mj_kdl::build_scene(&model, &data, &sc));
388
389 // By type, not by name: the ground plane is deliberately unnamed so it cannot collide
390 // with an asset that has a geom called "floor".
391 int floor_id = -1;
392 for (int i = 0; i < model->ngeom; ++i) {
393 if (model->geom_type[i] == mjGEOM_PLANE) floor_id = i;
394 }
395 ASSERT_GE(floor_id, 0);
396 EXPECT_DOUBLE_EQ(model->geom_pos[3 * floor_id + 2], -0.72);
397
398 mj_kdl::destroy_scene(model, data);
399}
400
401TEST(Recorder, OutputPathReachesFfmpegVerbatim)
402{
404 sc.timestep = 0.002;
405 sc.add_floor = true;
406 sc.add_skybox = false;
407 mj_kdl::Env env;
408 ASSERT_TRUE(mj_kdl::init_env(&env, &sc));
409
410 const fs::path dir = fs::temp_directory_path() / "mj_kdl_rec_test";
411 fs::remove_all(dir);
412 fs::create_directories(dir);
413 const fs::path out = dir / "a \"quoted\" $(touch injected) name.mp4";
414 const fs::path marker = dir / "injected";
415
417 if (!mj_kdl::init_video_recorder(&vr, env.model, out.c_str(), 64, 48, 10))
418 GTEST_SKIP() << "no EGL or ffmpeg";
419 for (int i = 0; i < 5; ++i) ASSERT_TRUE(mj_kdl::record_frame(&vr, &env));
420 mj_kdl::cleanup(&vr);
421
422 EXPECT_TRUE(fs::exists(out)) << out;
423 EXPECT_GT(fs::file_size(out), 0u);
424 EXPECT_FALSE(fs::exists(marker)) << "the path went through a shell";
425 EXPECT_FALSE(fs::exists(fs::current_path() / "injected"));
426 fs::remove_all(dir);
427}
428
429TEST(Offscreen, RendersTheSceneIntoABuffer)
430{
432 sc.timestep = 0.002;
433 sc.add_floor = true;
434 sc.add_skybox = true;
435 mj_kdl::Env env;
436 ASSERT_TRUE(mj_kdl::init_env(&env, &sc));
437
438 constexpr int kW = 64, kH = 48;
440 if (!mj_kdl::init_offscreen(&vr, env.model, kW, kH)) GTEST_SKIP() << "no EGL";
441 std::vector<std::uint8_t> rgb(kW * kH * 3, 0);
442 ASSERT_TRUE(mj_kdl::render_rgb(&vr, &env, rgb.data()));
443 mj_kdl::cleanup(&vr);
444
445 std::size_t lit = 0;
446 for (std::uint8_t c : rgb) lit += c != 0;
447 EXPECT_GT(lit, rgb.size() / 2) << "the floor and sky fill the frame";
448}
449
450TEST_F(InitTest, AFailureSaysWhy)
451{
452 mj_kdl::Robot other;
453 mj_kdl::Status s = mj_kdl::init_robot_from_mjcf(&other, &env_, "no_such_body", "bracelet_link");
454 EXPECT_FALSE(s);
455 EXPECT_NE(s.error.find("no_such_body"), std::string::npos) << s.error;
456
458 cube.name = "unweighed_cube";
460 s = mj_kdl::scene_add_object(&env_, cube);
461 EXPECT_FALSE(s);
462 EXPECT_NE(s.error.find("unweighed_cube"), std::string::npos) << s.error;
463 s = mj_kdl::scene_remove_object(&env_, "no_such_object");
464 EXPECT_FALSE(s);
465 EXPECT_NE(s.error.find("no_such_object"), std::string::npos) << s.error;
466}
467
468TEST(SceneSpecRequired, AnUnsetFieldFailsTheBuild)
469{
470 const auto builds = [](const mj_kdl::SceneSpec &sc) {
471 mjModel *model = nullptr;
472 mjData *data = nullptr;
473 const bool ok = static_cast<bool>(mj_kdl::build_scene(&model, &data, &sc));
474 mj_kdl::destroy_scene(model, data);
475 return ok;
476 };
478 base.timestep = 0.002;
479 base.add_floor = false;
480 base.add_skybox = false;
481
482 mj_kdl::SceneObject cube = small_cube();
483 cube.friction[0] = 1.0;
484 cube.friction[1] = 0.005;
485 cube.friction[2] = 0.0001;
486
487 mj_kdl::SceneSpec complete = base;
488 complete.objects = { cube };
489 EXPECT_TRUE(builds(complete));
490
491 mj_kdl::SceneSpec no_mass = complete;
492 no_mass.objects[0].mass = NAN;
493 EXPECT_FALSE(builds(no_mass));
494
495 mj_kdl::SceneSpec no_friction = complete;
496 no_friction.objects[0].friction[1] = NAN;
497 EXPECT_FALSE(builds(no_friction));
498
500 cam.name = "cam";
501 cam.pos[0] = cam.pos[1] = 0.0;
502 cam.pos[2] = 1.0;
503 mj_kdl::SceneSpec no_fovy = base;
504 no_fovy.cameras.push_back(cam);
505 EXPECT_FALSE(builds(no_fovy));
506
507 mj_kdl::SceneSpec no_timestep = base;
508 no_timestep.timestep = NAN;
509 EXPECT_FALSE(builds(no_timestep));
510}
511
512int main(int argc, char *argv[])
513{
514 testing::InitGoogleTest(&argc, argv);
515 return RUN_ALL_TESTS();
516}
int servo(int i) const
Definition test_init.cpp:69
int joint(int i) const
Definition test_init.cpp:66
mj_kdl::Env env_
Definition test_init.cpp:54
double qpos(int i) const
Definition test_init.cpp:67
int dof(int i) const
Definition test_init.cpp:68
mj_kdl::Robot s
Definition test_init.cpp:55
void SetUp() override
Definition test_init.cpp:57
mj_kdl::SceneSpec sc_
Definition test_init.cpp:53
unsigned int getNrOfSegments() const
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
void update(Env *env)
bool step(Env *env)
Status init_offscreen(VideoRecorder *vr, mjModel *model, int width, int height)
Status init_video_recorder(VideoRecorder *vr, mjModel *model, const char *out_path, int width=1280, int height=720, int fps=60)
bool record_frame(VideoRecorder *vr, Env *env)
bool render_rgb(VideoRecorder *vr, Env *env, std::uint8_t *out)
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)
void cleanup(Robot *r)
Status init_robot_from_chain(Robot *r, Env *env, const KDL::Chain &chain, const std::vector< std::string > &joint_names, const char *prefix="", const ToolFrameSpec *tool=nullptr)
bool get_body_frame(Env *env, const char *body_name, KDL::Frame *out)
Status build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
Status scene_remove_object(Env *env, const std::string &name)
void destroy_scene(mjModel *model, mjData *data)
Status scene_add_object(Env *env, const SceneObject &obj)
Status save_model_xml(const mjModel *model, const char *path)
IMETHOD bool Equal(const FrameAcc &r1, const FrameAcc &r2, double eps=epsilon)
std::string find_asset(const fs::path &relative)
KDL::JntArray home_q(unsigned n)
Definition common.hpp:68
std::function< std::pair< mjModel *, mjData * >(mjModel *, mjData *)> adopt
std::vector< double > jnt_pos_msr
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
std::vector< CameraSpec > cameras
std::string error
int main(int argc, char *argv[])
TEST(TwoEnvs, StepIndependently)
TEST_F(InitTest, BasicDOF)
Definition test_init.cpp:79