Mujoco KDL Wrapper  0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_table_pick_place.cpp
Go to the documentation of this file.
1/* ex_table_pick_place.cpp
2 * Kinova GEN3 + Robotiq 2F-85 on a table picks a cube and places it 0.24 m to the side, with
3 * joint impedance (PD + KDL gravity) in TORQUE mode tracking IK waypoints. While the arm carries
4 * the cube (PLACE_ABOVE), a scripted 20 N push across the motion on bracelet_link makes the arm
5 * give way and spring back, as the joint stiffness Kp predicts.
6 *
7 * Usage:
8 * ex_table_pick_place [--headless]
9 *
10 * --headless skips the viewer and exits 1 unless the cube rests on the table within kMaxPlaceErr
11 * of the place spot, the push deflected the TCP by at least kMinDeflection and within
12 * kDeflectionRatio of J K^-1 J^T F, the arm sprang back, the cube stayed in the gripper and the
13 * elbow stayed above kMinElbowHeight. */
14
15#include "common.hpp"
16#include "example_paths.hpp"
17
18#include <kdl/chainjnttojacsolver.hpp>
19
20#include <algorithm>
21#include <cmath>
22#include <iomanip>
23#include <iostream>
24#include <string>
25
26namespace ex = mj_kdl_examples;
27
28static constexpr double kMaxPlaceErr = 0.005; // [m] in the table plane
29
30static constexpr const char *kPushBody = "bracelet_link";
31static constexpr const char *kPushPhase = "PLACE_ABOVE";
32static constexpr double kPushForce = 20.0; // [N] along world +x, across the carry along +y
33static constexpr double kPushOn = 0.8; // [s] into the phase
34static constexpr double kPushOff = 2.0; // [s] into the phase
35static constexpr double kPushRamp = 0.2; // [s] rise and fall
36static constexpr double kSettle = 0.5; // [s] after the push, when the residual is taken
37
38static constexpr double kMinDeflection = 0.010; // [m]
39static constexpr double kDeflectionRatio[] = { 0.85, 1.15 }; // measured / predicted
40static constexpr double kMaxResidual = 0.004; // [m]
41static constexpr double kMaxCubeSlip = 0.010; // [m] cube centre from the TCP while pushed
42static constexpr double kMinElbowHeight = 0.45; // [m] forearm_link origin above the table
43
44struct Metrics
45{
46 KDL::Vector lag0; // TCP minus its reference just before the push
47 double peak = 0.0; // [m] along the push
48 double predicted = 0.0; // [m] J K^-1 J^T F at the peak
49 double residual = -1.0; // [m] along the push, kSettle after it; < 0 until taken
50 double cube_slip = 0.0; // [m]
51 double elbow_min = INFINITY; // [m] world z
52};
53
54static double push_scale(double t_rel)
55{
56 return std::min(
57 ex::clamp01((t_rel - kPushOn) / kPushRamp), ex::clamp01((kPushOff - t_rel) / kPushRamp)
58 );
59}
60
61int main(int argc, char *argv[])
62{
63 const bool headless = ex::parse_args(argc, argv).headless;
64
65 mj_kdl::RobotSpec robot_spec;
66 robot_spec.path = ex::asset("kinova_gen3/gen3.xml");
67 robot_spec.pos[2] = ex::kTableZ;
68 robot_spec.attachments.push_back(ex::gripper_attachment(ex::asset("robotiq_2f85/2f85.xml")));
69
71 scene.robots.push_back(robot_spec);
72 scene.objects.push_back(ex::table_object(ex::asset("table.xml"), ex::kTableZ));
74
75 mj_kdl::Env env;
76 mj_kdl::Robot robot;
77 if (!mj_kdl::init_env(&env, &scene)) return 1;
79 if (!mj_kdl::init_robot_from_mjcf(&robot, &env, "base_link", "bracelet_link", "", &tool))
80 return 1;
83 mj_kdl::bind_scene_actuator(&env.scene, "g_fingers_actuator");
86 if (!fingers || !cube || !push) return 1;
87
89 if (!ex::solve_pick_place(robot, wp)) return 1;
90 const unsigned n = robot.chain.getNrOfJoints();
91 KDL::ChainDynParam dyn(robot.chain, KDL::Vector(0.0, 0.0, scene.gravity_z));
93 KDL::ChainJntToJacSolver jac_solver(robot.chain);
94 KDL::Jacobian jac(n), jac_push(n);
95 KDL::JntArray q(n), q_ref(n);
96 const KDL::Vector push_dir(1.0, 0.0, 0.0);
97 const KDL::Vector base(0.0, 0.0, ex::kTableZ);
98 Metrics m;
99
100 bool restart = false;
101 env.on_reset = [&](mj_kdl::ResetContext *ctx) {
102 mj_kdl::set_joint_pos(&robot, wp.home);
103 ex::place_cube(env);
104 ctx->data->ctrl[fingers->ctrl_id] = 0.0;
105 ex::prime_gravity(robot, dyn, wp.home);
106 m = {};
107 restart = true;
108 };
109 mj_kdl::reset(&env);
110 if (!headless && !mj_kdl::open_viewer(&env)) return 1;
111
112 const auto control = [&](std::size_t, const KDL::JntArray &q_des) {
113 q_ref = q_des;
114 ex::pd_gravity(robot, dyn, q_des, ex::kKp, ex::kKd);
115 };
116 // Measured against the reference of the same cycle; the push lands at the next update().
117 const auto after_step = [&](const ex::Phase &phase, double t_rel) {
118 KDL::Frame elbow;
119 mj_kdl::get_body_frame(&env, "forearm_link", &elbow);
120 m.elbow_min = std::min(m.elbow_min, elbow.p.z());
121 const bool pushing = std::string(phase.name) == kPushPhase;
122 const KDL::Vector force = push_dir * (pushing ? kPushForce * push_scale(t_rel) : 0.0);
123 push->wrench = KDL::Wrench(force, KDL::Vector::Zero());
124 if (!pushing) return;
125
126 KDL::Frame tcp, ref;
127 ex::read_q(robot, q);
128 fk.JntToCart(q, tcp);
129 fk.JntToCart(q_ref, ref);
130 m.cube_slip = std::max(m.cube_slip, (cube->pose.p - base - tcp.p).Norm());
131 const KDL::Vector lag = tcp.p - ref.p;
132 if (t_rel < kPushOn) m.lag0 = lag;
133 const double deflection = KDL::dot(lag - m.lag0, push_dir);
134 if (t_rel >= kPushOff + kSettle && m.residual < 0.0) m.residual = std::abs(deflection);
135 // Only at full force: on the ramps the arm lags the changing force.
136 const bool full = t_rel >= kPushOn + kPushRamp && t_rel <= kPushOff - kPushRamp;
137 if (!full || deflection <= m.peak) return;
138
139 // dq = K^-1 J_push^T F at the pushed body's centre of mass, seen at the TCP through J.
140 jac_solver.JntToJac(q_ref, jac);
141 const double *com = env.data->xipos + 3 * push->body_id;
142 jac_push = jac;
143 jac_push.changeRefPoint(KDL::Vector(com[0], com[1], com[2]) - base - ref.p);
145 for (unsigned i = 0; i < n; ++i)
146 dx += jac.getColumn(i).vel * (KDL::dot(jac_push.getColumn(i).vel, force) / ex::kKp[i]);
147 m.peak = deflection;
148 m.predicted = KDL::dot(dx, push_dir);
149 };
150 const bool completed = ex::run_phases(
151 env, { { &robot, fingers } }, ex::pick_place_phases(wp), restart, control, after_step
152 );
153 mj_kdl::update(&env);
154
155 const KDL::Vector c = cube->pose.p;
156 const double place_xy = std::hypot(c.x() - ex::kPlaceXY[0], c.y() - ex::kPlaceXY[1]);
157 const bool on_table = std::abs(c.z() - (ex::kTableZ + ex::kCubeHS)) < 0.002;
158 const double ratio = m.predicted > 0.0 ? m.peak / m.predicted : 0.0;
159 const double elbow_h = m.elbow_min - ex::kTableZ;
160 std::cout << std::fixed << std::setprecision(4) << "cube final position: [" << c.x() << ", "
161 << c.y() << ", " << c.z() << "] place error " << place_xy * 1000.0 << " mm (limit "
162 << kMaxPlaceErr * 1000.0 << " mm)" << (on_table ? "" : ", not on the table") << "\n"
163 << "push deflection: " << m.peak * 1000.0 << " mm (at least "
164 << kMinDeflection * 1000.0 << " mm), stiffness predicts " << m.predicted * 1000.0
165 << " mm, ratio " << ratio << " (limits " << kDeflectionRatio[0] << ".."
166 << kDeflectionRatio[1] << ")\n"
167 << "deflection " << kSettle << " s after the push: " << m.residual * 1000.0
168 << " mm (limit " << kMaxResidual * 1000.0 << " mm)\n"
169 << "cube from the TCP while pushed: " << m.cube_slip * 1000.0 << " mm (limit "
170 << kMaxCubeSlip * 1000.0 << " mm)\n"
171 << "lowest elbow above the table: " << elbow_h * 1000.0 << " mm (limit "
172 << kMinElbowHeight * 1000.0 << " mm)\n";
173 mj_kdl::cleanup(&env);
174 const bool ok = completed && on_table && place_xy <= kMaxPlaceErr && m.peak >= kMinDeflection
175 && ratio >= kDeflectionRatio[0] && ratio <= kDeflectionRatio[1]
176 && m.residual >= 0.0 && m.residual <= kMaxResidual
177 && m.cube_slip <= kMaxCubeSlip && elbow_h >= kMinElbowHeight;
178 return headless ? ex::verdict(ok, "the cube was placed through the push") : 0;
179}
virtual int JntToCart(const JntArray &q_in, Frame &p_out, int segmentNr=-1)
virtual int JntToJac(const JntArray &q_in, Jacobian &jac, int seg_nr=-1)
unsigned int getNrOfJoints() const
void changeRefPoint(const Vector &base_AB)
Twist getColumn(unsigned int i) const
Vector vel
double z() const
double x() const
static Vector Zero()
double y() const
int main(int argc, char *argv[])
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
Status init_env(Env *env, const SceneSpec *spec)
void update(Env *env)
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)
void cleanup(Robot *r)
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)
SceneFreeBodySlot * bind_scene_free_body(SceneState *s, const char *body_name)
Status open_viewer(Env *env, const char *title="MuJoCo")
doubleAcc dot(const VectorAcc &lhs, const VectorAcc &rhs)
std::string asset(const fs::path &relative)
bool solve_pick_place(const mj_kdl::Robot &robot, PickPlaceWaypoints &w)
Definition common.hpp:248
double clamp01(double v)
Definition common.hpp:64
int verdict(bool ok, const std::string &goal)
Definition common.hpp:58
constexpr double kKd[7]
Definition common.hpp:26
mj_kdl::SceneSpec scene_spec()
Definition common.hpp:138
mj_kdl::ToolFrameSpec gripper_tool(const std::string &prefix="")
Definition common.hpp:159
constexpr double kCubeHS
Definition common.hpp:29
std::vector< Phase > pick_place_phases(const PickPlaceWaypoints &w)
Definition common.hpp:292
constexpr double kTableZ
Definition common.hpp:28
constexpr double kKp[7]
Definition common.hpp:25
constexpr double kPickXY[2]
Definition common.hpp:30
mj_kdl::AttachmentSpec gripper_attachment(const std::string &path, const std::string &site="pinch_site")
Definition common.hpp:149
constexpr double kPlaceXY[2]
Definition common.hpp:31
void read_q(const mj_kdl::Robot &robot, KDL::JntArray &q)
Definition common.hpp:80
void pd_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q_des, const double *kp=nullptr, const double *kd=nullptr)
Definition common.hpp:112
void place_cube(mj_kdl::Env &env)
Definition common.hpp:197
mj_kdl::SceneObject table_object(const std::string &path, double surface_z)
Definition common.hpp:168
void prime_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q)
Definition common.hpp:131
bool run_phases(mj_kdl::Env &env, const std::vector< PhaseArm > &arms, const std::vector< Phase > &phases, bool &restart, const std::function< void(std::size_t, const KDL::JntArray &)> &control, const std::function< void(const Phase &, double t_rel)> &after_step={})
Definition common.hpp:319
Args parse_args(int argc, char **argv, const std::string &default_record_path="")
Definition common.hpp:41
mj_kdl::SceneObject cube_object(double x, double y, double surface_z)
Definition common.hpp:178
KDL::Vector lag0
ResetHook on_reset
SceneState scene
std::vector< AttachmentSpec > attachments
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
const char * name
Definition common.hpp:284