Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_achd_table_slide.cpp
Go to the documentation of this file.
2#include "example_paths.hpp"
3
4#include <kdl/chainhdsolver_vereshchagin_fixed_joint.hpp>
5#include <kdl/chainfksolverpos_recursive.hpp>
6#include <kdl/chainidsolver_recursive_newton_euler.hpp>
7#include <kdl/kinfam_io.hpp>
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12#include <iomanip>
13#include <iostream>
14#include <string>
15#include <vector>
16
17static constexpr double kTableZ = 0.447;
18static constexpr double kMoveX = 0.20;
19static constexpr double kVMaxLin = 0.08;
20static constexpr double kTauMax = 59.0;
21static constexpr double kKpLin = 200.0;
22static constexpr double kKdLin = 30.0;
23static constexpr double kKpRot = 175.0;
24static constexpr double kKdRot = 28.0;
25static constexpr double kBetaMax = 120.0;
26
27static constexpr double kTablePose[7] = {
28 -0.00258, 1.43, 3.14, -1.70, -0.018, 1.74, 1.57
29};
30
31static double clamp_abs(double v, double limit) { return std::max(-limit, std::min(limit, v)); }
32
33static void print_array(const char *label, const KDL::JntArray &x)
34{
35 std::cout << label << "=" << x << "\n";
36}
37
38static void fill_q_state(const mj_kdl::Robot &robot, KDL::JntArray &q, KDL::JntArray &qd)
39{
40 for (unsigned i = 0; i < q.rows(); ++i) {
41 q(i) = robot.jnt_pos_msr[i];
42 qd(i) = robot.jnt_vel_msr[i];
43 }
44}
45
46static void set_alpha_no_linear_z(KDL::Jacobian &alpha)
47{
48 alpha.setColumn(0, KDL::Twist(KDL::Vector(1, 0, 0), KDL::Vector(0, 0, 0)));
49 alpha.setColumn(1, KDL::Twist(KDL::Vector(0, 1, 0), KDL::Vector(0, 0, 0)));
50 alpha.setColumn(2, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(1, 0, 0)));
51 alpha.setColumn(3, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(0, 1, 0)));
52 alpha.setColumn(4, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(0, 0, 1)));
53}
54
55static void print_contact_heights(mjModel *model, mjData *data)
56{
57 for (const char *name : { "spherical_wrist_2_link", "bracelet_link" }) {
58 KDL::Frame frame;
59 if (mj_kdl::get_body_frame(model, data, name, &frame)) {
60 std::cout << name << "_z_above_table=" << std::fixed << std::setprecision(4)
61 << frame.p.z() - kTableZ << "\n";
62 }
63 }
64 KDL::Frame tcp;
65 if (mj_kdl::get_site_frame(model, data, "g_pinch", &tcp)) {
66 std::cout << "tcp_z_above_table=" << std::fixed << std::setprecision(4)
67 << tcp.p.z() - kTableZ << "\n";
68 }
69}
70
71static bool control_step(
72 mj_kdl::Robot &robot,
73 KDL::ChainFkSolverPos_recursive &fk_pos,
74 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint &achd,
75 KDL::ChainIdSolver_RNE &rnea,
76 const KDL::Frame &target,
77 KDL::JntArray &q,
78 KDL::JntArray &qd,
79 KDL::JntArray &qdd,
80 KDL::Jacobian &alpha,
81 KDL::JntArray &beta,
82 KDL::Wrenches &f_ext_achd,
83 KDL::Wrenches &f_ext_rnea_zero,
84 KDL::JntArray &ff_tau,
85 KDL::JntArray &constraint_tau,
86 KDL::JntArray &tau_cmd,
87 std::array<double, 5> &err_prev,
88 bool &first_pid,
89 bool print_debug
90)
91{
92 mj_kdl::update(&robot);
93 fill_q_state(robot, q, qd);
94
95 KDL::Frame current;
96 fk_pos.JntToCart(q, current);
97 const KDL::Twist err = KDL::diff(current, target);
98 const double dt = robot.model ? robot.model->opt.timestep : 0.002;
99
100 const double e[5] = { err.vel.x(), err.vel.y(), err.rot.x(), err.rot.y(), err.rot.z() };
101 if (first_pid) {
102 for (unsigned i = 0; i < 5; ++i) err_prev[i] = e[i];
103 first_pid = false;
104 }
105
106 const double de[5] = {
107 (e[0] - err_prev[0]) / dt,
108 (e[1] - err_prev[1]) / dt,
109 (e[2] - err_prev[2]) / dt,
110 (e[3] - err_prev[3]) / dt,
111 (e[4] - err_prev[4]) / dt,
112 };
113
114 beta(0) = clamp_abs(kKpLin * e[0] + kKdLin * de[0], kBetaMax);
115 beta(1) = clamp_abs(kKpLin * e[1] + kKdLin * de[1], kBetaMax);
116 beta(2) = clamp_abs(kKpRot * e[2] + kKdRot * de[2], kBetaMax);
117 beta(3) = clamp_abs(kKpRot * e[3] + kKdRot * de[3], kBetaMax);
118 beta(4) = clamp_abs(kKpRot * e[4] + kKdRot * de[4], kBetaMax);
119 for (unsigned i = 0; i < 5; ++i) err_prev[i] = e[i];
120
121 KDL::SetToZero(ff_tau);
122 if (achd.CartToJnt(q, qd, qdd, alpha, beta, f_ext_achd, ff_tau, constraint_tau) < 0) return false;
123 if (rnea.CartToJnt(q, qd, qdd, f_ext_rnea_zero, tau_cmd) < 0) return false;
124
125 for (unsigned i = 0; i < q.rows(); ++i) robot.jnt_trq_cmd[i] = clamp_abs(tau_cmd(i), kTauMax);
126 mj_kdl::update(&robot);
127
128 if (print_debug) {
129 print_array("beta_no_lin_z", beta);
130 print_array("achd_qdd", qdd);
131 print_array("achd_constraint_tau", constraint_tau);
132 print_array("rnea_full_tau_cmd", tau_cmd);
133 }
134 return true;
135}
136
137int main(int argc, char **argv)
138{
139 bool headless = false;
140 for (int i = 1; i < argc; ++i)
141 if (std::string(argv[i]) == "--headless") headless = true;
142 const std::string arm_mjcf = mj_kdl_examples::menagerie_model("kinova_gen3/gen3.xml");
143 const std::string grp_mjcf = mj_kdl_examples::asset("robotiq_2f85/2f85.xml");
144 const std::string table_mjcf = mj_kdl_examples::asset("table.xml");
145
147 gripper.mjcf_path = grp_mjcf.c_str();
148 gripper.attach_to = { mj_kdl::AttachKind::Site, "pinch_site" };
149 gripper.prefix = "g_";
150
151 mj_kdl::RobotSpec robot_spec;
152 robot_spec.path = arm_mjcf.c_str();
153 robot_spec.pos[2] = kTableZ;
154 robot_spec.attachments.push_back(gripper);
155
156 mj_kdl::SceneSpec scene;
157 scene.timestep = 0.002;
158 scene.add_floor = true;
159 scene.add_skybox = true;
160 scene.robots.push_back(robot_spec);
161 scene.objects.push_back(mj_kdl::SceneObject{
162 .name = "table",
163 .mjcf_path = table_mjcf,
164 .pos = { 0.0, 0.0, kTableZ },
165 .fixed = true,
166 });
167
168 mjModel *model = nullptr;
169 mjData *data = nullptr;
170 if (!mj_kdl::build_scene(&model, &data, &scene)) return 1;
171
172 const mj_kdl::ToolFrameSpec tool{ .tool_body = "g_base", .tcp_site = "g_pinch" };
173 mj_kdl::Robot robot;
174 if (!mj_kdl::init_robot_from_mjcf(&robot, model, data, "base_link", "bracelet_link", "", &tool)) {
175 mj_kdl::destroy_scene(model, data);
176 return 1;
177 }
178
179 const unsigned n = robot.chain.getNrOfJoints();
180 const unsigned ns = robot.chain.getNrOfSegments();
181 KDL::JntArray q_start(n);
182 for (unsigned i = 0; i < n; ++i) q_start(i) = kTablePose[i];
183
185
186 mj_kdl::Env env;
187 env.spec = scene;
188 env.model = model;
189 env.data = data;
190 mj_kdl::env_add_robot(&env, &robot);
191 env.on_reset = [&](mj_kdl::ResetContext *) { mj_kdl::set_joint_pos(&robot, q_start, false); };
192 mj_kdl::reset(&env);
193
194 // Let contacts settle with the table before starting the horizontal task.
195 for (int i = 0; i < 300; ++i) mj_kdl::step(&robot);
196 print_contact_heights(model, data);
197
198 KDL::ChainFkSolverPos_recursive fk_pos(robot.chain);
199 KDL::Frame target;
200 mj_kdl::update(&robot);
201 KDL::JntArray q(n), qd(n);
202 fill_q_state(robot, q, qd);
203 fk_pos.JntToCart(q, target);
204 KDL::Frame tracked = target;
205 target.p += KDL::Vector(kMoveX, 0.0, 0.0);
206
207 KDL::Twist root_acc(KDL::Vector(0.0, 0.0, -scene.gravity_z), KDL::Vector::Zero());
208 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint achd(robot.chain, root_acc, 5);
209 KDL::ChainIdSolver_RNE rnea(robot.chain, KDL::Vector(0.0, 0.0, scene.gravity_z));
210
211 KDL::JntArray qdd(n), beta(5), ff_tau(n), constraint_tau(n), tau_cmd(n);
212 KDL::Wrenches f_ext_achd(ns, KDL::Wrench::Zero());
213 KDL::Wrenches f_ext_rnea_zero(ns, KDL::Wrench::Zero());
214 KDL::Jacobian alpha(5);
215 set_alpha_no_linear_z(alpha);
216
217 // one-shot comparison: nc=6 (lin Z constrained) vs nc=5 (lin Z free)
218 {
219 mj_kdl::update(&robot);
220 fill_q_state(robot, q, qd);
221 KDL::Frame cmp_current;
222 fk_pos.JntToCart(q, cmp_current);
223 const KDL::Twist cmp_err = KDL::diff(cmp_current, target);
224
225 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint achd6(robot.chain, root_acc, 6);
226 KDL::JntArray qdd6(n), beta6(6), ff6(n), ctau6(n), tau6(n);
227 KDL::Jacobian alpha6(6);
228 alpha6.setColumn(0, KDL::Twist(KDL::Vector(1, 0, 0), KDL::Vector(0, 0, 0)));
229 alpha6.setColumn(1, KDL::Twist(KDL::Vector(0, 1, 0), KDL::Vector(0, 0, 0)));
230 alpha6.setColumn(2, KDL::Twist(KDL::Vector(0, 0, 1), KDL::Vector(0, 0, 0)));
231 alpha6.setColumn(3, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(1, 0, 0)));
232 alpha6.setColumn(4, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(0, 1, 0)));
233 alpha6.setColumn(5, KDL::Twist(KDL::Vector(0, 0, 0), KDL::Vector(0, 0, 1)));
234 beta6(0) = clamp_abs(kKpLin * cmp_err.vel.x(), kBetaMax);
235 beta6(1) = clamp_abs(kKpLin * cmp_err.vel.y(), kBetaMax);
236 beta6(2) = clamp_abs(kKpLin * cmp_err.vel.z(), kBetaMax);
237 beta6(3) = clamp_abs(kKpRot * cmp_err.rot.x(), kBetaMax);
238 beta6(4) = clamp_abs(kKpRot * cmp_err.rot.y(), kBetaMax);
239 beta6(5) = clamp_abs(kKpRot * cmp_err.rot.z(), kBetaMax);
240 KDL::SetToZero(ff6);
241 achd6.CartToJnt(q, qd, qdd6, alpha6, beta6, f_ext_achd, ff6, ctau6);
242 rnea.CartToJnt(q, qd, qdd6, f_ext_rnea_zero, tau6);
243 std::cout << "\n--- nc=6 (lin Z constrained) ---\n";
244 print_array("beta6", beta6);
245 print_array("qdd6", qdd6);
246 print_array("constraint_tau6", ctau6);
247 print_array("tau_cmd6", tau6);
248
249 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint achd5(robot.chain, root_acc, 5);
250 KDL::JntArray qdd5(n), beta5(5), ff5(n), ctau5(n), tau5(n);
251 KDL::Jacobian alpha5(5);
252 set_alpha_no_linear_z(alpha5);
253 beta5(0) = clamp_abs(kKpLin * cmp_err.vel.x(), kBetaMax);
254 beta5(1) = clamp_abs(kKpLin * cmp_err.vel.y(), kBetaMax);
255 beta5(2) = clamp_abs(kKpRot * cmp_err.rot.x(), kBetaMax);
256 beta5(3) = clamp_abs(kKpRot * cmp_err.rot.y(), kBetaMax);
257 beta5(4) = clamp_abs(kKpRot * cmp_err.rot.z(), kBetaMax);
258 KDL::SetToZero(ff5);
259 achd5.CartToJnt(q, qd, qdd5, alpha5, beta5, f_ext_achd, ff5, ctau5);
260 rnea.CartToJnt(q, qd, qdd5, f_ext_rnea_zero, tau5);
261 std::cout << "\n--- nc=5 (lin Z free) ---\n";
262 print_array("beta5", beta5);
263 print_array("qdd5", qdd5);
264 print_array("constraint_tau5", ctau5);
265 print_array("tau_cmd5", tau5);
266 std::cout << "\n";
267 }
268
269 std::array<double, 5> err_prev{};
270 bool first_pid = true;
271 int step_count = 0;
272 double prev_sim_time = data->time;
273
274 auto reset_scene = [&]() {
275 mj_kdl::reset(&env);
276 mj_kdl::update(&robot);
277 fill_q_state(robot, q, qd);
278 fk_pos.JntToCart(q, tracked);
279 err_prev = {};
280 first_pid = true;
281 step_count = 0;
282 prev_sim_time = data->time;
283 };
284
285 auto step_control = [&]() {
286 const double dt = robot.model->opt.timestep;
287 const KDL::Vector to_goal = target.p - tracked.p;
288 const double dist = to_goal.Norm();
289 if (dist > 1e-4)
290 tracked.p += (to_goal / dist) * std::min(dist, kVMaxLin * dt);
291 const bool print_debug = headless && step_count == 0;
292 bool ok = control_step(
293 robot, fk_pos, achd, rnea, tracked, q, qd, qdd, alpha, beta, f_ext_achd,
294 f_ext_rnea_zero, ff_tau, constraint_tau, tau_cmd, err_prev, first_pid,
295 print_debug
296 );
297 ++step_count;
298 return ok;
299 };
300
301 if (headless) {
302 for (int i = 0; i < 2000; ++i) {
303 if (!step_control() || !mj_kdl::step(&robot)) break;
304 }
305 mj_kdl::update(&robot);
306 fill_q_state(robot, q, qd);
307 KDL::Frame current;
308 fk_pos.JntToCart(q, current);
309 KDL::Twist err = KDL::diff(current, target);
310 print_contact_heights(model, data);
311 print_array("final_achd_constraint_tau", constraint_tau);
312 print_array("final_rnea_full_tau_cmd", tau_cmd);
313 std::cout << "tcp_xy_err_mm=" << std::fixed << std::setprecision(3)
314 << std::sqrt(err.vel.x() * err.vel.x() + err.vel.y() * err.vel.y()) * 1000.0
315 << " tcp_z_error_unconstrained_mm=" << err.vel.z() * 1000.0
316 << " tcp_rot_err_rad=" << err.rot.Norm() << "\n";
317 } else {
318 mj_kdl::Viewer viewer;
319 if (!mj_kdl::init_window_sim(&viewer, &robot)) return 1;
320 while (true) {
321 if (data->time < prev_sim_time - 1e-6)
322 reset_scene();
323 prev_sim_time = data->time;
324 if (!step_control() || !mj_kdl::step(&robot)) break;
325 }
326 mj_kdl::cleanup(&viewer);
327 }
328
329 mj_kdl::cleanup(&robot);
330 mj_kdl::destroy_scene(model, data);
331 return 0;
332}
int main(int argc, char **argv)
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
bool step(Robot *s)
void cleanup(Robot *r)
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)
void update(Robot *r)
bool get_site_frame(const mjModel *model, mjData *data, const char *site_name, KDL::Frame *out)
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)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
std::string asset(const fs::path &relative)
std::string menagerie_model(const fs::path &relative)
ResetHook on_reset
std::vector< AttachmentSpec > attachments
std::vector< double > jnt_pos_msr
std::vector< double > jnt_vel_msr
std::vector< double > jnt_trq_cmd
std::vector< RobotSpec > robots
std::vector< SceneObject > objects