139int main(
int argc,
char *argv[])
141 bool headless =
false;
142 for (
int i = 1; i < argc; ++i)
143 if (std::string(argv[i]) ==
"--headless") headless =
true;
145 const fs::path root = repo_root();
146 if (!fs::exists(root /
"third_party/menagerie")) {
147 std::cerr <<
"third_party/menagerie/ not found - run:\n"
148 " cmake -B build -DFETCH_MENAGERIE=ON\n";
152 const std::string arm_mjcf = (root /
"third_party/menagerie/kinova_gen3/gen3.xml").
string();
153 const std::string grp_mjcf = (root /
"third_party/menagerie/robotiq_2f85/2f85.xml").
string();
154 const std::string table_mjcf = (root /
"src/examples/assets/table.xml").
string();
160 gripper.
pos[2] = -0.061525;
161 gripper.
euler[0] = 180.0;
164 robot_spec.
path = arm_mjcf.c_str();
165 robot_spec.
pos[2] = kTableZ;
169 scene.
robots.push_back(robot_spec);
172 .mjcf_path = table_mjcf,
173 .pos = { 0.0, 0.0, kTableZ },
176 scene.
objects.push_back(table);
177 scene.
objects.push_back(make_cube(kTableZ));
179 mjModel *model =
nullptr;
180 mjData *data =
nullptr;
182 std::cerr <<
"build_scene() failed\n";
186 KDL::Frame world_T_table_top;
189 std::cerr <<
"table_top site not found\n";
200 &robot, model, data,
"base_link",
"bracelet_link",
"", &tool
202 std::cerr <<
"init_robot_from_mjcf() failed\n";
207 const unsigned n = robot.
chain.getNrOfJoints();
208 const int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR,
"g_fingers_actuator");
209 const int cube_jnt = mj_name2id(model, mjOBJ_JOINT,
"cube_joint");
210 if (fingers_act < 0 || cube_jnt < 0) {
211 std::cerr <<
"required actuator or cube joint not found\n";
217 KDL::JntArray q_home(n);
218 for (
unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
220 KDL::ChainFkSolverPos_recursive fk(robot.
chain);
221 KDL::JntArray q_min(n), q_max(n);
222 std::vector<bool> joint_limited(n,
false);
223 for (
unsigned i = 0; i < n; ++i) {
225 if (model->jnt_limited[jid]) {
226 joint_limited[i] =
true;
227 q_min(i) = model->jnt_range[2 * jid];
228 q_max(i) = model->jnt_range[2 * jid + 1];
230 q_min(i) = -2 * M_PI;
234 KDL::ChainIkSolverVel_wdls ik_vel(robot.
chain, 1e-5, 150);
235 ik_vel.setLambda(0.05);
236 KDL::ChainDynParam dyn(robot.
chain, KDL::Vector(0.0, 0.0, scene.
gravity_z));
237 const KDL::Rotation kGraspRot = robot.
tip_T_tcp.M;
239 const double z_grasp = kCubeHS;
240 const double z_above = z_grasp + 0.20;
241 const double z_lift = z_grasp + 0.30;
243 KDL::JntArray q_pick_above(n), q_pick(n), q_lift(n), q_place_above(n), q_place(n);
250 const KDL::JntArray *seed;
252 Waypoint waypoints[] = {
253 { kPickX, kPickY, kTableZ + z_above, &q_pick_above, &q_home },
254 { kPickX, kPickY, kTableZ + z_grasp, &q_pick, &q_pick_above },
255 { kPickX, kPickY, kTableZ + z_lift, &q_lift, &q_pick },
256 { kPlaceX, kPlaceY, kTableZ + z_above, &q_place_above, &q_lift },
257 { kPlaceX, kPlaceY, kTableZ + z_grasp, &q_place, &q_place_above },
259 KDL::Frame world_T_base(KDL::Rotation::Identity(), KDL::Vector(0.0, 0.0, kTableZ));
260 KDL::Frame base_T_world = world_T_base.Inverse();
261 for (
const auto &wp : waypoints) {
262 KDL::Frame world_target(kGraspRot, KDL::Vector(wp.world_x, wp.world_y, wp.world_z));
263 KDL::Frame base_target = base_T_world * world_target;
264 if (!solve_near_seed(
265 ik_vel, fk, *wp.seed, base_target, joint_limited, q_min, q_max, *wp.out
267 std::cerr <<
"IK failed for waypoint at world [" << wp.world_x <<
", " << wp.world_y
268 <<
", " << wp.world_z <<
"]\n";
274 fk.JntToCart(*wp.out, fk_out);
275 double pos_err = (base_target.p - fk_out.p).Norm();
276 if (pos_err > kIkTol) {
277 std::cerr <<
"IK pose error " << pos_err <<
" exceeds tolerance at world ["
278 << wp.world_x <<
", " << wp.world_y <<
", " << wp.world_z <<
"]\n";
284 const std::vector<Phase> phases = {
285 { .name =
"HOME", .target = &q_home, .duration = 1.0, .timeout = 2.5, .settle_tol = 0.08, .gripper_cmd = 0.0 },
286 { .name =
"PICK_ABOVE", .target = &q_pick_above, .duration = 5.0, .timeout = 7.0, .settle_tol = 0.08, .gripper_cmd = 0.0 },
287 { .name =
"PICK", .target = &q_pick, .duration = 5.0, .timeout = 8.0, .settle_tol = 0.03, .gripper_cmd = 0.0 },
288 { .name =
"CLOSE", .target = &q_pick, .duration = 1.5, .timeout = 2.5, .settle_tol = -1.0, .gripper_cmd = 255.0 },
289 { .name =
"LIFT", .target = &q_lift, .duration = 3.0, .timeout = 5.0, .settle_tol = 0.08, .gripper_cmd = 255.0 },
290 { .name =
"PLACE_ABOVE", .target = &q_place_above, .duration = 3.0, .timeout = 5.0, .settle_tol = 0.08, .gripper_cmd = 255.0 },
291 { .name =
"PLACE", .target = &q_place, .duration = 5.0, .timeout = 8.0, .settle_tol = 0.03, .gripper_cmd = 255.0 },
292 { .name =
"OPEN", .target = &q_place, .duration = 1.0, .timeout = 2.0, .settle_tol = -1.0, .gripper_cmd = 0.0 },
293 { .name =
"RETREAT", .target = &q_place_above, .duration = 2.0, .timeout = 4.0, .settle_tol = 0.08, .gripper_cmd = 0.0 },
294 { .name =
"HOLD", .target = &q_place_above, .duration = headless ? 1.0 : 1e9, .timeout = headless ? 1.0 : 1e9, .settle_tol = -1.0, .gripper_cmd = 0.0 },
298 int qadr = model->jnt_qposadr[cube_jnt];
300 auto reset_cube = [&]() {
301 data->qpos[qadr] = kPickX;
302 data->qpos[qadr + 1] = kPickY;
303 data->qpos[qadr + 2] = kTableZ + kCubeHS;
304 data->qpos[qadr + 3] = 1.0;
305 data->qpos[qadr + 4] = data->qpos[qadr + 5] = data->qpos[qadr + 6] = 0.0;
317 data->ctrl[fingers_act] = 0.0;
320 KDL::JntArray q_enter(n), q_des(n);
322 double prev_sim_time = data->time;
323 bool aborted =
false;
324 bool restart =
false;
326 auto reset_scene = [&]() {
329 prev_sim_time = data->time;
337 std::cerr <<
"init_window_sim() failed\n";
345 for (
const Phase &phase : phases) {
346 if (aborted || restart)
break;
347 std::cout <<
"State: " << phase.name <<
"\n";
348 double t_enter = data->time;
349 snapshot_q(robot, n, q_enter);
352 if (data->time < prev_sim_time - 1e-6) {
356 prev_sim_time = data->time;
359 phase.duration > 0.0 ? clamp01((data->time - t_enter) / phase.duration) : 1.0;
360 lerp_q(q_enter, *phase.target, alpha, q_des);
361 impedance_ctrl(robot, q_des, n, dyn);
363 data->ctrl[fingers_act] = phase.gripper_cmd;
364 closed = phase.gripper_cmd > 0.0;
366 double t_rel = data->time - t_enter;
367 bool done_time = t_rel >= phase.duration;
368 bool done_pose = phase.settle_tol < 0.0
369 || max_abs_joint_err(robot, *phase.target, n) <= phase.settle_tol;
370 bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
371 if ((done_time && done_pose) || done_timeout)
break;
383 double cube_x = data->qpos[qadr];
384 double cube_y = data->qpos[qadr + 1];
385 double cube_z = data->qpos[qadr + 2];
386 double place_err_xy = std::hypot(cube_x - kPlaceX, cube_y - kPlaceY);
387 std::cout <<
"cube final position: [" << std::fixed << std::setprecision(3) << cube_x
388 <<
", " << cube_y <<
", " << cube_z <<
"]"
389 <<
" target=[" << kPlaceX <<
", " << kPlaceY <<
", " << kTableZ + kCubeHS
390 <<
"] xy_error=" << place_err_xy <<
" gripper=" << (closed ?
"closed" :
"open")
392 if (headless && place_err_xy > 0.08) ret = 1;