136int main(
int argc,
char *argv[])
138 bool headless =
false;
139 for (
int i = 1; i < argc; ++i)
140 if (std::string(argv[i]) ==
"--headless") headless =
true;
152 robot_spec.
path = arm_mjcf.c_str();
153 robot_spec.
pos[2] = kTableZ;
160 scene.
robots.push_back(robot_spec);
163 .mjcf_path = table_mjcf,
164 .pos = { 0.0, 0.0, kTableZ },
167 scene.
objects.push_back(table);
168 scene.
objects.push_back(make_cube(kTableZ));
170 mjModel *model =
nullptr;
171 mjData *data =
nullptr;
173 std::cerr <<
"build_scene() failed\n";
177 KDL::Frame world_T_table_top;
180 std::cerr <<
"table_top site not found\n";
191 &robot, model, data,
"base_link",
"bracelet_link",
"", &tool
193 std::cerr <<
"init_robot_from_mjcf() failed\n";
198 const unsigned n = robot.
chain.getNrOfJoints();
199 const int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR,
"g_fingers_actuator");
200 const int cube_jnt = mj_name2id(model, mjOBJ_JOINT,
"cube_joint");
201 if (fingers_act < 0 || cube_jnt < 0) {
202 std::cerr <<
"required actuator or cube joint not found\n";
208 KDL::JntArray q_home(n);
209 for (
unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
211 KDL::ChainFkSolverPos_recursive fk(robot.
chain);
212 KDL::JntArray q_min(n), q_max(n);
213 std::vector<bool> joint_limited(n,
false);
214 for (
unsigned i = 0; i < n; ++i) {
216 if (model->jnt_limited[jid]) {
217 joint_limited[i] =
true;
218 q_min(i) = model->jnt_range[2 * jid];
219 q_max(i) = model->jnt_range[2 * jid + 1];
221 q_min(i) = -2 * M_PI;
225 KDL::ChainIkSolverVel_wdls ik_vel(robot.
chain, 1e-5, 150);
226 ik_vel.setLambda(0.05);
227 KDL::ChainDynParam dyn(robot.
chain, KDL::Vector(0.0, 0.0, scene.
gravity_z));
228 const KDL::Rotation kGraspRot = robot.
tip_T_tcp.M;
230 const double z_grasp = kCubeHS;
231 const double z_above = z_grasp + 0.20;
232 const double z_lift = z_grasp + 0.30;
234 KDL::JntArray q_pick_above(n), q_pick(n), q_lift(n), q_place_above(n), q_place(n);
241 const KDL::JntArray *seed;
243 Waypoint waypoints[] = {
244 { kPickX, kPickY, kTableZ + z_above, &q_pick_above, &q_home },
245 { kPickX, kPickY, kTableZ + z_grasp, &q_pick, &q_pick_above },
246 { kPickX, kPickY, kTableZ + z_lift, &q_lift, &q_pick },
247 { kPlaceX, kPlaceY, kTableZ + z_above, &q_place_above, &q_lift },
248 { kPlaceX, kPlaceY, kTableZ + z_grasp, &q_place, &q_place_above },
250 KDL::Frame world_T_base(KDL::Rotation::Identity(), KDL::Vector(0.0, 0.0, kTableZ));
251 KDL::Frame base_T_world = world_T_base.Inverse();
252 for (
const auto &wp : waypoints) {
253 KDL::Frame world_target(kGraspRot, KDL::Vector(wp.world_x, wp.world_y, wp.world_z));
254 KDL::Frame base_target = base_T_world * world_target;
255 if (!solve_near_seed(
256 ik_vel, fk, *wp.seed, base_target, joint_limited, q_min, q_max, *wp.out
258 std::cerr <<
"IK failed for waypoint at world [" << wp.world_x <<
", " << wp.world_y
259 <<
", " << wp.world_z <<
"]\n";
265 fk.JntToCart(*wp.out, fk_out);
266 double pos_err = (base_target.p - fk_out.p).Norm();
267 if (pos_err > kIkTol) {
268 std::cerr <<
"IK pose error " << pos_err <<
" exceeds tolerance at world ["
269 << wp.world_x <<
", " << wp.world_y <<
", " << wp.world_z <<
"]\n";
275 const std::vector<Phase> phases = {
276 { .name =
"HOME", .target = &q_home, .duration = 1.0, .timeout = 2.5, .settle_tol = 0.08, .gripper_cmd = 0.0 },
277 { .name =
"PICK_ABOVE", .target = &q_pick_above, .duration = 5.0, .timeout = 7.0, .settle_tol = 0.08, .gripper_cmd = 0.0 },
278 { .name =
"PICK", .target = &q_pick, .duration = 5.0, .timeout = 8.0, .settle_tol = 0.03, .gripper_cmd = 0.0 },
279 { .name =
"CLOSE", .target = &q_pick, .duration = 1.5, .timeout = 2.5, .settle_tol = -1.0, .gripper_cmd = 0.8 },
280 { .name =
"LIFT", .target = &q_lift, .duration = 3.0, .timeout = 5.0, .settle_tol = 0.08, .gripper_cmd = 0.8 },
281 { .name =
"PLACE_ABOVE", .target = &q_place_above, .duration = 3.0, .timeout = 5.0, .settle_tol = 0.08, .gripper_cmd = 0.8 },
282 { .name =
"PLACE", .target = &q_place, .duration = 5.0, .timeout = 8.0, .settle_tol = 0.03, .gripper_cmd = 0.8 },
283 { .name =
"OPEN", .target = &q_place, .duration = 1.0, .timeout = 2.0, .settle_tol = -1.0, .gripper_cmd = 0.0 },
284 { .name =
"RETREAT", .target = &q_place_above, .duration = 2.0, .timeout = 4.0, .settle_tol = 0.08, .gripper_cmd = 0.0 },
285 { .name =
"HOLD", .target = &q_place_above, .duration = headless ? 1.0 : 1e9, .timeout = headless ? 1.0 : 1e9, .settle_tol = -1.0, .gripper_cmd = 0.0 },
289 int qadr = model->jnt_qposadr[cube_jnt];
291 auto reset_cube = [&]() {
292 data->qpos[qadr] = kPickX;
293 data->qpos[qadr + 1] = kPickY;
294 data->qpos[qadr + 2] = kTableZ + kCubeHS;
295 data->qpos[qadr + 3] = 1.0;
296 data->qpos[qadr + 4] = data->qpos[qadr + 5] = data->qpos[qadr + 6] = 0.0;
308 data->ctrl[fingers_act] = 0.0;
311 KDL::JntArray q_enter(n), q_des(n);
313 double prev_sim_time = data->time;
314 bool aborted =
false;
315 bool restart =
false;
317 auto reset_scene = [&]() {
320 prev_sim_time = data->time;
328 std::cerr <<
"init_window_sim() failed\n";
336 for (
const Phase &phase : phases) {
337 if (aborted || restart)
break;
338 std::cout <<
"State: " << phase.name <<
"\n";
339 double t_enter = data->time;
340 snapshot_q(robot, n, q_enter);
343 if (data->time < prev_sim_time - 1e-6) {
347 prev_sim_time = data->time;
350 phase.duration > 0.0 ? clamp01((data->time - t_enter) / phase.duration) : 1.0;
351 lerp_q(q_enter, *phase.target, alpha, q_des);
352 impedance_ctrl(robot, q_des, n, dyn);
354 data->ctrl[fingers_act] = phase.gripper_cmd;
355 closed = phase.gripper_cmd > 0.0;
357 double t_rel = data->time - t_enter;
358 bool done_time = t_rel >= phase.duration;
359 bool done_pose = phase.settle_tol < 0.0
360 || max_abs_joint_err(robot, *phase.target, n) <= phase.settle_tol;
361 bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
362 if ((done_time && done_pose) || done_timeout)
break;
374 double cube_x = data->qpos[qadr];
375 double cube_y = data->qpos[qadr + 1];
376 double cube_z = data->qpos[qadr + 2];
377 double place_err_xy = std::hypot(cube_x - kPlaceX, cube_y - kPlaceY);
378 std::cout <<
"cube final position: [" << std::fixed << std::setprecision(3) << cube_x
379 <<
", " << cube_y <<
", " << cube_z <<
"]"
380 <<
" target=[" << kPlaceX <<
", " << kPlaceY <<
", " << kTableZ + kCubeHS
381 <<
"] xy_error=" << place_err_xy <<
" gripper=" << (closed ?
"closed" :
"open")
383 if (headless && place_err_xy > 0.08) ret = 1;