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