Mujoco KDL Wrapper  0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
common.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <kdl/chaindynparam.hpp>
6#include <kdl/chainfksolverpos_recursive.hpp>
7#include <kdl/chainiksolvervel_wdls.hpp>
8
9#include <algorithm>
10#include <cmath>
11#include <functional>
12#include <iostream>
13#include <string>
14#include <vector>
15
16namespace mj_kdl_examples {
17
18// The bundled 2F-85's ctrl is its driver joint angle: ctrlrange 0..0.82, joint range 0..0.8.
19constexpr double kGripperClosed = 0.82;
20
21// Kinova Gen3 home pose [rad].
22constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
23
24// Gen3 joint impedance gains [Nm/rad], [Nm s/rad].
25constexpr double kKp[7] = { 100, 200, 100, 200, 100, 200, 100 };
26constexpr double kKd[7] = { 10, 20, 10, 20, 10, 20, 10 };
27
28constexpr double kTableZ = 0.70; // tabletop height of the bundled table [m]
29constexpr double kCubeHS = 0.02; // pick cube half-size [m]
30constexpr double kPickXY[2] = { 0.40, 0.00 };
31constexpr double kPlaceXY[2] = { 0.40, 0.24 };
32
33struct Args
34{
35 bool headless = false;
36 bool record = false;
37 std::string record_path;
38};
39
40// --headless skips the viewer; --record [out.mp4] records offscreen and implies --headless.
41inline Args parse_args(int argc, char **argv, const std::string &default_record_path = "")
42{
43 Args args;
44 args.record_path = default_record_path;
45 for (int i = 1; i < argc; ++i) {
46 const std::string arg = argv[i];
47 if (arg == "--headless") {
48 args.headless = true;
49 } else if (arg == "--record") {
50 args.record = true;
51 args.headless = true;
52 if (i + 1 < argc && argv[i + 1][0] != '-') args.record_path = argv[++i];
53 }
54 }
55 return args;
56}
57
58inline int verdict(bool ok, const std::string &goal)
59{
60 std::cout << (ok ? "PASS: " : "FAIL: ") << goal << "\n";
61 return ok ? 0 : 1;
62}
63
64inline double clamp01(double v) { return std::clamp(v, 0.0, 1.0); }
65
66inline double clamp_abs(double v, double limit) { return std::clamp(v, -limit, limit); }
67
68inline KDL::JntArray home_q(unsigned n)
69{
70 KDL::JntArray q(n);
71 for (unsigned i = 0; i < n; ++i) q(i) = kHomePose[i];
72 return q;
73}
74
75inline void lerp_q(const KDL::JntArray &a, const KDL::JntArray &b, double t, KDL::JntArray &out)
76{
77 for (unsigned i = 0; i < a.rows(); ++i) out(i) = a(i) + t * (b(i) - a(i));
78}
79
80inline void read_q(const mj_kdl::Robot &robot, KDL::JntArray &q)
81{
82 for (unsigned i = 0; i < q.rows(); ++i) q(i) = robot.jnt_pos_msr[i];
83}
84
85inline void read_q(const mj_kdl::Robot &robot, KDL::JntArray &q, KDL::JntArray &qd)
86{
87 for (unsigned i = 0; i < q.rows(); ++i) {
88 q(i) = robot.jnt_pos_msr[i];
89 qd(i) = robot.jnt_vel_msr[i];
90 }
91}
92
93inline double max_abs_joint_err(const mj_kdl::Robot &robot, const KDL::JntArray &q)
94{
95 double max_err = 0.0;
96 for (unsigned i = 0; i < q.rows(); ++i)
97 max_err = std::max(max_err, std::abs(q(i) - robot.jnt_pos_msr[i]));
98 return max_err;
99}
100
101// TCP of the chain at the measured joint positions, in the robot base frame.
103{
105 read_q(robot, q);
106 KDL::Frame out;
107 fk.JntToCart(q, out);
108 return out;
109}
110
111// jnt_trq_cmd = g(q) + kp * (q_des - q) - kd * qdot; kp/kd null for gravity alone.
112inline void pd_gravity(
113 mj_kdl::Robot &robot,
115 const KDL::JntArray &q_des,
116 const double *kp = nullptr,
117 const double *kd = nullptr
118)
119{
120 const unsigned n = robot.chain.getNrOfJoints();
121 KDL::JntArray q(n), g(n);
122 read_q(robot, q);
123 dyn.JntToGravity(q, g);
124 for (unsigned i = 0; i < n; ++i) {
125 robot.jnt_trq_cmd[i] = g(i);
126 if (kp) robot.jnt_trq_cmd[i] += kp[i] * (q_des(i) - q(i)) - kd[i] * robot.jnt_vel_msr[i];
127 }
128}
129
130// Gravity at q as the first torque command, so the first step after a reset holds the arm.
132{
133 KDL::JntArray g(q.rows());
134 dyn.JntToGravity(q, g);
135 for (unsigned i = 0; i < q.rows(); ++i) robot.jnt_trq_cmd[i] = g(i);
136}
137
139{
141 spec.timestep = 0.002;
142 spec.add_floor = true;
143 spec.add_skybox = true;
144 return spec;
145}
146
147// The bundled 2F-85 on the arm's pinch site (or another site), element names prefixed "g_".
149 gripper_attachment(const std::string &path, const std::string &site = "pinch_site")
150{
152 gripper.mjcf_path = path;
153 gripper.attach_to = { mj_kdl::AttachKind::Site, site };
154 gripper.prefix = "g_";
155 return gripper;
156}
157
158// The 2F-85's mount carries mass too, so the tool starts there; the TCP is its pinch site.
159inline mj_kdl::ToolFrameSpec gripper_tool(const std::string &prefix = "")
160{
162 tool.tool_body = prefix + "g_base_mount";
163 tool.tcp_site = prefix + "g_pinch";
164 return tool;
165}
166
167// The bundled table, its top (site "table_top") at surface_z.
168inline mj_kdl::SceneObject table_object(const std::string &path, double surface_z)
169{
171 table.name = "table";
172 table.mjcf_path = path;
173 table.pos[2] = surface_z;
174 table.fixed = true;
175 return table;
176}
177
178inline mj_kdl::SceneObject cube_object(double x, double y, double surface_z)
179{
181 cube.name = "cube";
183 cube.pos[0] = x;
184 cube.pos[1] = y;
185 cube.pos[2] = surface_z + kCubeHS;
186 for (int k = 0; k < 3; ++k) cube.size[k] = kCubeHS;
187 const float rgba[4] = { 0.1f, 0.35f, 1.0f, 1.0f };
188 std::copy(rgba, rgba + 4, cube.rgba);
189 cube.mass = 0.1;
191 cube.friction[0] = 0.8;
192 cube.friction[1] = 0.02;
193 cube.friction[2] = 0.001;
194 return cube;
195}
196
197inline void place_cube(mj_kdl::Env &env)
198{
199 const double pos[3] = { kPickXY[0], kPickXY[1], kTableZ + kCubeHS };
200 mj_kdl::set_body_pose(&env, "cube", pos);
201}
202
203// Damped-least-squares IK stepped from seed, so the answer stays on the seed's branch.
204inline bool solve_near_seed(
207 const mj_kdl::Robot &robot,
208 const KDL::JntArray &seed,
209 const KDL::Frame &target,
210 KDL::JntArray &out
211)
212{
213 const auto error = [&] {
214 KDL::Frame fk_out;
215 fk.JntToCart(out, fk_out);
216 return KDL::diff(fk_out, target);
217 };
218 const auto converged = [](const KDL::Twist &dx) {
219 return dx.vel.Norm() <= 2e-3 && dx.rot.Norm() <= 2e-2;
220 };
221 out = seed;
222 KDL::JntArray dq(out.rows());
223 for (int iter = 0; iter < 300; ++iter) {
224 KDL::Twist dx = error();
225 if (converged(dx)) return true;
226
227 const double vel_norm = dx.vel.Norm();
228 if (vel_norm > 0.05) dx.vel = dx.vel * (0.05 / vel_norm);
229 const double rot_norm = dx.rot.Norm();
230 if (rot_norm > 0.20) dx.rot = dx.rot * (0.20 / rot_norm);
231
232 if (ik_vel.CartToJnt(out, dx, dq) < 0) return false;
233 for (unsigned i = 0; i < out.rows(); ++i) {
234 // joint_limits are +-inf for an unlimited joint, so the clamp leaves it alone.
235 const auto [lo, hi] = robot.joint_limits[i];
236 out(i) = std::clamp(out(i) + dq(i), lo, hi);
237 }
238 }
239 return converged(error());
240}
241
246
247// Joint waypoints of the table pick-place, for an arm standing on the table at the world origin.
249{
250 const unsigned n = robot.chain.getNrOfJoints();
252 KDL::ChainIkSolverVel_wdls ik_vel(robot.chain, 1e-5, 150);
253 ik_vel.setLambda(0.05);
254
255 w.home = home_q(n);
256 // Base-frame heights: the TCP at the cube centre, then above it.
257 const double z_grasp = kCubeHS;
258 struct Target
259 {
260 const double *xy;
261 double z;
262 KDL::JntArray *out;
263 const KDL::JntArray *seed;
264 };
265 const Target targets[] = {
266 { kPickXY, z_grasp + 0.20, &w.pick_above, &w.home },
267 { kPickXY, z_grasp, &w.pick, &w.pick_above },
268 { kPickXY, z_grasp + 0.30, &w.lift, &w.pick },
269 { kPlaceXY, z_grasp + 0.20, &w.place_above, &w.lift },
270 { kPlaceXY, z_grasp, &w.place, &w.place_above },
271 };
272 for (const Target &t : targets) {
273 const KDL::Frame target(robot.tip_T_tcp.M, KDL::Vector(t.xy[0], t.xy[1], t.z));
274 if (!solve_near_seed(ik_vel, fk, robot, *t.seed, target, *t.out)) {
275 std::cerr << "IK failed for [" << t.xy[0] << ", " << t.xy[1] << ", " << t.z << "]\n";
276 return false;
277 }
278 }
279 return true;
280}
281
282struct Phase
283{
284 const char *name;
286 double duration; // [s] to interpolate from the entry pose to target
287 double timeout; // [s] ends the phase even if not settled
288 double settle_tol; // [rad]; < 0 ends on duration alone
290};
291
292inline std::vector<Phase> pick_place_phases(const PickPlaceWaypoints &w)
293{
294 // clang-format off
295 return {
296 { "HOME", &w.home, 1.0, 2.5, 0.08, 0.0 },
297 { "PICK_ABOVE", &w.pick_above, 5.0, 7.0, 0.08, 0.0 },
298 { "PICK", &w.pick, 5.0, 8.0, 0.03, 0.0 },
299 { "CLOSE", &w.pick, 1.5, 2.5, -1.0, kGripperClosed },
300 { "LIFT", &w.lift, 3.0, 5.0, 0.08, kGripperClosed },
301 { "PLACE_ABOVE", &w.place_above, 3.0, 5.0, 0.08, kGripperClosed },
302 { "PLACE", &w.place, 5.0, 8.0, 0.03, kGripperClosed },
303 { "OPEN", &w.place, 1.0, 2.0, -1.0, 0.0 },
304 { "RETREAT", &w.place_above, 2.0, 4.0, 0.08, 0.0 },
305 { "HOLD", &w.place_above, 1.0, 1.0, -1.0, 0.0 },
306 };
307 // clang-format on
308}
309
310// An arm the phases drive; each follows the same joint waypoints in its own base frame.
316
317// One update() per step for all arms, so a command lands one cycle later; control(i, q_des)
318// commands arm i. False if the viewer was closed.
319inline bool run_phases(
320 mj_kdl::Env &env,
321 const std::vector<PhaseArm> &arms,
322 const std::vector<Phase> &phases,
323 bool &restart,
324 const std::function<void(std::size_t, const KDL::JntArray &)> &control,
325 const std::function<void(const Phase &, double t_rel)> &after_step = {}
326)
327{
328 const unsigned n = arms.front().robot->chain.getNrOfJoints();
329 std::vector<KDL::JntArray> q_enter(arms.size(), KDL::JntArray(n));
330 KDL::JntArray q_des(n);
331 do {
332 restart = false;
333 for (const Phase &phase : phases) {
334 std::cout << "State: " << phase.name << "\n";
335 const double t_enter = env.data->time;
336 for (std::size_t i = 0; i < arms.size(); ++i) read_q(*arms[i].robot, q_enter[i]);
337 while (true) {
338 mj_kdl::update(&env);
339 const double t_rel = env.data->time - t_enter;
340 const double alpha = phase.duration > 0.0 ? clamp01(t_rel / phase.duration) : 1.0;
341 double err = 0.0;
342 for (std::size_t i = 0; i < arms.size(); ++i) {
343 lerp_q(q_enter[i], *phase.target, alpha, q_des);
344 control(i, q_des);
345 arms[i].gripper->command = phase.gripper_cmd;
346 err = std::max(err, max_abs_joint_err(*arms[i].robot, *phase.target));
347 }
348
349 const bool done_pose = phase.settle_tol < 0.0 || err <= phase.settle_tol;
350 const bool done_timeout = phase.timeout > 0.0 && t_rel >= phase.timeout;
351 if ((t_rel >= phase.duration && done_pose) || done_timeout) break;
352
353 if (!mj_kdl::step(&env)) return false;
354 if (restart) break;
356 if (after_step) after_step(phase, t_rel);
357 }
358 if (restart) break;
359 }
360 } while (restart);
361 return true;
362}
363
364} // namespace mj_kdl_examples
virtual int JntToGravity(const JntArray &q, JntArray &gravity)
virtual int JntToCart(const JntArray &q_in, Frame &p_out, int segmentNr=-1)
void setLambda(const double lambda)
virtual int CartToJnt(const JntArray &q_in, const Twist &v_in, JntArray &qdot_out)
unsigned int getNrOfJoints() const
Rotation M
unsigned int rows() const
Vector rot
Vector vel
double Norm(double eps=epsilon) const
void set_body_pose(Env *env, const char *body_name, const double pos[3], const double *quat=nullptr)
void update(Env *env)
bool step(Env *env)
void pace_realtime(Env *env)
IMETHOD Vector diff(const Vector &p_w_a, const Vector &p_w_b, double dt=1)
double clamp_abs(double v, double limit)
Definition common.hpp:66
bool solve_pick_place(const mj_kdl::Robot &robot, PickPlaceWaypoints &w)
Definition common.hpp:248
constexpr double kGripperClosed
Definition common.hpp:19
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
double max_abs_joint_err(const mj_kdl::Robot &robot, const KDL::JntArray &q)
Definition common.hpp:93
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
KDL::Frame tcp_frame(KDL::ChainFkSolverPos_recursive &fk, const mj_kdl::Robot &robot)
Definition common.hpp:102
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 kHomePose[7]
Definition common.hpp:22
constexpr double kPlaceXY[2]
Definition common.hpp:31
KDL::JntArray home_q(unsigned n)
Definition common.hpp:68
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
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)
Definition common.hpp:204
void prime_gravity(mj_kdl::Robot &robot, KDL::ChainDynParam &dyn, const KDL::JntArray &q)
Definition common.hpp:131
void lerp_q(const KDL::JntArray &a, const KDL::JntArray &b, double t, KDL::JntArray &out)
Definition common.hpp:75
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
std::vector< double > jnt_vel_msr
std::vector< double > jnt_pos_msr
std::vector< double > jnt_trq_cmd
std::vector< std::pair< double, double > > joint_limits
KDL::Frame tip_T_tcp
std::string record_path
Definition common.hpp:37
mj_kdl::SceneActuatorSlot * gripper
Definition common.hpp:314
mj_kdl::Robot * robot
Definition common.hpp:313
const char * name
Definition common.hpp:284
const KDL::JntArray * target
Definition common.hpp:285