149int main(
int argc,
char *argv[])
151 bool headless =
false;
152 for (
int i = 1; i < argc; ++i)
153 if (std::string(argv[i]) ==
"--headless") headless =
true;
165 robot_spec.
path = arm_mjcf.c_str();
166 robot_spec.
pos[2] = kTableZ;
173 scene.
robots.push_back(robot_spec);
176 .mjcf_path = table_mjcf,
177 .pos = { 0.0, 0.0, kTableZ },
180 scene.
objects.push_back(table);
181 scene.
objects.push_back(make_cube(kTableZ));
183 mjModel *model =
nullptr;
184 mjData *data =
nullptr;
186 std::cerr <<
"build_scene() failed\n";
196 &robot, model, data,
"base_link",
"bracelet_link",
"", &tool
198 std::cerr <<
"init_robot_from_mjcf() failed\n";
203 const unsigned n = robot.
chain.getNrOfJoints();
204 const unsigned ns = robot.
chain.getNrOfSegments();
205 const int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR,
"g_fingers_actuator");
206 const int cube_jnt = mj_name2id(model, mjOBJ_JOINT,
"cube_joint");
207 if (fingers_act < 0 || cube_jnt < 0) {
208 std::cerr <<
"required actuator or cube joint not found\n";
214 KDL::JntArray q_home(n);
215 for (
unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
217 KDL::ChainFkSolverPos_recursive fk(robot.
chain);
218 KDL::JntArray q_min(n), q_max(n);
219 std::vector<bool> joint_limited(n,
false);
220 for (
unsigned i = 0; i < n; ++i) {
222 if (model->jnt_limited[jid]) {
223 joint_limited[i] =
true;
224 q_min(i) = model->jnt_range[2 * jid];
225 q_max(i) = model->jnt_range[2 * jid + 1];
227 q_min(i) = -2 * M_PI;
231 KDL::ChainIkSolverVel_wdls ik_vel(robot.
chain, 1e-5, 150);
232 ik_vel.setLambda(0.05);
234 KDL::ChainIdSolver_RNE rnea(robot.
chain, KDL::Vector(0.0, 0.0, scene.
gravity_z));
235 KDL::JntArray q_buf(n), qdot_buf(n), qddot_des(n), torques(n);
236 KDL::Wrenches f_ext(ns, KDL::Wrench::Zero());
238 const KDL::Rotation kGraspRot = robot.
tip_T_tcp.M;
240 const double z_grasp = kCubeHS;
241 const double z_above = z_grasp + 0.20;
242 const double z_lift = z_grasp + 0.30;
244 KDL::JntArray q_pick_above(n), q_pick(n), q_lift(n), q_place_above(n), q_place(n);
251 const KDL::JntArray *seed;
253 Waypoint waypoints[] = {
254 { kPickX, kPickY, kTableZ + z_above, &q_pick_above, &q_home },
255 { kPickX, kPickY, kTableZ + z_grasp, &q_pick, &q_pick_above },
256 { kPickX, kPickY, kTableZ + z_lift, &q_lift, &q_pick },
257 { kPlaceX, kPlaceY, kTableZ + z_above, &q_place_above, &q_lift },
258 { kPlaceX, kPlaceY, kTableZ + z_grasp, &q_place, &q_place_above },
260 KDL::Frame world_T_base(KDL::Rotation::Identity(), KDL::Vector(0.0, 0.0, kTableZ));
261 KDL::Frame base_T_world = world_T_base.Inverse();
262 for (
const auto &wp : waypoints) {
263 KDL::Frame world_target(kGraspRot, KDL::Vector(wp.world_x, wp.world_y, wp.world_z));
264 KDL::Frame base_target = base_T_world * world_target;
265 if (!solve_near_seed(
266 ik_vel, fk, *wp.seed, base_target, joint_limited, q_min, q_max, *wp.out
268 std::cerr <<
"IK failed for waypoint at world [" << wp.world_x <<
", " << wp.world_y
269 <<
", " << wp.world_z <<
"]\n";
275 fk.JntToCart(*wp.out, fk_out);
276 double pos_err = (base_target.p - fk_out.p).Norm();
277 if (pos_err > kIkTol) {
278 std::cerr <<
"IK pose error " << pos_err <<
" exceeds tolerance at world ["
279 << wp.world_x <<
", " << wp.world_y <<
", " << wp.world_z <<
"]\n";
286 const std::vector<Phase> phases = {
287 {
"HOME", &q_home, 1.0, 2.5, 0.08, 0.0 },
288 {
"PICK_ABOVE", &q_pick_above, 5.0, 7.0, 0.08, 0.0 },
289 {
"PICK", &q_pick, 5.0, 8.0, 0.03, 0.0 },
290 {
"CLOSE", &q_pick, 1.5, 2.5, -1.0, 0.8 },
291 {
"LIFT", &q_lift, 3.0, 5.0, 0.08, 0.8 },
292 {
"PLACE_ABOVE", &q_place_above, 3.0, 5.0, 0.08, 0.8 },
293 {
"PLACE", &q_place, 5.0, 8.0, 0.03, 0.8 },
294 {
"OPEN", &q_place, 1.0, 2.0, -1.0, 0.0 },
295 {
"RETREAT", &q_place_above, 2.0, 4.0, 0.08, 0.0 },
296 {
"HOLD", &q_place_above, headless ? 1.0 : 1e9, headless ? 1.0 : 1e9, -1.0, 0.0 },
300 int qadr = model->jnt_qposadr[cube_jnt];
302 auto reset_cube = [&]() {
303 data->qpos[qadr] = kPickX;
304 data->qpos[qadr + 1] = kPickY;
305 data->qpos[qadr + 2] = kTableZ + kCubeHS;
306 data->qpos[qadr + 3] = 1.0;
307 data->qpos[qadr + 4] = data->qpos[qadr + 5] = data->qpos[qadr + 6] = 0.0;
319 data->ctrl[fingers_act] = 0.0;
322 KDL::JntArray q_enter(n), q_des(n);
323 double prev_sim_time = data->time;
324 bool aborted =
false;
325 bool restart =
false;
327 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 rnea_ctrl(robot, q_des, n, rnea, q_buf, qdot_buf, qddot_des, torques, f_ext);
363 data->ctrl[fingers_act] = phase.gripper_cmd;
365 double t_rel = data->time - t_enter;
366 bool done_time = t_rel >= phase.duration;
367 bool done_pose = phase.settle_tol < 0.0
368 || max_abs_joint_err(robot, *phase.target, n) <= phase.settle_tol;
369 bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
370 if ((done_time && done_pose) || done_timeout)
break;
382 double cube_x = data->qpos[qadr];
383 double cube_y = data->qpos[qadr + 1];
384 double cube_z = data->qpos[qadr + 2];
385 double place_err_xy = std::hypot(cube_x - kPlaceX, cube_y - kPlaceY);
386 std::cout <<
"cube final position: [" << std::fixed << std::setprecision(3) << cube_x
387 <<
", " << cube_y <<
", " << cube_z <<
"]"
388 <<
" target=[" << kPlaceX <<
", " << kPlaceY <<
", " << kTableZ + kCubeHS
389 <<
"] xy_error=" << place_err_xy <<
"\n";
390 if (headless && place_err_xy > 0.08) ret = 1;