24#include <kdl/chainidsolver_recursive_newton_euler.hpp>
25#include <kdl/chainjnttojacsolver.hpp>
36static constexpr double kMAdm = 8.0;
37static constexpr double kDAdm = 80.0;
38static constexpr double kKAdm = 0.0;
39static constexpr double kForceDeadband = 2.5;
40static constexpr double kMaxOffset = 0.20;
41static constexpr double kMaxVel = 0.25;
44static constexpr double kTeachTime = 16.0;
45static constexpr double kTeachRadius = 0.04;
46static constexpr double kTeachRise = 0.10;
47static constexpr double kTeachTurns = 5.0;
50static constexpr int kSettleSteps = 300;
51static constexpr double kHandoffTareTime = 1.0;
52static constexpr int kHandoffSteps = 100;
53static constexpr double kSettleTime = 0.5;
54static constexpr double kPushTime = 4.0;
55static constexpr double kSelfcheckPush[3] = { 8.0, 12.0, 6.0 };
56static constexpr double kMinElbowHeight = 0.64;
59static constexpr double kKpLin = 2500.0;
60static constexpr double kKdLin = 100.0;
61static constexpr double kKpRot = 2500.0;
62static constexpr double kKdRot = 100.0;
63static constexpr double kBetaLinMax = 300.0;
64static constexpr double kBetaRotMax = 300.0;
65static constexpr double kKpNull = 100.0;
66static constexpr double kKdNull = 20.0;
68static constexpr const char *kFtSensor =
"wrist_ft";
69static constexpr const char *kToolBody =
"g_base";
70static constexpr const char *kGripperActuator =
"g_fingers_actuator";
71static constexpr const char *kElbowBody =
"forearm_link";
76 std::clamp(v.
x(), -limit, limit),
77 std::clamp(v.
y(), -limit, limit),
78 std::clamp(v.
z(), -limit, limit)
84 return std::sqrt(v.
x() * v.
x() + v.
y() * v.
y() + v.
z() * v.
z());
91 const double theta = 2.0 * M_PI * kTeachTurns * t / kTeachTime;
92 const double theta_dot = 2.0 * M_PI * kTeachTurns / kTeachTime;
94 kDAdm * (-kTeachRadius * theta_dot * std::sin(theta)),
95 kDAdm * (kTeachRadius * theta_dot * std::cos(theta)),
96 kDAdm * (kTeachRise / kTeachTime)
109 if (norm3(force) == 0.0) {
114 a.
vel = vclamp(a.
vel + acc * dt, kMaxVel);
123 return s.name == kFtSensor;
126 const double *m = robot.
data->site_xmat + 9 * ft->frame_site_id;
127 const KDL::Rotation R(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
128 return R * ft->wrench.force;
147static bool build_scene(
Scene &s)
168 ft_sensor.
name = kFtSensor;
185 rnea(
s.robot.chain,
KDL::Vector(0.0, 0.0,
s.spec.gravity_z)),
posture(q_posture),
186 q(
s.robot.n_joints),
qd(
s.robot.n_joints),
qdd(
s.robot.n_joints),
z(
s.robot.n_joints),
187 tau(
s.robot.n_joints),
jac(
s.robot.n_joints),
188 f_ext(
s.robot.chain.getNrOfSegments(),
KDL::Wrench::Zero())
195 for (
int i = 0; i <
s.robot.n_joints; ++i)
q(i) =
s.robot.jnt_pos_msr[i];
197 fk.JntToCart(
q, out);
203 for (
int i = 0; i <
s.robot.n_joints; ++i)
qd(i) =
s.robot.jnt_vel_msr[i];
208 for (
unsigned j = 0; j <
q.rows(); ++j) tcp_vel +=
jac.getColumn(j) *
qd(j);
210 const auto pd = [](
double kp,
double kd,
double e,
double v,
double lim) {
211 return std::clamp(kp * e - kd * v, -lim, lim);
215 pd(kKpLin, kKdLin, err.
vel.
x(), tcp_vel.
vel.
x(), kBetaLinMax),
216 pd(kKpLin, kKdLin, err.
vel.
y(), tcp_vel.
vel.
y(), kBetaLinMax),
217 pd(kKpLin, kKdLin, err.
vel.
z(), tcp_vel.
vel.
z(), kBetaLinMax)
220 pd(kKpRot, kKdRot, err.
rot.
x(), tcp_vel.
rot.
x(), kBetaRotMax),
221 pd(kKpRot, kKdRot, err.
rot.
y(), tcp_vel.
rot.
y(), kBetaRotMax),
222 pd(kKpRot, kKdRot, err.
rot.
z(), tcp_vel.
rot.
z(), kBetaRotMax)
228 for (
unsigned j = 0; j <
q.rows(); ++j) {
229 z(j) = kKpNull * (
posture(j) -
q(j)) - kKdNull *
qd(j);
230 jz +=
jac.getColumn(j) *
z(j);
232 if (
ik_acc.CartToJnt(
q, beta - jz,
qdd) < 0)
return;
233 for (
unsigned j = 0; j <
q.rows(); ++j)
qdd(j) +=
z(j);
236 for (
int i = 0; i <
s.robot.n_joints; ++i)
s.robot.jnt_trq_cmd[i] =
tau(i);
272 for (
int i = 0; i < kSettleSteps; ++i) {
297static double elbow_height(
Scene &s)
311 const double t0 = s.
env.
data->time;
312 while (s.
env.
data->time - t0 < kTeachTime) {
313 const double t = s.
env.
data->time - t0;
316 const KDL::Frame target = control(s, ctrl, a, nominal, spiral_force(t));
324 const double th = s.
env.
data->time;
325 while (s.
env.
data->time - th < kHandoffTareTime) {
337 for (
int i = 0; i < kHandoffSteps; ++i) {
342 control(s, ctrl, a, nominal, f);
348 const double ts = s.
env.
data->time;
349 while (s.
env.
data->time - ts < kSettleTime) {
359 const KDL::Vector push(kSelfcheckPush[0], kSelfcheckPush[1], kSelfcheckPush[2]);
362 bool have_recovery =
false;
363 bool have_settled =
false;
364 const double tp = s.
env.
data->time;
365 while (s.
env.
data->time - tp < kPushTime) {
366 const double t = s.
env.
data->time - tp;
370 const KDL::Frame target = control(s, ctrl, a, nominal, external_force(s.
robot, a));
371 if (!have_recovery && t >= 2.0) {
373 have_recovery =
true;
376 if (!have_settled && t >= 2.5) {
393static bool report(
const Metrics &m)
395 std::cout << std::fixed << std::setprecision(4)
396 <<
"helix force response (max offset): " << m.
helix_react <<
" m\n"
399 <<
"FT handoff residual force: " << m.
handoff_force <<
" N\n"
400 <<
"FT push response (offset norm): " << m.
push_response <<
" m\n"
401 <<
"FT push response (offset dY): " << m.
push_dy <<
" m\n"
403 <<
"hold drift after push released: " << m.
hold_drift <<
" m\n"
405 <<
" m (limit " << kMinElbowHeight <<
" m)\n";
418 const double run_time =
419 kTeachTime + kHandoffTareTime + kHandoffSteps * s.
spec.
timestep + kSettleTime + kPushTime;
420 double start = s.
env.
data->time;
421 bool handoff_tared =
false;
422 bool have_prev =
false;
432 handoff_tared =
false;
436 const double t = s.
env.
data->time - start;
437 if (t >= run_time)
break;
442 if (t < kTeachTime) {
443 force = spiral_force(t);
444 }
else if (t >= kTeachTime + kHandoffTareTime) {
445 if (!handoff_tared) {
447 handoff_tared =
true;
449 force = external_force(s.
robot, a);
451 const KDL::Frame target = control(s, ctrl, a, nominal, force);
458 if (have_prev && trace_step % 5 == 0) {
459 const float yellow[4] = { 1.0f, 0.95f, 0.0f, 1.0f };
460 const float green[4] = { 0.0f, 1.0f, 0.2f, 1.0f };
464 target_prev = target_xyz;
478 if (!build_scene(s)) {
479 std::cerr <<
"failed to build admittance FT scene\n";
493 settle_and_tare(s, ctrl, a);
496 std::cout << std::fixed << std::setprecision(3) <<
"FT bias: [" << a.
bias.
x() <<
", "
497 << a.
bias.
y() <<
", " << a.
bias.
z() <<
"] N\n";
501 report(run_selfcheck(s, ctrl, a, nominal)),
502 "the admittance responded "
503 "to the helix and the FT push and held on release"
506 run_gui(s, ctrl, a, nominal);
507 std::cout << std::fixed << std::setprecision(4) <<
"final offset: [" << a.
offset.
x() <<
", "
unsigned int getNrOfJoints() 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)
SceneWrenchSlot * bind_scene_wrench(SceneState *s, const char *body_name)
SceneActuatorSlot * bind_scene_actuator(SceneState *s, const char *name)
Status open_viewer(Env *env, const char *title="MuJoCo")
void pace_realtime(Env *env)
void clear_trace(Viewer *v)
void add_trace_segment(Viewer *v, const KDL::Vector &a, const KDL::Vector &b, const float rgba[4]=nullptr)
void set_free_camera(Viewer *v, double distance, double azimuth, double elevation, const std::array< double, 3 > &lookat)
bool is_running(const Viewer *v)
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)
constexpr double kGripperClosed
int verdict(bool ok, const std::string &goal)
mj_kdl::SceneSpec scene_spec()
mj_kdl::ToolFrameSpec gripper_tool(const std::string &prefix="")
mj_kdl::AttachmentSpec gripper_attachment(const std::string &path, const std::string &site="pinch_site")
KDL::JntArray home_q(unsigned n)
mj_kdl::SceneObject table_object(const std::string &path, double surface_z)
Args parse_args(int argc, char **argv, const std::string &default_record_path="")
KDL::ChainIkSolverVel_wdls ik_acc
void track(const KDL::Frame &target)
KDL::ChainFkSolverPos_recursive fk
KDL::ChainJntToJacSolver jac_solver
RneaController(Scene &scene, const KDL::JntArray &q_posture)
const KDL::JntArray posture
KDL::ChainIdSolver_RNE rnea
mj_kdl::SceneWrenchSlot * push
mj_kdl::SceneActuatorSlot * gripper
std::vector< AttachmentSpec > attachments
std::vector< ForceTorqueSensor > ft_sensors
std::vector< RobotSpec > robots
std::vector< SceneObject > objects