Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_achd_pick_place.cpp
Go to the documentation of this file.
1/* ex_achd_pick_place.cpp
2 * Kinova GEN3 + Robotiq 2F-85 picks a cube from one table location and places
3 * it at another using Cartesian acceleration-constrained hybrid dynamics
4 * via KDL ChainHdSolver_Vereshchagin_Fixed_Joint + RNEA:
5 *
6 * Xddot_des = Kp * diff(T_tcp, T_target) + Kd * d/dt(diff)
7 * beta = alpha^T * Xddot_des, with alpha = I_6
8 * qddot = ACHD(q, qdot, alpha, beta) [constrained joint accelerations]
9 * tau = RNEA(q, qdot, qddot) [full inverse dynamics for MuJoCo]
10 *
11 * mj_kdl_wrapper's TORQUE mode fully nulls the MuJoCo position actuators
12 * (ctrl = qpos + kv/kp * qvel), so qfrc_applied is the sole torque source.
13 *
14 * Usage:
15 * ex_achd_pick_place [--headless] [--record output.mp4]
16 *
17 * With --headless runs the full sequence and prints final cube position. */
18
20#include "example_paths.hpp"
21
22#include <kdl/chainhdsolver_vereshchagin_fixed_joint.hpp>
23#include <kdl/chainfksolverpos_recursive.hpp>
24#include <kdl/chainfksolvervel_recursive.hpp>
25#include <kdl/chainidsolver_recursive_newton_euler.hpp>
26
27#include <algorithm>
28#include <cmath>
29#include <iomanip>
30#include <iostream>
31#include <string>
32#include <vector>
33
34static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
35static constexpr double kCubeHS = 0.02;
36static constexpr double kSceneBaseZ = 0.70;
37static constexpr double kPickX = 0.40;
38static constexpr double kPickY = 0.00;
39static constexpr double kPlaceX = 0.40;
40static constexpr double kPlaceY = 0.24;
41static constexpr double kTableZ = 0.0;
42
43// Cartesian acceleration gains. ACHD consumes acceleration-energy setpoints; with
44// alpha = I_6 these are the desired TCP linear/angular accelerations.
45static constexpr double kKpLin = 200.0;
46static constexpr double kKiLin = 100.0;
47static constexpr double kKdLin = 40.0;
48static constexpr double kKpRot = 120.0;
49static constexpr double kKiRot = 50.0;
50static constexpr double kKdRot = 80.0;
51static constexpr double kBetaMaxLin = 120.0;
52static constexpr double kBetaMaxRot = 80.0;
53static constexpr double kTauMax = 59.0;
54static constexpr double kIntegralMax = 0.5;
55static constexpr int kRecordFps = 30;
56static constexpr const char *kSupportLink = "half_arm_2_link";
57static constexpr double kSupportKp = 800.0;
58static constexpr double kSupportKd = 80.0;
59static constexpr double kSupportFMax = 45.0;
60static constexpr double kSupportLift = 0.06;
61
62static double clamp01(double v) { return std::max(0.0, std::min(1.0, v)); }
63static double clamp_abs(double v, double limit) { return std::max(-limit, std::min(limit, v)); }
64static double smoothstep(double t)
65{
66 t = clamp01(t);
67 return t * t * (3.0 - 2.0 * t);
68}
69
70static KDL::Frame lerp_frame(const KDL::Frame &a, const KDL::Frame &b, double t)
71{
72 return KDL::addDelta(a, KDL::diff(a, b), clamp01(t));
73}
74
75static void fill_q_state(
76 const mj_kdl::Robot &robot,
77 unsigned n,
78 KDL::JntArray &q,
79 KDL::JntArray &qdot
80)
81{
82 for (unsigned i = 0; i < n; ++i) {
83 q(i) = robot.jnt_pos_msr[i];
84 qdot(i) = robot.jnt_vel_msr[i];
85 }
86}
87
88static int find_segment_index(const KDL::Chain &chain, const std::string &name)
89{
90 for (unsigned i = 0; i < chain.getNrOfSegments(); ++i) {
91 if (chain.getSegment(i).getName() == name) return static_cast<int>(i);
92 }
93 return -1;
94}
95
96static bool support_phase(const std::string &name)
97{
98 return name == "PLACE_ABOVE" || name == "PLACE" || name == "OPEN" || name == "RETREAT"
99 || name == "HOLD";
100}
101
102static void clear_wrenches(KDL::Wrenches &wrenches)
103{
104 std::fill(wrenches.begin(), wrenches.end(), KDL::Wrench::Zero());
105}
106
107static double link_world_z(
108 KDL::ChainFkSolverPos_recursive &fk_pos,
109 const KDL::JntArray &q,
110 int segment_index
111)
112{
113 KDL::Frame base_T_link;
114 fk_pos.JntToCart(q, base_T_link, segment_index + 1);
115 return kSceneBaseZ + base_T_link.p.z();
116}
117
118static double apply_support_wrench(
119 KDL::ChainFkSolverPos_recursive &fk_pos,
120 const KDL::JntArray &q,
121 int segment_index,
122 double z_ref,
123 double dt,
124 double &prev_z,
125 bool &prev_z_valid,
126 KDL::Wrenches &f_ext_achd
127)
128{
129 KDL::Frame base_T_link;
130 fk_pos.JntToCart(q, base_T_link, segment_index + 1);
131
132 const double z_world = kSceneBaseZ + base_T_link.p.z();
133 const double vz = prev_z_valid ? (z_world - prev_z) / dt : 0.0;
134 prev_z = z_world;
135 prev_z_valid = true;
136
137 const double fz = std::min(
138 kSupportFMax,
139 std::max(0.0, kSupportKp * (z_ref - z_world) - kSupportKd * vz)
140 );
141 const KDL::Wrench wrench_base(KDL::Vector(0.0, 0.0, fz), KDL::Vector::Zero());
142 f_ext_achd[segment_index] = wrench_base;
143 return fz;
144}
145
146static void achd_cartesian_ctrl(
147 mj_kdl::Robot &robot,
148 const KDL::Frame &target,
149 unsigned n,
150 KDL::ChainFkSolverPos_recursive &fk_pos,
151 KDL::ChainFkSolverVel_recursive &fk_vel,
152 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint &achd,
153 KDL::ChainIdSolver_RNE &rnea,
154 KDL::JntArray &q,
155 KDL::JntArray &qdot,
156 KDL::JntArray &qddot,
157 KDL::Jacobian &alpha,
158 KDL::JntArray &beta,
159 KDL::Wrenches &f_ext_achd,
160 KDL::Wrenches &f_ext_rnea_zero,
161 KDL::JntArray &ff_torques,
162 KDL::JntArray &constraint_torques,
163 KDL::JntArray &tau_cmd,
164 const KDL::Twist &target_twist,
165 std::array<double, 6> &err_i,
166 std::array<double, 6> &err_prev,
167 bool &first_pid
168)
169{
170 fill_q_state(robot, n, q, qdot);
171
172 KDL::Frame current;
173 fk_pos.JntToCart(q, current);
174 KDL::FrameVel current_vel;
175 fk_vel.JntToCart(KDL::JntArrayVel(q, qdot), current_vel);
176 const KDL::Twist tcp_twist = current_vel.deriv();
177
178 const KDL::Twist err = KDL::diff(current, target);
179 const double dt = robot.model ? robot.model->opt.timestep : 0.002;
180 const double e[6] = { err.vel.x(), err.vel.y(), err.vel.z(),
181 err.rot.x(), err.rot.y(), err.rot.z() };
182 if (first_pid) {
183 for (unsigned i = 0; i < 6; ++i) err_prev[i] = e[i];
184 first_pid = false;
185 }
186 for (unsigned i = 0; i < 6; ++i) err_i[i] = clamp_abs(err_i[i] + e[i] * dt, kIntegralMax);
187 const double de[6] = {
188 (e[0] - err_prev[0]) / dt,
189 (e[1] - err_prev[1]) / dt,
190 (e[2] - err_prev[2]) / dt,
191 (e[3] - err_prev[3]) / dt,
192 (e[4] - err_prev[4]) / dt,
193 (e[5] - err_prev[5]) / dt,
194 };
195
196 beta(0) = clamp_abs(kKpLin * e[0] + kKiLin * err_i[0] + kKdLin * de[0], kBetaMaxLin);
197 beta(1) = clamp_abs(kKpLin * e[1] + kKiLin * err_i[1] + kKdLin * de[1], kBetaMaxLin);
198 beta(2) = clamp_abs(kKpLin * e[2] + kKiLin * err_i[2] + kKdLin * de[2], kBetaMaxLin);
199 beta(3) = clamp_abs(kKpRot * e[3] + kKiRot * err_i[3] + kKdRot * (target_twist.rot.x() - tcp_twist.rot.x()), kBetaMaxRot);
200 beta(4) = clamp_abs(kKpRot * e[4] + kKiRot * err_i[4] + kKdRot * (target_twist.rot.y() - tcp_twist.rot.y()), kBetaMaxRot);
201 beta(5) = clamp_abs(kKpRot * e[5] + kKiRot * err_i[5] + kKdRot * (target_twist.rot.z() - tcp_twist.rot.z()), kBetaMaxRot);
202 for (unsigned i = 0; i < 6; ++i) err_prev[i] = e[i];
203 KDL::SetToZero(ff_torques);
204 if (achd.CartToJnt(q, qdot, qddot, alpha, beta, f_ext_achd, ff_torques, constraint_torques) < 0) {
205 std::cerr << "ACHD CartToJnt() failed\n";
206 std::fill(robot.jnt_trq_cmd.begin(), robot.jnt_trq_cmd.end(), 0.0);
207 return;
208 }
209 if (rnea.CartToJnt(q, qdot, qddot, f_ext_rnea_zero, tau_cmd) < 0) {
210 std::cerr << "RNEA CartToJnt() failed\n";
211 std::fill(robot.jnt_trq_cmd.begin(), robot.jnt_trq_cmd.end(), 0.0);
212 return;
213 }
214 for (unsigned i = 0; i < n; ++i) robot.jnt_trq_cmd[i] = clamp_abs(tau_cmd(i), kTauMax);
215}
216
217static mj_kdl::SceneObject make_cube(double surface_z)
218{
219 return {
220 .name = "cube",
221 .mjcf_path = "",
222 .shape = mj_kdl::Shape::BOX,
223 .size = { kCubeHS, kCubeHS, kCubeHS },
224 .pos = { kPickX, kPickY, surface_z + kCubeHS },
225 .rgba = { 0.1f, 0.35f, 1.0f, 1.0f },
226 .mass = 0.1,
228 .friction = { 0.8, 0.02, 0.001 },
229 };
230}
231
232struct Phase
233{
234 const char *name;
235 KDL::Frame target;
236 double duration;
237 double timeout;
241};
242
243int main(int argc, char *argv[])
244{
245 bool headless = false;
246 bool do_record = false;
247 bool print_ee_angular_vel = false;
248 std::string record_path = "achd_pick_place.mp4";
249 for (int i = 1; i < argc; ++i) {
250 std::string arg(argv[i]);
251 if (arg == "--headless") {
252 headless = true;
253 } else if (arg == "--record") {
254 do_record = true;
255 headless = true;
256 if (i + 1 < argc && argv[i + 1][0] != '-') record_path = argv[++i];
257 } else if (arg == "--print-ee-angular-vel") {
258 print_ee_angular_vel = true;
259 }
260 }
261
262 const std::string arm_mjcf = mj_kdl_examples::menagerie_model("kinova_gen3/gen3.xml");
263 const std::string grp_mjcf = mj_kdl_examples::asset("robotiq_2f85/2f85.xml");
264 const std::string table_mjcf = mj_kdl_examples::asset("table.xml");
265
267 gripper.mjcf_path = grp_mjcf.c_str();
268 gripper.attach_to = { mj_kdl::AttachKind::Site, "pinch_site" };
269 gripper.prefix = "g_";
270
271 mj_kdl::RobotSpec robot_spec;
272 robot_spec.path = arm_mjcf.c_str();
273 robot_spec.pos[2] = kSceneBaseZ;
274 robot_spec.attachments.push_back(gripper);
275
276 mj_kdl::SceneSpec scene;
277 scene.timestep = 0.002;
278 scene.add_floor = true;
279 scene.add_skybox = true;
280 scene.robots.push_back(robot_spec);
282 .name = "table",
283 .mjcf_path = table_mjcf,
284 .pos = { 0.0, 0.0, kSceneBaseZ },
285 .fixed = true,
286 };
287 scene.objects.push_back(table);
288 scene.objects.push_back(make_cube(kSceneBaseZ));
289
290 mjModel *model = nullptr;
291 mjData *data = nullptr;
292 if (!mj_kdl::build_scene(&model, &data, &scene)) {
293 std::cerr << "build_scene() failed\n";
294 return 1;
295 }
296
298 tool.tool_body = "g_base";
299 tool.tcp_site = "g_pinch";
300
301 mj_kdl::Robot robot;
303 &robot, model, data, "base_link", "bracelet_link", "", &tool
304 )) {
305 std::cerr << "init_robot_from_mjcf() failed\n";
306 mj_kdl::destroy_scene(model, data);
307 return 1;
308 }
309
310 const unsigned n = robot.chain.getNrOfJoints();
311 const unsigned ns = robot.chain.getNrOfSegments();
312 const int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR, "g_fingers_actuator");
313 const int cube_jnt = mj_name2id(model, mjOBJ_JOINT, "cube_joint");
314 if (fingers_act < 0 || cube_jnt < 0) {
315 std::cerr << "required actuator or cube joint not found\n";
316 mj_kdl::cleanup(&robot);
317 mj_kdl::destroy_scene(model, data);
318 return 1;
319 }
320
321 KDL::JntArray q_home(n);
322 for (unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
323
324 KDL::ChainFkSolverPos_recursive fk_pos(robot.chain);
325 KDL::ChainFkSolverVel_recursive fk_vel(robot.chain);
326 KDL::Twist root_acc(KDL::Vector(0.0, 0.0, -scene.gravity_z), KDL::Vector::Zero());
327 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint achd(robot.chain, root_acc, 6);
328 KDL::ChainIdSolver_RNE rnea(robot.chain, KDL::Vector(0.0, 0.0, scene.gravity_z));
329 KDL::JntArray q_buf(n), qdot_buf(n), qddot(n), ff_torques(n), constraint_torques(n), tau_cmd(n);
330 KDL::Wrenches f_ext_achd(ns, KDL::Wrench::Zero());
331 KDL::Wrenches f_ext_rnea_zero(ns, KDL::Wrench::Zero());
332 KDL::Jacobian alpha(6);
333 KDL::JntArray beta(6);
334 for (unsigned i = 0; i < 6; ++i) alpha(i, i) = 1.0;
335
336 const int support_segment = find_segment_index(robot.chain, kSupportLink);
337 if (support_segment < 0) {
338 std::cerr << "support segment not found: " << kSupportLink << "\n";
339 mj_kdl::cleanup(&robot);
340 mj_kdl::destroy_scene(model, data);
341 return 1;
342 }
343 std::cout << "support segment: " << kSupportLink << " index=" << support_segment << "\n";
344
345 const double z_grasp = kCubeHS;
346 const double z_above = z_grasp + 0.20;
347 const double z_lift = z_grasp + 0.30;
348
349 mj_kdl::set_joint_pos(&robot, q_home, false);
350 mj_kdl::update(&robot);
351 fill_q_state(robot, n, q_buf, qdot_buf);
352 KDL::Frame home_tcp;
353 fk_pos.JntToCart(q_buf, home_tcp);
354 const KDL::Rotation grasp_rot = robot.tip_T_tcp.M;
355
356 auto target_frame = [&](double base_x, double base_y, double base_z) {
357 return KDL::Frame(grasp_rot, KDL::Vector(base_x, base_y, base_z));
358 };
359
360 const std::vector<Phase> phases = {
361 { "HOME", home_tcp, 1.0, 2.5, 0.03, 0.05, 0.0 },
362 { "PICK_ABOVE", target_frame(kPickX, kPickY, kTableZ + z_above), 8.0, 14.0, 0.04, 0.03, 0.0 },
363 { "PICK", target_frame(kPickX, kPickY, kTableZ + z_grasp), 5.0, 12.0, 0.02, 0.03, 0.0 },
364 { "CLOSE", target_frame(kPickX, kPickY, kTableZ + z_grasp), 1.5, 2.5, -1.0, -1.0, 0.8 },
365 { "LIFT", target_frame(kPickX, kPickY, kTableZ + z_lift), 3.0, 8.0, 0.04, 0.03, 0.8 },
366 { "PLACE_ABOVE", target_frame(kPlaceX, kPlaceY, kTableZ + z_above), 5.0, 12.0, 0.04, 0.03, 0.8 },
367 { "PLACE", target_frame(kPlaceX, kPlaceY, kTableZ + z_grasp), 5.0, 14.0, 0.02, 0.03, 0.8 },
368 { "OPEN", target_frame(kPlaceX, kPlaceY, kTableZ + z_grasp), 1.0, 2.0, -1.0, -1.0, 0.0 },
369 { "RETREAT", target_frame(kPlaceX, kPlaceY, kTableZ + z_above), 3.0, 6.0, 0.04, 0.08, 0.0 },
370 { "HOLD", target_frame(kPlaceX, kPlaceY, kTableZ + z_above), headless ? 4.0 : 1e9, headless ? 4.0 : 1e9, -1.0, -1.0, 0.0 },
371 };
372
374 int qadr = model->jnt_qposadr[cube_jnt];
375
376 auto reset_cube = [&]() {
377 data->qpos[qadr] = kPickX;
378 data->qpos[qadr + 1] = kPickY;
379 data->qpos[qadr + 2] = kSceneBaseZ + kCubeHS;
380 data->qpos[qadr + 3] = 1.0;
381 data->qpos[qadr + 4] = data->qpos[qadr + 5] = data->qpos[qadr + 6] = 0.0;
382 };
383
384 mj_kdl::Env env;
385 env.spec = scene;
386 env.model = model;
387 env.data = data;
388 mj_kdl::env_add_robot(&env, &robot);
389
390 env.on_reset = [&](mj_kdl::ResetContext *) {
391 mj_kdl::set_joint_pos(&robot, q_home, false);
392 reset_cube();
393 data->ctrl[fingers_act] = 0.0;
394 };
395
396 double prev_sim_time = data->time;
397 bool aborted = false;
398 bool restart = false;
399
400 auto reset_scene = [&]() {
401 mj_kdl::reset(&env);
402 prev_sim_time = data->time;
403 restart = true;
404 };
405
406 reset_scene();
407
408 mj_kdl::Viewer viewer;
409 if (!headless && !mj_kdl::init_window_sim(&viewer, &robot)) {
410 std::cerr << "init_window_sim() failed\n";
411 mj_kdl::cleanup(&robot);
412 mj_kdl::destroy_scene(model, data);
413 return 1;
414 }
415
416 mj_kdl::VideoRecorder recorder;
417 bool recorder_ok = false;
418 if (do_record) {
419 recorder_ok = mj_kdl::init_video_recorder(
420 &recorder, model, record_path.c_str(), mj_kdl::VideoResolution::R720p, kRecordFps
421 );
422 if (!recorder_ok)
423 std::cerr << "init_video_recorder() failed -- is EGL available and ffmpeg installed?\n";
424 else {
425 recorder.cam.azimuth = 145.0;
426 recorder.cam.elevation = -22.0;
427 recorder.cam.distance = 1.35;
428 recorder.cam.lookat[0] = 0.12;
429 recorder.cam.lookat[1] = 0.12;
430 recorder.cam.lookat[2] = 0.90;
431 }
432 }
433 const int steps_per_record_frame =
434 std::max(1, static_cast<int>(1.0 / (kRecordFps * model->opt.timestep)));
435 int sim_step = 0;
436
437 do {
438 restart = false;
439 double support_z_ref = 0.0;
440 bool support_z_ref_valid = false;
441 double support_prev_z = 0.0;
442 bool support_prev_z_valid = false;
443 for (const Phase &phase : phases) {
444 if (aborted || restart) break;
445 std::cout << "State: " << phase.name << "\n";
446 double t_enter = data->time;
447 mj_kdl::update(&robot);
448 fill_q_state(robot, n, q_buf, qdot_buf);
449 KDL::Frame phase_start;
450 fk_pos.JntToCart(q_buf, phase_start);
451 if (std::string(phase.name) == "PLACE_ABOVE" || (support_phase(phase.name) && !support_z_ref_valid)) {
452 support_z_ref = link_world_z(fk_pos, q_buf, support_segment) + kSupportLift;
453 support_z_ref_valid = true;
454 support_prev_z = support_z_ref;
455 support_prev_z_valid = true;
456 std::cout << " support_z_ref=" << std::fixed << std::setprecision(3)
457 << support_z_ref << "\n";
458 }
459
460 std::array<double, 6> err_i{}, err_prev{};
461 bool first_pid = true;
462 KDL::Frame prev_target = phase_start;
463 bool first_target = true;
464 while (true) {
465 if (data->time < prev_sim_time - 1e-6) {
466 reset_scene();
467 break;
468 }
469 prev_sim_time = data->time;
470
471 double phase_alpha =
472 phase.duration > 0.0 ? smoothstep((data->time - t_enter) / phase.duration) : 1.0;
473 KDL::Frame target = lerp_frame(phase_start, phase.target, phase_alpha);
474 const double dt = model->opt.timestep;
475 KDL::Twist target_twist = KDL::Twist::Zero();
476 if (!first_target) target_twist = KDL::diff(prev_target, target, dt);
477 prev_target = target;
478 first_target = false;
479 mj_kdl::update(&robot);
480 fill_q_state(robot, n, q_buf, qdot_buf);
481 clear_wrenches(f_ext_achd);
482 clear_wrenches(f_ext_rnea_zero);
483 double support_force = 0.0;
484 if (support_phase(phase.name) && support_z_ref_valid) {
485 support_force = apply_support_wrench(
486 fk_pos,
487 q_buf,
488 support_segment,
489 support_z_ref,
490 dt,
491 support_prev_z,
492 support_prev_z_valid,
493 f_ext_achd
494 );
495 }
496 achd_cartesian_ctrl(
497 robot,
498 target,
499 n,
500 fk_pos,
501 fk_vel,
502 achd,
503 rnea,
504 q_buf,
505 qdot_buf,
506 qddot,
507 alpha,
508 beta,
509 f_ext_achd,
510 f_ext_rnea_zero,
511 ff_torques,
512 constraint_torques,
513 tau_cmd,
514 target_twist,
515 err_i,
516 err_prev,
517 first_pid
518 );
519 mj_kdl::update(&robot);
520 data->ctrl[fingers_act] = phase.gripper_cmd;
521
522 fill_q_state(robot, n, q_buf, qdot_buf);
523 KDL::Frame current;
524 fk_pos.JntToCart(q_buf, current);
525 if (print_ee_angular_vel) {
526 KDL::FrameVel current_vel;
527 fk_vel.JntToCart(KDL::JntArrayVel(q_buf, qdot_buf), current_vel);
528 const KDL::Twist ee_twist = current_vel.deriv();
529 std::cout << "ee_ang_vel"
530 << " phase=" << phase.name
531 << " t=" << std::fixed << std::setprecision(4) << (data->time - t_enter)
532 << " wx=" << ee_twist.rot.x()
533 << " wy=" << ee_twist.rot.y()
534 << " wz=" << ee_twist.rot.z()
535 << " target_wx=" << target_twist.rot.x()
536 << " target_wy=" << target_twist.rot.y()
537 << " target_wz=" << target_twist.rot.z()
538 << " support_fz=" << support_force
539 << "\n";
540 }
541 KDL::Twist err = KDL::diff(current, phase.target);
542
543 double t_rel = data->time - t_enter;
544 bool done_time = t_rel >= phase.duration;
545 bool done_pose = phase.settle_pos_tol < 0.0
546 || (err.vel.Norm() <= phase.settle_pos_tol
547 && err.rot.Norm() <= phase.settle_rot_tol);
548 bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
549 if ((done_time && done_pose) || done_timeout) break;
550
551 if (!mj_kdl::step(&robot)) {
552 aborted = true;
553 break;
554 }
555 if (recorder_ok && sim_step % steps_per_record_frame == 0) {
556 if (!mj_kdl::record_frame(&recorder, model, data)) {
557 std::cerr << "record_frame() failed at sim step " << sim_step << "\n";
558 mj_kdl::cleanup(&recorder);
559 recorder_ok = false;
560 }
561 }
562 ++sim_step;
563 }
564 fill_q_state(robot, n, q_buf, qdot_buf);
565 KDL::Frame phase_end;
566 fk_pos.JntToCart(q_buf, phase_end);
567 KDL::Twist phase_err = KDL::diff(phase_end, phase.target);
568 std::cout << " pos_err=" << std::fixed << std::setprecision(3)
569 << phase_err.vel.Norm() << " rot_err=" << phase_err.rot.Norm()
570 << " t=" << data->time - t_enter << "\n";
571 }
572 } while (restart);
573
574 int ret = 0;
575 if (!aborted) {
576 double cube_x = data->qpos[qadr];
577 double cube_y = data->qpos[qadr + 1];
578 double cube_z = data->qpos[qadr + 2];
579 double place_err_xy = std::hypot(cube_x - kPlaceX, cube_y - kPlaceY);
580 std::cout << "cube final position: [" << std::fixed << std::setprecision(3) << cube_x
581 << ", " << cube_y << ", " << cube_z << "]"
582 << " target=[" << kPlaceX << ", " << kPlaceY << ", " << kSceneBaseZ + kCubeHS
583 << "] xy_error=" << place_err_xy << "\n";
584 if (headless && place_err_xy > 0.08) ret = 1;
585 }
586 if (!headless) mj_kdl::cleanup(&viewer);
587 if (recorder_ok) {
588 mj_kdl::cleanup(&recorder);
589 std::cout << "Saved recording: " << record_path << "\n";
590 }
591 mj_kdl::cleanup(&robot);
592 mj_kdl::destroy_scene(model, data);
593 return ret;
594}
int main(int argc, char *argv[])
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
bool 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, mjModel *model, mjData *data)
bool step(Robot *s)
void cleanup(Robot *r)
bool init_robot_from_mjcf(Robot *r, mjModel *model, mjData *data, 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 call_forward=true)
void update(Robot *r)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
std::string asset(const fs::path &relative)
std::string menagerie_model(const fs::path &relative)
KDL::Frame target
double settle_rot_tol
const char * name
double settle_pos_tol
ResetHook on_reset
std::vector< AttachmentSpec > attachments
std::vector< double > jnt_pos_msr
std::vector< double > jnt_vel_msr
std::vector< double > jnt_trq_cmd
KDL::Frame tip_T_tcp
std::vector< RobotSpec > robots
std::vector< SceneObject > objects