6#include <kdl/chainfksolverpos_recursive.hpp>
7#include <kdl/chainhdsolver_vereshchagin_fixed_joint.hpp>
8#include <kdl/chainidsolver_recursive_newton_euler.hpp>
9#include <kdl/chainiksolvervel_wdls.hpp>
10#include <kdl/chainjnttojacsolver.hpp>
24static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
25static constexpr double kTableZ = 0.70;
26static constexpr double kMAdm = 8.0;
27static constexpr double kDAdm = 80.0;
28static constexpr double kKAdm = 0.0;
29static constexpr double kForceDeadband = 2.5;
30static constexpr double kMaxOffset = 0.20;
31static constexpr double kMaxVel = 0.25;
32static constexpr double kTeachTime = 16.0;
33static constexpr double kTeachRadius = 0.04;
34static constexpr double kTeachRise = 0.10;
35static constexpr double kTeachTurns = 5.0;
36static constexpr int kSettleSteps = 300;
37static constexpr double kHandoffTareTime = 1.0;
38static constexpr const char *kToolBody =
"g_base";
39static constexpr const char *kGripperActuator =
"g_fingers_actuator";
40static constexpr double kSelfcheckPush[3] = { 8.0, 12.0, 6.0 };
42inline double clamp(
double v,
double lo,
double hi) {
return std::max(lo, std::min(hi, v)); }
44inline KDL::JntArray
home_q(
unsigned n)
47 for (
unsigned i = 0; i < n; ++i) q(i) = kHomePose[i];
51inline KDL::Vector
vclamp(
const KDL::Vector &v,
double limit)
54 clamp(v.x(), -limit, limit),
55 clamp(v.y(), -limit, limit),
56 clamp(v.z(), -limit, limit)
60inline double norm3(
const KDL::Vector &v) {
return std::sqrt(v.x() * v.x() + v.y() * v.y() + v.z() * v.z()); }
64 return KDL::Rotation(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
70 const double *p = data->site_xpos + 3 * id;
71 const double *R = data->site_xmat + 9 * id;
86 KDL::Vector
bias = KDL::Vector::Zero();
87 KDL::Vector
offset = KDL::Vector::Zero();
88 KDL::Vector
vel = KDL::Vector::Zero();
93 if (t < 0.0 || t > kTeachTime)
return KDL::Vector::Zero();
94 const double theta = 2.0 * M_PI * kTeachTurns * t / kTeachTime;
95 const double theta_dot = 2.0 * M_PI * kTeachTurns / kTeachTime;
97 kDAdm * (-kTeachRadius * theta_dot * std::sin(theta)),
98 kDAdm * ( kTeachRadius * theta_dot * std::cos(theta)),
99 kDAdm * ( kTeachRise / kTeachTime)
105 if (
norm3(force) == 0.0) {
106 s.
vel = KDL::Vector::Zero();
110 (force.x() - kDAdm * s.
vel.x() - kKAdm * s.
offset.x()) / kMAdm,
111 (force.y() - kDAdm * s.
vel.y() - kKAdm * s.
offset.y()) / kMAdm,
112 (force.z() - kDAdm * s.
vel.z() - kKAdm * s.
offset.z()) / kMAdm
118inline void set_body_wrench(mjModel *model, mjData *data,
const char *body,
const KDL::Vector &force)
120 const int id = mj_name2id(model, mjOBJ_BODY, body);
122 data->xfrc_applied[6 *
id + 0] = force.x();
123 data->xfrc_applied[6 *
id + 1] = force.y();
124 data->xfrc_applied[6 *
id + 2] = force.z();
125 data->xfrc_applied[6 *
id + 3] = 0.0;
126 data->xfrc_applied[6 *
id + 4] = 0.0;
127 data->xfrc_applied[6 *
id + 5] = 0.0;
132 const int id = mj_name2id(model, mjOBJ_ACTUATOR, kGripperActuator);
133 if (
id >= 0) data->ctrl[id] = 255.0;
139 if (!ft || ft->
frame_site_id < 0)
return KDL::Vector::Zero();
146 if (!ft || ft->
frame_site_id < 0)
return KDL::Vector::Zero();
148 const KDL::Vector f_ext = s.
bias - f_world;
149 return norm3(f_ext) < kForceDeadband ? KDL::Vector::Zero() : f_ext;
178 table.
name =
"table";
182 table.
pos[2] = kTableZ;
190 gripper_spec.
mjcf_path = gripper.c_str();
192 gripper_spec.
prefix =
"g_";
195 robot_spec.
path = arm.c_str();
210 ft_sensor.
name =
"wrist_ft";
231 virtual const char *
name()
const = 0;
234 virtual void track(
const KDL::Frame &target) = 0;
241 const KDL::Frame &nominal,
242 const KDL::Vector &force,
248 KDL::Frame target(nominal.M, nominal.p + state.
offset);
256 KDL::ChainFkSolverPos_recursive fk(h.
robot.
chain);
258 for (
int i = 0; i < kSettleSteps; ++i) {
282 KDL::ChainFkSolverPos_recursive fk(h.
robot.
chain);
285 const double t0 = h.
data->time;
286 while (h.
data->time - t0 < kTeachTime) {
287 const double t = h.
data->time - t0;
297 const double th = h.
data->time;
298 while (h.
data->time - th < kHandoffTareTime) {
309 for (
int i = 0; i < 100; ++i) {
318 const double ts = h.
data->time;
319 while (h.
data->time - ts < 0.5) {
328 const KDL::Vector pre_push = state.
offset;
329 const double tp = h.
data->time;
330 KDL::Vector settled = pre_push;
331 bool have_recovery =
false;
332 while (h.
data->time - tp < 4.0) {
333 const double t = h.
data->time - tp;
336 t < 1.0 ? KDL::Vector(kSelfcheckPush[0], kSelfcheckPush[1], kSelfcheckPush[2]) : KDL::Vector::Zero()
342 if (!have_recovery && t >= 2.0) {
344 have_recovery =
true;
346 if (t >= 2.5) settled = state.
offset;
351 const KDL::Vector response = settled - pre_push;
360 std::cout << std::fixed << std::setprecision(4)
361 <<
"helix force response (max offset): " << m.
helix_react <<
" m\n"
364 <<
"FT handoff residual force: " << m.
handoff_force <<
" N\n"
365 <<
"FT push response (offset norm): " << m.
push_response <<
" m\n"
366 <<
"FT push response (offset dY): " << m.
push_dy <<
" m\n"
368 <<
"hold drift after push released: " << m.
hold_drift <<
" m\n";
374 std::cout <<
"OK: admittance responded to helix + FT push and held on release\n";
384 KDL::ChainFkSolverPos_recursive fk(h.
robot.
chain);
385 double start = h.
data->time;
386 double prev = h.
data->time;
387 bool handoff_tared =
false;
388 bool have_prev =
false;
389 KDL::Vector target_prev, tcp_prev;
393 if (h.
data->time < prev - 1e-6) {
397 start = h.
data->time;
398 handoff_tared =
false;
402 const double t = h.
data->time - start;
406 KDL::Vector force = KDL::Vector::Zero();
407 if (t < kTeachTime) {
409 }
else if (t >= kTeachTime + kHandoffTareTime) {
410 if (!handoff_tared) {
412 handoff_tared =
true;
418 KDL::Frame world_base;
420 KDL::Vector target_xyz = world_base * target.p;
421 KDL::Vector tcp_xyz = world_base * tcp.p;
423 if (have_prev && trace_step % 5 == 0) {
424 const float yellow[4] = { 1.0f, 0.95f, 0.0f, 1.0f };
425 const float green[4] = { 0.0f, 1.0f, 0.2f, 1.0f };
429 target_prev = target_xyz;
439inline int run(
int argc,
char **argv, std::unique_ptr<Controller> (*make_controller)(
SceneHandles &))
441 bool headless =
false;
442 for (
int i = 1; i < argc; ++i)
443 if (std::string(argv[i]) ==
"--headless") headless =
true;
447 std::cerr <<
"failed to build admittance FT scene\n";
451 std::unique_ptr<Controller> ctrl = make_controller(h);
464 KDL::ChainFkSolverPos_recursive fk(h.
robot.
chain);
467 std::cout << std::fixed << std::setprecision(3)
468 <<
"FT bias: [" << state.
bias.x() <<
", " << state.
bias.y() <<
", " << state.
bias.z() <<
"] N\n";
473 run_gui(h, *ctrl, state, nominal);
474 std::cout << std::fixed << std::setprecision(4)
475 <<
"final offset: [" << state.
offset.x() <<
", " << state.
offset.y() <<
", "
476 << state.
offset.z() <<
"] m\n";
virtual const char * name() const =0
virtual void track(const KDL::Frame &target)=0
virtual mj_kdl::CtrlMode mode() const =0
virtual ~Controller()=default
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
bool init_robot_from_mjcf(Robot *r, mjModel *model, mjData *data, 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 call_forward=true)
const ForceTorqueSensor * find_ft_sensor(const Robot *r, const char *name)
bool get_body_frame(const mjModel *model, mjData *data, const char *body_name, KDL::Frame *out)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
std::string scene_object_site_name(const SceneObject &obj, const char *site_name)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
void add_trace_segment(Viewer *v, const KDL::Vector &a, const KDL::Vector &b, const float rgba[4]=nullptr)
bool is_running(const Viewer *v)
Metrics run_selfcheck(SceneHandles &h, Controller &ctrl, AdmState &state, const KDL::Frame &nominal)
KDL::JntArray home_q(unsigned n)
KDL::Rotation mj_xmat_to_kdl_rot(const double *m)
KDL::Vector tare_force(const mj_kdl::Robot &robot)
KDL::Vector vclamp(const KDL::Vector &v, double limit)
KDL::Vector measured_force(const mj_kdl::Robot &robot, const AdmState &s)
int run(int argc, char **argv, std::unique_ptr< Controller >(*make_controller)(SceneHandles &))
void run_gui(SceneHandles &h, Controller &ctrl, AdmState &state, const KDL::Frame &nominal)
KDL::Frame admittance_step(mj_kdl::Robot &robot, Controller &ctrl, AdmState &state, const KDL::Frame &nominal, const KDL::Vector &force, double dt)
int finish_headless(const Metrics &m)
void close_gripper(mjModel *model, mjData *data)
KDL::Frame current_tcp(KDL::ChainFkSolverPos_recursive &fk, const mj_kdl::Robot &robot)
void admittance_update(AdmState &s, const KDL::Vector &force, double dt)
void settle_and_tare(SceneHandles &h, Controller &ctrl, AdmState &state)
double norm3(const KDL::Vector &v)
double clamp(double v, double lo, double hi)
KDL::Vector spiral_force(double t)
KDL::Frame site_frame_by_id(const mjModel *model, const mjData *data, int id)
bool build_scene(SceneHandles &h)
void set_body_wrench(mjModel *model, mjData *data, const char *body, const KDL::Vector &force)
std::string asset(const fs::path &relative)
std::string menagerie_model(const fs::path &relative)
void set_free_camera(Viewer *v, double distance, double azimuth, double elevation, const std::array< double, 3 > &lookat)
std::vector< AttachmentSpec > attachments
std::vector< double > jnt_pos_msr
std::vector< RobotSpec > robots
std::vector< SceneObject > objects