Mujoco KDL Wrapper  0.1.0
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
urdf_solver_probe.cpp
Go to the documentation of this file.
1/* urdf_solver_probe.cpp
2 * Single Kinova GEN3 URDF/MuJoCo dynamics probe:
3 * - parses the in-repo Kinova URDF with kdl_parser
4 * - runs ACHD fixed-joint solver at the home pose with 6 TCP constraints
5 * - builds the MuJoCo-derived KDL chain and compares RNEA gravity torques
6 */
7
8#include "chainhdsolver_vereshchagin_fixed_joint.hpp"
10
11#include <kdl/chainfksolverpos_recursive.hpp>
12#include <kdl/chainidsolver_recursive_newton_euler.hpp>
13#include <kdl/frames_io.hpp>
14#include <kdl/kinfam_io.hpp>
15#include <kdl/tree.hpp>
16#include <kdl_parser/kdl_parser.hpp>
17
18#include <algorithm>
19#include <cmath>
20#include <filesystem>
21#include <iomanip>
22#include <iostream>
23#include <string>
24#include <vector>
25
26namespace fs = std::filesystem;
27
28static constexpr double kHome[7] = {
29 0.0, 0.26179939, 3.14159265, -2.26892803, 0.0, 0.95993109, 1.57079633
30};
31
32static fs::path repo_root()
33{
34 return fs::path(__FILE__).parent_path().parent_path();
35}
36
37static void set_alpha_identity(KDL::Jacobian &alpha)
38{
39 for (unsigned i = 0; i < 6; ++i) {
40 alpha.setColumn(
41 i,
42 KDL::Twist(KDL::Vector(i == 0, i == 1, i == 2), KDL::Vector(i == 3, i == 4, i == 5))
43 );
44 }
45}
46
47static void print_chain(const KDL::Chain &chain, const char *label)
48{
49 std::cout << label << ": " << chain.getNrOfJoints() << " joints, "
50 << chain.getNrOfSegments() << " segments\n";
51 for (unsigned i = 0; i < chain.getNrOfSegments(); ++i) {
52 const KDL::Segment &seg = chain.getSegment(i);
53 const KDL::RigidBodyInertia &ri = seg.getInertia();
54 const KDL::RotationalInertia &I = ri.getRotationalInertia();
55 const bool fixed = seg.getJoint().getType() == KDL::Joint::None;
56 std::cout << " [" << i << "] " << seg.getName()
57 << (fixed ? " [fixed]" : " [rev]")
58 << " joint=" << seg.getJoint().getName()
59 << " m=" << std::setprecision(4) << ri.getMass()
60 << " Ixx=" << I.data[0]
61 << " Iyy=" << I.data[4]
62 << " Izz=" << I.data[8] << "\n";
63 }
64 std::cout << "\n";
65}
66
67static bool parse_urdf_chain(const fs::path &urdf, const std::string &tip, KDL::Chain &chain)
68{
69 KDL::Tree tree;
70 if (!kdl_parser::treeFromFile(urdf.string(), tree)) {
71 std::cerr << "kdl_parser: failed to parse " << urdf << "\n";
72 return false;
73 }
74 if (!tree.getChain("base_link", tip, chain)) {
75 std::cerr << "getChain base_link->" << tip << " failed\n";
76 return false;
77 }
78 return true;
79}
80
81static bool run_achd_probe(const KDL::Chain &chain)
82{
83 const unsigned n = chain.getNrOfJoints();
84 const unsigned ns = chain.getNrOfSegments();
85 if (n != 7) {
86 std::cerr << "ACHD chain has unexpected joint count: " << n << "\n";
87 return false;
88 }
89
90 KDL::JntArray q(n), qd(n), qdd(n), beta(6), ff_tau(n), constraint_tau(n);
91 for (unsigned i = 0; i < n; ++i) q(i) = kHome[i];
92 KDL::SetToZero(qd);
93 KDL::SetToZero(beta);
94 KDL::SetToZero(ff_tau);
95
96 KDL::ChainFkSolverPos_recursive fk_pos(chain);
97 KDL::Frame home_pose;
98 fk_pos.JntToCart(q, home_pose);
99
100 KDL::Jacobian alpha(6);
101 set_alpha_identity(alpha);
102 KDL::Wrenches f_ext(ns, KDL::Wrench::Zero());
103
104 KDL::Twist root_acc(KDL::Vector(0.0, 0.0, 9.81), KDL::Vector::Zero());
105 KDL::ChainHdSolver_Vereshchagin_Fixed_Joint achd(chain, root_acc, 6);
106 const int rc = achd.CartToJnt(q, qd, qdd, alpha, beta, f_ext, ff_tau, constraint_tau);
107 if (rc < 0) {
108 std::cerr << "ACHD CartToJnt failed: " << rc << "\n";
109 return false;
110 }
111
112 std::vector<KDL::Twist> xdd(ns + 1);
113 achd.getTransformedLinkAcceleration(xdd);
114
115 std::cout << "=== ACHD fixed-joint probe: URDF base_link -> EndEffector_Link ===\n";
116 print_chain(chain, "URDF ACHD");
117 std::cout << "home_pose=" << home_pose << "\n";
118 std::cout << "q=" << q << "\n";
119 std::cout << "qd=" << qd << "\n";
120 std::cout << "alpha=\n" << alpha.data << "\n";
121 std::cout << "beta=" << beta << "\n";
122 std::cout << "ff_tau=" << ff_tau << "\n";
123 std::cout << "qdd=" << qdd << "\n";
124 std::cout << "constraint_tau=" << constraint_tau << "\n";
125 std::cout << "tcp_acc_base=" << xdd.back() << "\n\n";
126 return true;
127}
128
129static bool run_rnea(
130 const KDL::Chain &chain,
131 const char *label,
132 std::vector<double> &tau_out
133)
134{
135 const unsigned n = chain.getNrOfJoints();
136 const unsigned s = chain.getNrOfSegments();
137 if (n != 7) {
138 std::cerr << label << ": unexpected joint count " << n << "\n";
139 return false;
140 }
141
142 KDL::JntArray q(n), qdot(n), qddot(n), torques(n);
143 for (unsigned i = 0; i < n; ++i) q(i) = kHome[i];
144
145 KDL::Wrenches f_ext(s, KDL::Wrench::Zero());
146 KDL::ChainIdSolver_RNE rnea(chain, KDL::Vector(0.0, 0.0, -9.81));
147 if (rnea.CartToJnt(q, qdot, qddot, f_ext, torques) < 0) {
148 std::cerr << label << ": RNEA failed\n";
149 return false;
150 }
151
152 tau_out.resize(n);
153 std::cout << label << " RNEA gravity torques at home:\n";
154 for (unsigned i = 0; i < n; ++i) {
155 tau_out[i] = torques(i);
156 std::cout << " j[" << i << "] " << std::setw(10) << std::fixed
157 << std::setprecision(5) << torques(i) << " Nm\n";
158 }
159
160 KDL::ChainFkSolverPos_recursive fk(chain);
161 KDL::Frame ee;
162 fk.JntToCart(q, ee);
163 std::cout << " EE: (" << std::setprecision(4)
164 << ee.p.x() << ", " << ee.p.y() << ", " << ee.p.z() << ")\n\n";
165 return true;
166}
167
168int main(int argc, char *argv[])
169{
170 const fs::path root = repo_root();
171 const fs::path urdf = (argc > 1)
172 ? fs::path(argv[1])
173 : root / "third_party/kinova/GEN3_URDF_V12.urdf";
174
175 KDL::Chain urdf_achd_chain;
176 if (!parse_urdf_chain(urdf, "EndEffector_Link", urdf_achd_chain)) return 1;
177 if (!run_achd_probe(urdf_achd_chain)) return 1;
178
179 KDL::Chain urdf_rnea_chain;
180 if (!parse_urdf_chain(urdf, "Bracelet_Link", urdf_rnea_chain)) return 1;
181
182 const std::string arm_mjcf = (root / "third_party/menagerie/kinova_gen3/gen3.xml").string();
183 const std::string grp_mjcf = (root / "third_party/menagerie/robotiq_2f85/2f85.xml").string();
184
186 gripper.mjcf_path = grp_mjcf.c_str();
187 gripper.attach_to = "bracelet_link";
188 gripper.prefix = "g_";
189 gripper.pos[2] = -0.061525;
190 gripper.euler[0] = 180.0;
191
192 mj_kdl::RobotSpec robot_spec;
193 robot_spec.path = arm_mjcf.c_str();
194 robot_spec.attachments.push_back(gripper);
195
196 mj_kdl::SceneSpec scene;
197 scene.robots.push_back(robot_spec);
198
199 mjModel *model = nullptr;
200 mjData *data = nullptr;
201 if (!mj_kdl::build_scene(&model, &data, &scene)) {
202 std::cerr << "build_scene failed\n";
203 return 1;
204 }
205
206 mj_kdl::Robot robot;
207 if (!mj_kdl::init_robot_from_mjcf(&robot, model, data, "base_link", "bracelet_link", "", nullptr)) {
208 std::cerr << "init_robot_from_mjcf failed\n";
209 mj_kdl::destroy_scene(model, data);
210 return 1;
211 }
212
213 std::cout << "=== RNEA comparison: URDF Bracelet_Link vs MuJoCo bracelet_link ===\n";
214 print_chain(urdf_rnea_chain, "URDF RNEA");
215 print_chain(robot.chain, "MuJoCo RNEA");
216
217 std::vector<double> tau_urdf, tau_mj;
218 const bool rnea_ok = run_rnea(urdf_rnea_chain, "URDF", tau_urdf)
219 && run_rnea(robot.chain, "MuJoCo", tau_mj);
220 if (!rnea_ok) {
221 mj_kdl::cleanup(&robot);
222 mj_kdl::destroy_scene(model, data);
223 return 1;
224 }
225
226 std::cout << "MuJoCo - URDF gravity torque difference:\n";
227 const unsigned n = std::min(tau_urdf.size(), tau_mj.size());
228 for (unsigned i = 0; i < n; ++i) {
229 const double d = tau_mj[i] - tau_urdf[i];
230 std::cout << " j[" << i << "] " << std::setw(9) << std::fixed
231 << std::setprecision(5) << d << " Nm"
232 << (std::abs(d) > 0.05 ? " <-- MISMATCH" : "") << "\n";
233 }
234
235 mj_kdl::cleanup(&robot);
236 mj_kdl::destroy_scene(model, data);
237 return 0;
238}
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)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
std::vector< AttachmentSpec > attachments
std::vector< RobotSpec > robots
int main(int argc, char *argv[])