243int main(
int argc,
char *argv[])
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") {
253 }
else if (arg ==
"--record") {
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;
272 robot_spec.
path = arm_mjcf.c_str();
273 robot_spec.
pos[2] = kSceneBaseZ;
280 scene.
robots.push_back(robot_spec);
283 .mjcf_path = table_mjcf,
284 .pos = { 0.0, 0.0, kSceneBaseZ },
287 scene.
objects.push_back(table);
288 scene.
objects.push_back(make_cube(kSceneBaseZ));
290 mjModel *model =
nullptr;
291 mjData *data =
nullptr;
293 std::cerr <<
"build_scene() failed\n";
303 &robot, model, data,
"base_link",
"bracelet_link",
"", &tool
305 std::cerr <<
"init_robot_from_mjcf() failed\n";
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";
321 KDL::JntArray q_home(n);
322 for (
unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
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;
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";
343 std::cout <<
"support segment: " << kSupportLink <<
" index=" << support_segment <<
"\n";
345 const double z_grasp = kCubeHS;
346 const double z_above = z_grasp + 0.20;
347 const double z_lift = z_grasp + 0.30;
351 fill_q_state(robot, n, q_buf, qdot_buf);
353 fk_pos.JntToCart(q_buf, home_tcp);
354 const KDL::Rotation grasp_rot = robot.
tip_T_tcp.M;
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));
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 },
374 int qadr = model->jnt_qposadr[cube_jnt];
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;
393 data->ctrl[fingers_act] = 0.0;
396 double prev_sim_time = data->time;
397 bool aborted =
false;
398 bool restart =
false;
400 auto reset_scene = [&]() {
402 prev_sim_time = data->time;
410 std::cerr <<
"init_window_sim() failed\n";
417 bool recorder_ok =
false;
423 std::cerr <<
"init_video_recorder() failed -- is EGL available and ffmpeg installed?\n";
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;
433 const int steps_per_record_frame =
434 std::max(1,
static_cast<int>(1.0 / (kRecordFps * model->opt.timestep)));
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;
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";
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;
465 if (data->time < prev_sim_time - 1e-6) {
469 prev_sim_time = data->time;
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;
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(
492 support_prev_z_valid,
520 data->ctrl[fingers_act] = phase.gripper_cmd;
522 fill_q_state(robot, n, q_buf, qdot_buf);
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
541 KDL::Twist err = KDL::diff(current, phase.target);
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;
555 if (recorder_ok && sim_step % steps_per_record_frame == 0) {
557 std::cerr <<
"record_frame() failed at sim step " << sim_step <<
"\n";
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";
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;
589 std::cout <<
"Saved recording: " << record_path <<
"\n";