22#include <kdl/chainhdsolver_vereshchagin.hpp>
23#include <kdl/chainidsolver_recursive_newton_euler.hpp>
24#include <kdl/kinfam_io.hpp>
35static constexpr int kApproachSteps = 7500;
36static constexpr int kPressSteps = 250;
37static constexpr int kSlideSteps = 2000;
38static constexpr double kTableZ = 0.447;
39static constexpr double kMoveX = 0.20;
40static constexpr double kVMaxLin = 0.08;
41static constexpr double kVTouch = 0.02;
42static constexpr double kDescentGain = 0.5;
43static constexpr double kKpLin = 200.0;
44static constexpr double kKdLin = 30.0;
45static constexpr double kKpRot = 175.0;
46static constexpr double kKdRot = 28.0;
47static constexpr double kBetaMax = 120.0;
48static constexpr double kPressForce = 10.0;
49static constexpr double kContactHeld = 0.6;
50static constexpr double kMaxTouchdown = 0.05;
51static constexpr double kMinElbowHeight = 0.30;
52static constexpr double kNoTouchdown = std::numeric_limits<double>::quiet_NaN();
54static constexpr const char *kElbowBody =
"forearm_link";
55static constexpr const char *kTcpSite =
"pinch_site";
58static constexpr double kStartTcp[3] = { 0.40, 0.0, 0.12 };
67static double contact_normal_force(
const mjModel *model, mjData *data,
int geom)
70 for (
int i = 0; i < data->ncon; ++i) {
71 const mjContact &c = data->contact[i];
72 if (c.geom1 != geom && c.geom2 != geom)
continue;
74 mj_contactForce(model, data, i, f);
93 return elbow.
p.
z() - kTableZ;
96static bool touches(
const mjData *data,
int geom)
98 for (
int i = 0; i < data->ncon; ++i)
99 if (data->contact[i].geom1 == geom || data->contact[i].geom2 == geom)
return true;
104static double site_sink_speed(
const mjModel *model,
const mjData *data,
int site)
107 mj_objectVelocity(model, data, mjOBJ_SITE, site, vel, 0);
113 for (
const char *name : {
"spherical_wrist_2_link",
"bracelet_link" }) {
116 std::cout << name <<
"_z_above_table=" << std::fixed << std::setprecision(4)
117 << frame.
p.
z() - kTableZ <<
"\n";
122 std::cout <<
"tcp_z_above_table=" << std::fixed << std::setprecision(4)
123 << tcp.
p.
z() - kTableZ <<
"\n";
128static void print_nc_comparison(
144 for (
unsigned i = 0; i < 6; ++i) alpha(i, i) = 1.0;
145 const double e[6] = { err.
vel.
x(), err.
vel.
y(), err.
vel.
z(),
147 for (
unsigned i = 0; i < 6; ++i)
148 beta(i) =
ex::clamp_abs((i < 3 ? kKpLin : kKpRot) * e[i], kBetaMax);
150 set_alpha_no_linear_z(alpha);
152 for (
unsigned i = 0; i < 5; ++i)
153 beta(i) =
ex::clamp_abs((i < 2 ? kKpLin : kKpRot) * e[i], kBetaMax);
155 achd.CartToJnt(q, qd, qdd, alpha, beta, f_ext, ff, ctau);
157 std::cout <<
"\n--- nc=" << nc << (nc == 6 ?
" (lin Z constrained)" :
" (lin Z free)")
158 <<
" ---\nbeta=" << beta <<
"\nqdd=" << qdd <<
"\nconstraint_tau=" << ctau
159 <<
"\ntau_cmd=" << tau <<
"\n";
168 robot_spec.
pos[2] = kTableZ;
171 scene.
robots.push_back(robot_spec);
181 const int table_geom = mj_name2id(env.
model, mjOBJ_GEOM,
"top");
182 const int tcp_site = mj_name2id(env.
model, mjOBJ_SITE, kTcpSite);
183 if (table_geom < 0 || tcp_site < 0)
return 1;
192 const KDL::Vector start(kStartTcp[0], kStartTcp[1], kStartTcp[2]);
201 std::cerr <<
"IK failed for the start pose\n";
206 bool restarted =
false;
209 for (
unsigned i = 0; i < n; ++i) robot.
jnt_pos_cmd[i] = q_start(i);
213 print_contact_heights(env);
221 KDL::JntArray q(n), qd(n), qdd(n), beta6(6), beta5(5), ff_tau(n), ctau(n), tau(n);
226 for (
unsigned i = 0; i < 6; ++i) alpha6(i, i) = 1.0;
227 set_alpha_no_linear_z(alpha5);
235 print_nc_comparison(robot,
KDL::diff(tracked, target), q, qd, root_acc, rnea, 6);
236 print_nc_comparison(robot,
KDL::diff(tracked, target), q, qd, root_acc, rnea, 5);
242 std::array<double, 6> err_prev{};
243 Phase phase = Phase::Approach;
246 double touchdown = kNoTouchdown;
247 double elbow_min = elbow_height(env);
249 int contact_steps = 0;
250 double reaction_sum = 0.0;
251 int reaction_count = 0;
253 const auto restart_task = [&] {
257 phase = Phase::Approach;
260 touchdown = kNoTouchdown;
261 elbow_min = elbow_height(env);
268 const auto control = [&] {
269 const double dt = env.
model->opt.timestep;
270 if (phase == Phase::Approach) {
272 const double v = std::clamp(kDescentGain * tracked.
p.
z(), kVTouch, kVMaxLin);
274 }
else if (phase == Phase::Slide) {
276 const double dist = to_goal.
Norm();
277 if (dist > 1e-4) tracked.
p += (to_goal / dist) * std::min(dist, kVMaxLin * dt);
285 const double e[6] = { err.
vel.
x(), err.
vel.
y(), err.
vel.
z(),
287 if (first) std::copy(e, e + 6, err_prev.begin());
290 for (
unsigned i = 0; i < 6; ++i) {
291 const double kp = i < 3 ? kKpLin : kKpRot, kd = i < 3 ? kKdLin : kKdRot;
292 b[i] =
ex::clamp_abs(kp * e[i] + kd * (e[i] - err_prev[i]) / dt, kBetaMax);
296 if (rnea.
CartToJnt(q, zero, zero, f_zero, ff_tau) < 0)
return false;
297 if (phase == Phase::Approach) {
298 for (
unsigned i = 0; i < 6; ++i) beta6(i) = b[i];
299 if (achd_approach.
CartToJnt(q, qd, qdd, alpha6, beta6, f_zero, ff_tau, ctau) < 0)
302 const double ramp = phase == Phase::Press
303 ? std::min(1.0,
static_cast<double>(phase_steps) / kPressSteps)
307 const unsigned no_z[5] = { 0, 1, 3, 4, 5 };
308 for (
unsigned i = 0; i < 5; ++i) beta5(i) = b[no_z[i]];
309 if (achd.
CartToJnt(q, qd, qdd, alpha5, beta5, f_ext, ff_tau, ctau) < 0)
return false;
311 if (rnea.
CartToJnt(q, qd, qdd, f_zero, tau) < 0)
return false;
313 for (
unsigned i = 0; i < n; ++i) robot.
jnt_trq_cmd[i] = tau(i);
320 bool solver_ok =
true;
321 bool finished =
false;
328 elbow_min = std::min(elbow_min, elbow_height(env));
329 if (phase == Phase::Approach && touches(env.
data, table_geom)) {
330 touchdown = site_sink_speed(env.
model, env.
data, tcp_site);
333 phase = Phase::Press;
335 }
else if (phase == Phase::Press && phase_steps >= kPressSteps) {
336 phase = Phase::Slide;
338 }
else if (phase == Phase::Slide && phase_steps > kSlideSteps / 2) {
339 const double reaction = contact_normal_force(env.
model, env.
data, table_geom);
340 if (reaction > 0.0) ++contact_steps;
341 reaction_sum += reaction;
350 if (phase == Phase::Approach && phase_steps >= kApproachSteps)
break;
351 if (phase == Phase::Slide && phase_steps >= kSlideSteps) {
362 print_contact_heights(env);
363 std::cout << std::fixed << std::setprecision(3)
364 <<
"tcp_xy_err_mm=" << std::hypot(err.
vel.
x(), err.
vel.
y()) * 1000.0
365 <<
" tcp_z_error_unconstrained_mm=" << err.
vel.
z() * 1000.0
366 <<
" tcp_rot_err_rad=" << err.
rot.
Norm() <<
"\n";
368 const double contact_fraction =
369 reaction_count > 0 ?
static_cast<double>(contact_steps) / reaction_count : 0.0;
370 const double mean_reaction = reaction_count > 0 ? reaction_sum / reaction_count : 0.0;
371 std::cout <<
"table_contact_fraction=" << contact_fraction
372 <<
" mean_table_reaction_N=" << mean_reaction <<
" commanded_press_N=" << kPressForce
373 <<
" reaction_over_command=" << mean_reaction / kPressForce <<
"\n"
374 <<
"touchdown_speed_m_s=" << std::setprecision(4) << touchdown <<
" (limit "
375 << kMaxTouchdown <<
") elbow_min_z_above_table=" << elbow_min <<
" (limit "
376 << kMinElbowHeight <<
")\n";
378 const bool ok = solver_ok && finished && contact_fraction >= kContactHeld
379 && touchdown <= kMaxTouchdown && elbow_min >= kMinElbowHeight;
381 ?
ex::verdict(ok,
"the TCP touched down gently and held the table during the slide")
virtual int JntToCart(const JntArray &q_in, Frame &p_out, int segmentNr=-1)
int CartToJnt(const JntArray &q, const JntArray &q_dot, JntArray &q_dotdot, const Jacobian &alfa, const JntArray &beta, const Wrenches &f_ext, const JntArray &ff_torques, JntArray &constraint_torques)
int setDriverWeights(const Eigen::VectorXd &w_f_ext, const Eigen::VectorXd &w_ff_torques)
int CartToJnt(const JntArray &q, const JntArray &q_dot, const JntArray &q_dotdot, const Wrenches &f_ext, JntArray &torques)
void setLambda(const double lambda)
unsigned int getNrOfJoints() const
unsigned int getNrOfSegments() const
void setColumn(unsigned int i, const Twist &t)
static Rotation RotX(double angle)
double Norm(double eps=epsilon) const
int main(int argc, char **argv)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
Status set_control_mode(Robot *r, CtrlMode mode)
Status init_robot_from_mjcf(Robot *r, Env *env, const char *base_body, const char *tip_body, const char *prefix="", const ToolFrameSpec *tool=nullptr)
void set_joint_pos(Robot *r, const KDL::JntArray &q)
bool get_body_frame(Env *env, const char *body_name, KDL::Frame *out)
bool get_site_frame(Env *env, const char *site_name, KDL::Frame *out)
Status open_viewer(Env *env, const char *title="MuJoCo")
void pace_realtime(Env *env)
IMETHOD Vector diff(const Vector &p_w_a, const Vector &p_w_b, double dt=1)
std::vector< Wrench > Wrenches
std::string asset(const fs::path &relative)
double clamp_abs(double v, double limit)
int verdict(bool ok, const std::string &goal)
mj_kdl::SceneSpec scene_spec()
KDL::JntArray home_q(unsigned n)
void read_q(const mj_kdl::Robot &robot, KDL::JntArray &q)
mj_kdl::SceneObject table_object(const std::string &path, double surface_z)
bool solve_near_seed(KDL::ChainIkSolverVel_wdls &ik_vel, KDL::ChainFkSolverPos_recursive &fk, const mj_kdl::Robot &robot, const KDL::JntArray &seed, const KDL::Frame &target, KDL::JntArray &out)
Args parse_args(int argc, char **argv, const std::string &default_record_path="")
std::vector< double > jnt_pos_cmd
std::vector< double > jnt_trq_cmd
std::vector< RobotSpec > robots
std::vector< SceneObject > objects