152int main(
int argc,
char *argv[])
154 bool headless =
false;
155 for (
int i = 1; i < argc; ++i)
156 if (std::string(argv[i]) ==
"--headless") headless =
true;
158 const fs::path root = repo_root();
159 if (!fs::exists(root /
"third_party/menagerie")) {
160 std::cerr <<
"third_party/menagerie/ not found - run:\n"
161 " cmake -B build -DFETCH_MENAGERIE=ON\n";
165 const std::string arm_mjcf = (root /
"third_party/menagerie/kinova_gen3/gen3.xml").
string();
166 const std::string grp_mjcf = (root /
"third_party/menagerie/robotiq_2f85/2f85.xml").
string();
167 const std::string table_mjcf = (root /
"src/examples/assets/table.xml").
string();
173 gripper.
pos[2] = -0.061525;
174 gripper.
euler[0] = 180.0;
177 robot_spec.
path = arm_mjcf.c_str();
178 robot_spec.
pos[2] = kTableZ;
182 scene.
robots.push_back(robot_spec);
185 .mjcf_path = table_mjcf,
186 .pos = { 0.0, 0.0, kTableZ },
189 scene.
objects.push_back(table);
190 scene.
objects.push_back(make_cube(kTableZ));
192 mjModel *model =
nullptr;
193 mjData *data =
nullptr;
195 std::cerr <<
"build_scene() failed\n";
205 &robot, model, data,
"base_link",
"bracelet_link",
"", &tool
207 std::cerr <<
"init_robot_from_mjcf() failed\n";
212 const unsigned n = robot.
chain.getNrOfJoints();
213 const unsigned ns = robot.
chain.getNrOfSegments();
214 const int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR,
"g_fingers_actuator");
215 const int cube_jnt = mj_name2id(model, mjOBJ_JOINT,
"cube_joint");
216 if (fingers_act < 0 || cube_jnt < 0) {
217 std::cerr <<
"required actuator or cube joint not found\n";
223 KDL::JntArray q_home(n);
224 for (
unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
226 KDL::ChainFkSolverPos_recursive fk(robot.
chain);
227 KDL::JntArray q_min(n), q_max(n);
228 std::vector<bool> joint_limited(n,
false);
229 for (
unsigned i = 0; i < n; ++i) {
231 if (model->jnt_limited[jid]) {
232 joint_limited[i] =
true;
233 q_min(i) = model->jnt_range[2 * jid];
234 q_max(i) = model->jnt_range[2 * jid + 1];
236 q_min(i) = -2 * M_PI;
240 KDL::ChainIkSolverVel_wdls ik_vel(robot.
chain, 1e-5, 150);
241 ik_vel.setLambda(0.05);
243 KDL::ChainIdSolver_RNE rnea(robot.
chain, KDL::Vector(0.0, 0.0, scene.
gravity_z));
244 KDL::JntArray q_buf(n), qdot_buf(n), qddot_des(n), torques(n);
245 KDL::Wrenches f_ext(ns, KDL::Wrench::Zero());
247 const KDL::Rotation kGraspRot = robot.
tip_T_tcp.M;
249 const double z_grasp = kCubeHS;
250 const double z_above = z_grasp + 0.20;
251 const double z_lift = z_grasp + 0.30;
253 KDL::JntArray q_pick_above(n), q_pick(n), q_lift(n), q_place_above(n), q_place(n);
260 const KDL::JntArray *seed;
262 Waypoint waypoints[] = {
263 { kPickX, kPickY, kTableZ + z_above, &q_pick_above, &q_home },
264 { kPickX, kPickY, kTableZ + z_grasp, &q_pick, &q_pick_above },
265 { kPickX, kPickY, kTableZ + z_lift, &q_lift, &q_pick },
266 { kPlaceX, kPlaceY, kTableZ + z_above, &q_place_above, &q_lift },
267 { kPlaceX, kPlaceY, kTableZ + z_grasp, &q_place, &q_place_above },
269 KDL::Frame world_T_base(KDL::Rotation::Identity(), KDL::Vector(0.0, 0.0, kTableZ));
270 KDL::Frame base_T_world = world_T_base.Inverse();
271 for (
const auto &wp : waypoints) {
272 KDL::Frame world_target(kGraspRot, KDL::Vector(wp.world_x, wp.world_y, wp.world_z));
273 KDL::Frame base_target = base_T_world * world_target;
274 if (!solve_near_seed(
275 ik_vel, fk, *wp.seed, base_target, joint_limited, q_min, q_max, *wp.out
277 std::cerr <<
"IK failed for waypoint at world [" << wp.world_x <<
", " << wp.world_y
278 <<
", " << wp.world_z <<
"]\n";
284 fk.JntToCart(*wp.out, fk_out);
285 double pos_err = (base_target.p - fk_out.p).Norm();
286 if (pos_err > kIkTol) {
287 std::cerr <<
"IK pose error " << pos_err <<
" exceeds tolerance at world ["
288 << wp.world_x <<
", " << wp.world_y <<
", " << wp.world_z <<
"]\n";
295 const std::vector<Phase> phases = {
296 {
"HOME", &q_home, 1.0, 2.5, 0.08, 0.0 },
297 {
"PICK_ABOVE", &q_pick_above, 5.0, 7.0, 0.08, 0.0 },
298 {
"PICK", &q_pick, 5.0, 8.0, 0.03, 0.0 },
299 {
"CLOSE", &q_pick, 1.5, 2.5, -1.0, 255.0 },
300 {
"LIFT", &q_lift, 3.0, 5.0, 0.08, 255.0 },
301 {
"PLACE_ABOVE", &q_place_above, 3.0, 5.0, 0.08, 255.0 },
302 {
"PLACE", &q_place, 5.0, 8.0, 0.03, 255.0 },
303 {
"OPEN", &q_place, 1.0, 2.0, -1.0, 0.0 },
304 {
"RETREAT", &q_place_above, 2.0, 4.0, 0.08, 0.0 },
305 {
"HOLD", &q_place_above, headless ? 1.0 : 1e9, headless ? 1.0 : 1e9, -1.0, 0.0 },
309 int qadr = model->jnt_qposadr[cube_jnt];
311 auto reset_cube = [&]() {
312 data->qpos[qadr] = kPickX;
313 data->qpos[qadr + 1] = kPickY;
314 data->qpos[qadr + 2] = kTableZ + kCubeHS;
315 data->qpos[qadr + 3] = 1.0;
316 data->qpos[qadr + 4] = data->qpos[qadr + 5] = data->qpos[qadr + 6] = 0.0;
328 data->ctrl[fingers_act] = 0.0;
331 KDL::JntArray q_enter(n), q_des(n);
332 double prev_sim_time = data->time;
333 bool aborted =
false;
334 bool restart =
false;
336 auto reset_scene = [&]() {
338 prev_sim_time = data->time;
346 std::cerr <<
"init_window_sim() failed\n";
354 for (
const Phase &phase : phases) {
355 if (aborted || restart)
break;
356 std::cout <<
"State: " << phase.name <<
"\n";
357 double t_enter = data->time;
358 snapshot_q(robot, n, q_enter);
361 if (data->time < prev_sim_time - 1e-6) {
365 prev_sim_time = data->time;
368 phase.duration > 0.0 ? clamp01((data->time - t_enter) / phase.duration) : 1.0;
369 lerp_q(q_enter, *phase.target, alpha, q_des);
370 rnea_ctrl(robot, q_des, n, rnea, q_buf, qdot_buf, qddot_des, torques, f_ext);
372 data->ctrl[fingers_act] = phase.gripper_cmd;
374 double t_rel = data->time - t_enter;
375 bool done_time = t_rel >= phase.duration;
376 bool done_pose = phase.settle_tol < 0.0
377 || max_abs_joint_err(robot, *phase.target, n) <= phase.settle_tol;
378 bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
379 if ((done_time && done_pose) || done_timeout)
break;
391 double cube_x = data->qpos[qadr];
392 double cube_y = data->qpos[qadr + 1];
393 double cube_z = data->qpos[qadr + 2];
394 double place_err_xy = std::hypot(cube_x - kPlaceX, cube_y - kPlaceY);
395 std::cout <<
"cube final position: [" << std::fixed << std::setprecision(3) << cube_x
396 <<
", " << cube_y <<
", " << cube_z <<
"]"
397 <<
" target=[" << kPlaceX <<
", " << kPlaceY <<
", " << kTableZ + kCubeHS
398 <<
"] xy_error=" << place_err_xy <<
"\n";
399 if (headless && place_err_xy > 0.08) ret = 1;